Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New app theming #1010

Merged
merged 121 commits into from
Oct 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
65b437c
feat: start new app theming feature
hjubb Jun 7, 2022
c8471d2
Merge remote-tracking branch 'upstream/master' into new_app_theming
hjubb Jun 9, 2022
2eb2231
feat: add some theming colours
hjubb Jun 10, 2022
a4d7e44
Merge branch 'dev' into new_app_theming
hjubb Jun 10, 2022
5d60117
refactor: start refactoring themes and colours to use dynamic attributes
hjubb Jun 10, 2022
6f8b3a4
feat: adding more colours and switching over default colours to be th…
hjubb Jun 14, 2022
b8da118
refactor: take a look at ocean light and logo colour
hjubb Jun 14, 2022
ef6f45e
feat: global search colours for light and dark ocean
hjubb Jun 14, 2022
412669e
feat: more styling
hjubb Jun 14, 2022
e5c01c7
Merge branch 'dev' into new_app_theming
hjubb Jun 16, 2022
b9636f3
feat: adding themes to conversation activity and refactoring the base…
hjubb Jun 16, 2022
0055985
feat: add dynamic accent color
hjubb Jun 17, 2022
e0e5065
docs: add todo for changing how accent colour is applied
hjubb Jun 24, 2022
7b3e0a1
feat: update new theming to use override primary style so that the re…
hjubb Jun 27, 2022
3d1f532
Merge branch 'dev' into new_app_theming
hjubb Jun 28, 2022
cd84d04
feat: coordinating styles across layouts, fixing up pinned icons and …
hjubb Jun 28, 2022
c40a6c8
refactor: re-styling layouts to match new themes and attributes. Need…
hjubb Jun 30, 2022
2ad8f90
refactor: remove @color/text and replace with ?android:textColorPrima…
hjubb Jul 4, 2022
c8d547a
refactor: add context theme wrapper to bottom sheet dialog that refer…
hjubb Jul 4, 2022
0b8e910
fix: input bar bug fix and preference activity themes
hjubb Jul 5, 2022
46849f9
refactor: new settings menu options
hjubb Aug 31, 2022
aa34b0d
fix: crash for PNModeActivity.kt
hjubb Aug 31, 2022
1c16b97
feat: add new appearance settings activity
hjubb Sep 1, 2022
b669739
refactor: title and VM changes
hjubb Sep 1, 2022
8e263f3
fix: correct override
hjubb Sep 1, 2022
daf594b
feat: add theme appearance screen UI features and start VM implementa…
hjubb Sep 2, 2022
972bba0
Merge branch 'master' into new_app_theming
hjubb Sep 5, 2022
fbeaeaa
fix: compile errors and missing themes from emoji features
hjubb Sep 5, 2022
4b5e79d
refactor: remove background shape alteration and old bottom sheet sty…
hjubb Sep 5, 2022
1a4182d
feat: appearance screen wired up, just need to refresh theme
hjubb Sep 6, 2022
2766e6e
feat: add theme state recreation and fix match system settings option
hjubb Sep 6, 2022
0c96da1
refactor: add bottom margin
hjubb Sep 6, 2022
3e0b662
Merge branch 'master' into new_app_theming
hjubb Sep 7, 2022
573698e
feat: explore custom preference category
hjubb Sep 7, 2022
4e20b0a
feat: add the customized session theme for CorrectedPreferenceFragment
hjubb Sep 8, 2022
4219f65
feat: replace AppProtectionPreferenceFragment to extend ListSummaryPr…
hjubb Sep 8, 2022
a495c16
refactor: change drawable style and remove explicit dividers
hjubb Sep 9, 2022
8e64e31
refactor: remove divider in CorrectedPreferenceFragment
hjubb Sep 9, 2022
4508132
feat: add theme state check on resume, might be jarring currently
hjubb Sep 12, 2022
e5be337
feat: add preference divider elements for settings menu
hjubb Sep 13, 2022
c2d0f87
Merge branch 'dev' into new_app_theming
hjubb Sep 14, 2022
e393745
refactor: settings menu redesigns
hjubb Sep 14, 2022
5f43de1
refactor: change led preference to integer and refactor TextSecurePre…
hjubb Sep 14, 2022
34ddedb
feat: add scroll parcel to save/restore hierarchy on restart with app…
hjubb Sep 15, 2022
3555bd8
feat: add the conversations blocked contacts and refactor preference …
hjubb Sep 15, 2022
5f134cd
feat: add blocked contacts activity, basic layout and vm
hjubb Sep 19, 2022
2ae10be
feat: add unblock DB functions and storage protocol, start working on…
hjubb Sep 20, 2022
29ca519
feat: add blocked contacts and notif recipient listeners
hjubb Sep 20, 2022
9501a88
feat: add recipient db reader
hjubb Sep 20, 2022
dd1baf1
feat: add blocked contact interactions and fix a theming crash for no…
hjubb Sep 26, 2022
2ce7006
Merge branch 'master' into new_app_theming
hjubb Sep 26, 2022
76838cf
feat: introduce better equals and hashcode implementations to recipie…
hjubb Sep 26, 2022
4bd87b9
feat: add settings menu vectors
hjubb Sep 27, 2022
48ac6a6
fix: preview compile error
hjubb Sep 27, 2022
4b41013
refactor: migrating settings menu to new designs
hjubb Sep 27, 2022
aa40f2f
feat: help menu
hjubb Sep 27, 2022
490d114
refactor: simplify link opening
hjubb Sep 27, 2022
4d1ca38
refactor: remove space
hjubb Sep 28, 2022
5cf885a
feat: refactor preferences and start theming for light mode options
hjubb Sep 28, 2022
e28813f
refactor: fixing dark and light modes with dialogs
hjubb Sep 29, 2022
210b362
refactor: popup dialogs use proper themes now
hjubb Sep 30, 2022
058ec93
Merge remote-tracking branch 'upstream/master' into new_app_theming
hjubb Oct 2, 2022
97c1ecc
Merge branch 'dev' into new_app_theming
hjubb Oct 2, 2022
09a3c58
refactor: alert dialogs and media edit fragments use attribute refere…
hjubb Oct 3, 2022
240f5b9
refactor: use input bar button attribute instead color control normal…
hjubb Oct 3, 2022
26436ad
refactor: transparency, dialog fixes, notification fix
hjubb Oct 4, 2022
93389bd
refactor: attrs and styles for buttons
hjubb Oct 4, 2022
220c589
fix: use prominent button color on the outline button's border
hjubb Oct 4, 2022
aae4e3a
fix: fix the trash
hjubb Oct 4, 2022
786680c
refactor: remove the appearance
hjubb Oct 4, 2022
716a396
refactor: avatar placeholder generation, chips and element border styles
hjubb Oct 5, 2022
d71b8b6
refactor: use colors instead of style references
hjubb Oct 5, 2022
2a05d20
refactor: theming changes to match designs and feedback
hjubb Oct 5, 2022
3771332
refactor: the titles are bold and the categories are tertiary coloure…
hjubb Oct 6, 2022
b9d940b
fix: appearance settings match preferences, search bottom bar uses th…
hjubb Oct 6, 2022
e5d9036
refactor: increase setting button height
hjubb Oct 6, 2022
c512ada
Merge branch 'dev' into new_app_theming
hjubb Oct 9, 2022
562d06c
Update clear all data dialog
Oct 10, 2022
0137b77
Update seed dialog
Oct 10, 2022
a2fd97a
refactor: more qa feedback changes
hjubb Oct 10, 2022
5385604
Merge remote-tracking branch 'charles/new_app_theming' into new_app_t…
hjubb Oct 10, 2022
61bcd30
feat: add new TLs and fa-rIR TLs
hjubb Oct 11, 2022
92d9177
Update notification content dialog
Oct 11, 2022
bf6ce17
Merge remote-tracking branch 'charles/new_app_theming' into new_app_t…
hjubb Oct 11, 2022
7522142
Fix message requests clear all button text color
Oct 11, 2022
b4ce040
feat: re-add screenshot observer
hjubb Oct 11, 2022
95504bf
Merge remote-tracking branch 'charles/new_app_theming' into new_app_t…
hjubb Oct 11, 2022
fcb0c39
refactor: make send tint accent color
hjubb Oct 11, 2022
3eb610e
feat: add unread background differences
hjubb Oct 11, 2022
fe36d7f
fix: change unread count indicator
hjubb Oct 11, 2022
9d5d5e9
build: upgrade build numbers
hjubb Oct 11, 2022
09419d6
Fix message requests popupmenu background color
Oct 11, 2022
292198c
Merge remote-tracking branch 'charles/new_app_theming' into new_app_t…
hjubb Oct 11, 2022
b08bdf2
fix: crash from attr reference in color attribute
hjubb Oct 11, 2022
c1573fe
build: upgrade build number
hjubb Oct 11, 2022
5170965
fix: message bubbles, thumbnail backgrounds, search bar visibility wi…
hjubb Oct 12, 2022
952bede
fix: tertiary text for keyboard page search view
hjubb Oct 12, 2022
e3fe54d
fix: emoji overflow colour differences
hjubb Oct 12, 2022
2780182
fix: reaction pill dialog background is now correct colour
hjubb Oct 12, 2022
bb0386b
Add style to reactions tab layout
Oct 12, 2022
ff89f33
fix: appearance activity reverting primary color at correct time
hjubb Oct 12, 2022
4d7d19b
Merge remote-tracking branch 'origin/new_app_theming' into new_app_th…
hjubb Oct 12, 2022
5177af0
fix: show call privacy warning every time instead of just once
hjubb Oct 12, 2022
545f4d3
fix: gradient background(?) and audio autoplay disable
hjubb Oct 12, 2022
1581f64
fix: crash in all media containing documents
hjubb Oct 12, 2022
340f9f2
fix: reaction dialog heading fixes
hjubb Oct 12, 2022
24b6c71
Add style to reactions tab layout
Oct 12, 2022
b937b1c
fix: remove gradient backgrounds
hjubb Oct 12, 2022
b34e27b
fix: adding new reaction normal text attribute to try correct the tab…
hjubb Oct 12, 2022
112896e
Merge remote-tracking branch 'origin/new_app_theming' into new_app_th…
hjubb Oct 12, 2022
44f94ae
fix: ocean dark unread/read colours
hjubb Oct 12, 2022
81c18c8
build; update build number
hjubb Oct 12, 2022
676e7d0
build: update build number
hjubb Oct 12, 2022
6e8e875
Fix ocean light theme pin color
Oct 12, 2022
74d146b
Update classic dark unread indicator text color
Oct 13, 2022
4aab321
Reduce group name text size on join community screen
Oct 14, 2022
e269d88
Restore tab indicator color on join community and all media screens
Oct 14, 2022
8da186c
Home adapter rename
Oct 16, 2022
f2ecafb
Updated the HomeViewModel to cancel old executors when new ones are a…
mpretty-cyro Oct 17, 2022
46a7f1c
Explicitly remove observers when the HomeActivity is paused (#2)
mpretty-cyro Oct 18, 2022
813b384
Merge branch 'dev' into new_app_theming
ceokot Oct 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

public abstract class BaseActionBarActivity extends AppCompatActivity {
private static final String TAG = BaseActionBarActivity.class.getSimpleName();
private ThemeState currentThemeState;
public ThemeState currentThemeState;

private TextSecurePreferences getPreferences() {
ApplicationContext appContext = (ApplicationContext) getApplicationContext();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.thoughtcrime.securesms.home

import org.thoughtcrime.securesms.database.model.ThreadRecord

interface ConversationClickListener {
fun onConversationClick(thread: ThreadRecord)
fun onLongConversationClick(thread: ThreadRecord)
}
47 changes: 30 additions & 17 deletions app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
private val publicKey: String
get() = textSecurePreferences.getLocalNumber()!!

private val homeAdapter: NewHomeAdapter by lazy {
NewHomeAdapter(context = this, listener = this)
private val homeAdapter: HomeAdapter by lazy {
HomeAdapter(context = this, listener = this)
}

private val globalSearchAdapter = GlobalSearchAdapter { model ->
Expand Down Expand Up @@ -172,23 +172,12 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
homeAdapter.glide = glide
binding.recyclerView.adapter = homeAdapter
binding.globalSearchRecycler.adapter = globalSearchAdapter

// Set up empty state view
binding.createNewPrivateChatButton.setOnClickListener { showNewConversation() }
IP2Country.configureIfNeeded(this@HomeActivity)
homeViewModel.getObservable(this).observe(this) { newData ->
val manager = binding.recyclerView.layoutManager as LinearLayoutManager
val firstPos = manager.findFirstCompletelyVisibleItemPosition()
val offsetTop = if(firstPos >= 0) {
manager.findViewByPosition(firstPos)?.let { view ->
manager.getDecoratedTop(view) - manager.getTopDecorationHeight(view)
} ?: 0
} else 0
homeAdapter.data = newData
if(firstPos >= 0) { manager.scrollToPositionWithOffset(firstPos, offsetTop) }
setupMessageRequestsBanner()
updateEmptyState()
}
homeViewModel.tryUpdateChannel()
startObservingUpdates()

// Set up new conversation button
binding.newConversationButton.setOnClickListener { showNewConversation() }
// Observe blocked contacts changed events
Expand Down Expand Up @@ -286,7 +275,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
binding.searchToolbar.isVisible = isShown
binding.sessionToolbar.isVisible = !isShown
binding.recyclerView.isVisible = !isShown
binding.emptyStateContainer.isVisible = (binding.recyclerView.adapter as NewHomeAdapter).itemCount == 0 && binding.recyclerView.isVisible
binding.emptyStateContainer.isVisible = (binding.recyclerView.adapter as HomeAdapter).itemCount == 0 && binding.recyclerView.isVisible
binding.seedReminderView.isVisible = !TextSecurePreferences.getHasViewedSeed(this) && !isShown
binding.globalSearchRecycler.isVisible = isShown
binding.newConversationButton.isVisible = !isShown
Expand Down Expand Up @@ -335,11 +324,19 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
ConfigurationMessageUtilities.syncConfigurationIfNeeded(this@HomeActivity)
}
}

// If the theme hasn't changed then start observing updates again (if it does change then we
// will recreate the activity resulting in it responding to changes multiple times)
if (currentThemeState == textSecurePreferences.themeState() && !homeViewModel.getObservable(this).hasActiveObservers()) {
startObservingUpdates()
}
}

override fun onPause() {
super.onPause()
ApplicationContext.getInstance(this).messageNotifier.setHomeScreenVisible(false)

homeViewModel.getObservable(this).removeObservers(this)
}

override fun onDestroy() {
Expand All @@ -353,6 +350,22 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
// endregion

// region Updating
private fun startObservingUpdates() {
homeViewModel.getObservable(this).observe(this) { newData ->
val manager = binding.recyclerView.layoutManager as LinearLayoutManager
val firstPos = manager.findFirstCompletelyVisibleItemPosition()
val offsetTop = if(firstPos >= 0) {
manager.findViewByPosition(firstPos)?.let { view ->
manager.getDecoratedTop(view) - manager.getTopDecorationHeight(view)
} ?: 0
} else 0
homeAdapter.data = newData
if(firstPos >= 0) { manager.scrollToPositionWithOffset(firstPos, offsetTop) }
setupMessageRequestsBanner()
updateEmptyState()
}
}

private fun updateEmptyState() {
val threadCount = (binding.recyclerView.adapter)!!.itemCount
binding.emptyStateContainer.isVisible = threadCount == 0 && binding.recyclerView.isVisible
Expand Down
113 changes: 110 additions & 3 deletions app/src/main/java/org/thoughtcrime/securesms/home/HomeAdapter.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,115 @@
package org.thoughtcrime.securesms.home

import android.content.Context
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListUpdateCallback
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.NO_ID
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.mms.GlideRequests

class HomeAdapter(
private val context: Context,
private val listener: ConversationClickListener
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), ListUpdateCallback {

companion object {
private const val HEADER = 0
private const val ITEM = 1
}

var header: View? = null

private var _data: List<ThreadRecord> = emptyList()
var data: List<ThreadRecord>
get() = _data.toList()
set(newData) {
val previousData = _data.toList()
val diff = HomeDiffUtil(previousData, newData, context)
val diffResult = DiffUtil.calculateDiff(diff)
_data = newData
diffResult.dispatchUpdatesTo(this as ListUpdateCallback)
}

fun hasHeaderView(): Boolean = header != null

private val headerCount: Int
get() = if (header == null) 0 else 1

override fun onInserted(position: Int, count: Int) {
notifyItemRangeInserted(position + headerCount, count)
}

override fun onRemoved(position: Int, count: Int) {
notifyItemRangeRemoved(position + headerCount, count)
}

override fun onMoved(fromPosition: Int, toPosition: Int) {
notifyItemMoved(fromPosition + headerCount, toPosition + headerCount)
}

override fun onChanged(position: Int, count: Int, payload: Any?) {
notifyItemRangeChanged(position + headerCount, count, payload)
}

override fun getItemId(position: Int): Long {
if (hasHeaderView() && position == 0) return NO_ID
val offsetPosition = if (hasHeaderView()) position-1 else position
return _data[offsetPosition].threadId
}

lateinit var glide: GlideRequests
var typingThreadIDs = setOf<Long>()
set(value) {
field = value
// TODO: replace this with a diffed update or a partial change set with payloads
notifyDataSetChanged()
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
when (viewType) {
HEADER -> {
HeaderFooterViewHolder(header!!)
}
ITEM -> {
val view = ConversationView(context)
view.setOnClickListener { view.thread?.let { listener.onConversationClick(it) } }
view.setOnLongClickListener {
view.thread?.let { listener.onLongConversationClick(it) }
true
}
ViewHolder(view)
}
else -> throw Exception("viewType $viewType isn't valid")
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is ViewHolder) {
val offset = if (hasHeaderView()) position - 1 else position
val thread = data[offset]
val isTyping = typingThreadIDs.contains(thread.threadId)
holder.view.bind(thread, isTyping, glide)
}
}

override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
if (holder is ViewHolder) {
holder.view.recycle()
} else {
super.onViewRecycled(holder)
}
}

override fun getItemViewType(position: Int): Int =
if (hasHeaderView() && position == 0) HEADER
else ITEM

override fun getItemCount(): Int = data.size + if (hasHeaderView()) 1 else 0

class ViewHolder(val view: ConversationView) : RecyclerView.ViewHolder(view)

class HeaderFooterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)

interface ConversationClickListener {
fun onConversationClick(thread: ThreadRecord)
fun onLongConversationClick(thread: ThreadRecord)
}
56 changes: 34 additions & 22 deletions app/src/main/java/org/thoughtcrime/securesms/home/HomeViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,22 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.cash.copper.flow.observeQuery
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import kotlinx.coroutines.withContext
import org.thoughtcrime.securesms.database.DatabaseContentProviders
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.model.ThreadRecord
import java.lang.ref.WeakReference
import javax.inject.Inject

@HiltViewModel
class HomeViewModel @Inject constructor(private val threadDb: ThreadDatabase): ViewModel() {

private val executor = viewModelScope + SupervisorJob()
private var lastContext: WeakReference<Context>? = null
private var updateJobs: MutableList<Job> = mutableListOf()

private val _conversations = MutableLiveData<List<ThreadRecord>>()
val conversations: LiveData<List<ThreadRecord>> = _conversations
Expand All @@ -33,25 +32,38 @@ class HomeViewModel @Inject constructor(private val threadDb: ThreadDatabase): V
fun tryUpdateChannel() = listUpdateChannel.trySend(Unit)

fun getObservable(context: Context): LiveData<List<ThreadRecord>> {
executor.launch(Dispatchers.IO) {
context.contentResolver
.observeQuery(DatabaseContentProviders.ConversationList.CONTENT_URI)
.onEach { listUpdateChannel.trySend(Unit) }
.collect()
}
executor.launch(Dispatchers.IO) {
for (update in listUpdateChannel) {
threadDb.approvedConversationList.use { openCursor ->
val reader = threadDb.readerFor(openCursor)
val threads = mutableListOf<ThreadRecord>()
while (true) {
threads += reader.next ?: break
}
withContext(Dispatchers.Main) {
_conversations.value = threads
// If the context has changed (eg. the activity gets recreated) then
// we need to cancel the old executors and recreate them to prevent
// the app from triggering extra updates when data changes
if (context != lastContext?.get()) {
lastContext = WeakReference(context)
updateJobs.forEach { it.cancel() }
updateJobs.clear()

updateJobs.add(
executor.launch(Dispatchers.IO) {
context.contentResolver
.observeQuery(DatabaseContentProviders.ConversationList.CONTENT_URI)
.onEach { listUpdateChannel.trySend(Unit) }
.collect()
}
)
updateJobs.add(
executor.launch(Dispatchers.IO) {
for (update in listUpdateChannel) {
threadDb.approvedConversationList.use { openCursor ->
val reader = threadDb.readerFor(openCursor)
val threads = mutableListOf<ThreadRecord>()
while (true) {
threads += reader.next ?: break
}
withContext(Dispatchers.Main) {
_conversations.value = threads
}
}
}
}
}
)
}
return conversations
}
Expand Down
Loading