-
Notifications
You must be signed in to change notification settings - Fork 908
Shortcut fixes #4354
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
Shortcut fixes #4354
Changes from 7 commits
9b75da5
76314b9
f166348
3a81c10
6f577d8
3a48e33
6691edb
34e8cf8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Do not show shortcuts if a PIN code is set |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,51 +22,87 @@ import android.os.Build | |
| import androidx.core.content.getSystemService | ||
| import androidx.core.content.pm.ShortcutManagerCompat | ||
| import im.vector.app.core.di.ActiveSessionHolder | ||
| import im.vector.app.features.pin.PinCodeStore | ||
| import im.vector.app.features.pin.PinCodeStoreListener | ||
| import io.reactivex.disposables.Disposable | ||
| import io.reactivex.disposables.Disposables | ||
| import org.matrix.android.sdk.api.session.room.RoomSortOrder | ||
| import org.matrix.android.sdk.api.session.room.model.Membership | ||
| import org.matrix.android.sdk.api.session.room.model.RoomSummary | ||
| import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams | ||
| import org.matrix.android.sdk.rx.asObservable | ||
| import timber.log.Timber | ||
| import javax.inject.Inject | ||
|
|
||
| class ShortcutsHandler @Inject constructor( | ||
| private val context: Context, | ||
| private val shortcutCreator: ShortcutCreator, | ||
| private val activeSessionHolder: ActiveSessionHolder | ||
| ) { | ||
| private val activeSessionHolder: ActiveSessionHolder, | ||
| private val pinCodeStore: PinCodeStore | ||
| ) : PinCodeStoreListener { | ||
| private val isRequestPinShortcutSupported = ShortcutManagerCompat.isRequestPinShortcutSupported(context) | ||
|
|
||
| // Value will be set correctly if necessary | ||
| private var hasPinCode = true | ||
|
|
||
| fun observeRoomsAndBuildShortcuts(): Disposable { | ||
| if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) { | ||
| // No op | ||
| return Disposables.empty() | ||
| } | ||
|
|
||
| return activeSessionHolder.getSafeActiveSession() | ||
| ?.getPagedRoomSummariesLive( | ||
| roomSummaryQueryParams { | ||
| memberships = listOf(Membership.JOIN) | ||
| }, | ||
| sortOrder = RoomSortOrder.PRIORITY_AND_ACTIVITY | ||
| ) | ||
| ?.asObservable() | ||
| ?.subscribe { rooms -> | ||
| hasPinCode = pinCodeStore.getEncodedPin() != null | ||
|
|
||
| val session = activeSessionHolder.getSafeActiveSession() ?: return Disposables.empty() | ||
| pinCodeStore.addListener(this) | ||
| return session.getRoomSummariesLive( | ||
| roomSummaryQueryParams { | ||
| memberships = listOf(Membership.JOIN) | ||
| }, | ||
| sortOrder = RoomSortOrder.PRIORITY_AND_ACTIVITY | ||
| ) | ||
| .asObservable() | ||
| .doOnDispose { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIT: it could be easier to follow the listeners flow if they're added and removed within the pipeline .doOnSubscribe { pinCodeStore.addListener(this) }
.doFinally { pinCodeStore.removeListener(this) } // finally catches error, completes and disposed
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please be aware the code will change with the Rx-Flow migration
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I can do this small change.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Not so small since doOnDispose -> doFinally) 34e8cf8 |
||
| pinCodeStore.removeListener(this) | ||
| } | ||
| .subscribe { rooms -> | ||
| // Remove dead shortcuts (i.e. deleted rooms) | ||
| val roomIds = rooms.map { it.roomId } | ||
| val deadShortcutIds = ShortcutManagerCompat.getShortcuts(context, ShortcutManagerCompat.FLAG_MATCH_DYNAMIC) | ||
| .map { it.id } | ||
| .filter { !roomIds.contains(it) } | ||
| ShortcutManagerCompat.removeLongLivedShortcuts(context, deadShortcutIds) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| val shortcuts = rooms.mapIndexed { index, room -> | ||
| shortcutCreator.create(room, index) | ||
| } | ||
|
|
||
| shortcuts.forEach { shortcut -> | ||
| ShortcutManagerCompat.pushDynamicShortcut(context, shortcut) | ||
| } | ||
| removeDeadShortcut(rooms.map { it.roomId }) | ||
|
|
||
| // Create shortcuts | ||
| createShortcuts(rooms) | ||
| } | ||
| } | ||
|
|
||
| private fun removeDeadShortcut(roomIds: List<String>) { | ||
| val deadShortcutIds = ShortcutManagerCompat.getShortcuts(context, ShortcutManagerCompat.FLAG_MATCH_DYNAMIC) | ||
| .map { it.id } | ||
| .filter { !roomIds.contains(it) } | ||
|
|
||
| if (deadShortcutIds.isNotEmpty()) { | ||
| Timber.d("Removing shortcut(s) $deadShortcutIds") | ||
| ShortcutManagerCompat.removeLongLivedShortcuts(context, deadShortcutIds) | ||
| if (isRequestPinShortcutSupported) { | ||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { | ||
| context.getSystemService<ShortcutManager>()?.disableShortcuts(deadShortcutIds) | ||
| } | ||
| ?: Disposables.empty() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private fun createShortcuts(rooms: List<RoomSummary>) { | ||
| if (hasPinCode) { | ||
| // No shortcut in this case (privacy) | ||
| ShortcutManagerCompat.removeAllDynamicShortcuts(context) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. to double check my understanding, if there's a pin code set we don't allow shortcuts?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's not exactly true. So a PIN code prevent a few rooms (5 on my phone) to be disclosed without having to enter the PIN code.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah thanks for explaining! |
||
| } else { | ||
| val shortcuts = rooms.mapIndexed { index, room -> | ||
| shortcutCreator.create(room, index) | ||
| } | ||
|
|
||
| shortcuts.forEach { shortcut -> | ||
| ShortcutManagerCompat.pushDynamicShortcut(context, shortcut) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fun clearShortcuts() { | ||
|
|
@@ -82,7 +118,7 @@ class ShortcutsHandler @Inject constructor( | |
| ShortcutManagerCompat.removeLongLivedShortcuts(context, shortcuts) | ||
|
|
||
| // We can only disabled pinned shortcuts with the API, but at least it will prevent the crash | ||
| if (ShortcutManagerCompat.isRequestPinShortcutSupported(context)) { | ||
| if (isRequestPinShortcutSupported) { | ||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { | ||
| context.getSystemService<ShortcutManager>() | ||
| ?.let { | ||
|
|
@@ -91,4 +127,14 @@ class ShortcutsHandler @Inject constructor( | |
| } | ||
| } | ||
| } | ||
|
|
||
| override fun onPinSetUpChange(isConfigured: Boolean) { | ||
| hasPinCode = isConfigured | ||
| if (isConfigured) { | ||
| // Remove shortcuts immediately | ||
| ShortcutManagerCompat.removeAllDynamicShortcuts(context) | ||
| } | ||
| // Else shortcut will be created next time any room summary is updated, or | ||
| // next time the app is started which is acceptable | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,6 +25,7 @@ import kotlinx.coroutines.Dispatchers | |
| import kotlinx.coroutines.withContext | ||
| import org.matrix.android.sdk.api.extensions.orFalse | ||
| import javax.inject.Inject | ||
| import javax.inject.Singleton | ||
| import kotlin.coroutines.resume | ||
| import kotlin.coroutines.suspendCoroutine | ||
|
|
||
|
|
@@ -56,26 +57,40 @@ interface PinCodeStore { | |
| * Will reset the counters | ||
| */ | ||
| fun resetCounters() | ||
|
|
||
| fun addListener(listener: PinCodeStoreListener) | ||
| fun removeListener(listener: PinCodeStoreListener) | ||
| } | ||
|
|
||
| interface PinCodeStoreListener { | ||
| fun onPinSetUpChange(isConfigured: Boolean) | ||
| } | ||
|
|
||
| @Singleton | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add this to be able to add listener to this "object" |
||
| class SharedPrefPinCodeStore @Inject constructor(private val sharedPreferences: SharedPreferences) : PinCodeStore { | ||
| private val listeners = mutableSetOf<PinCodeStoreListener>() | ||
|
|
||
| override suspend fun storeEncodedPin(encodePin: String) = withContext(Dispatchers.IO) { | ||
| sharedPreferences.edit { | ||
| putString(ENCODED_PIN_CODE_KEY, encodePin) | ||
| override suspend fun storeEncodedPin(encodePin: String) { | ||
| withContext(Dispatchers.IO) { | ||
| sharedPreferences.edit { | ||
| putString(ENCODED_PIN_CODE_KEY, encodePin) | ||
| } | ||
| } | ||
| listeners.forEach { it.onPinSetUpChange(isConfigured = true) } | ||
| } | ||
|
|
||
| override suspend fun deleteEncodedPin() = withContext(Dispatchers.IO) { | ||
| // Also reset the counters | ||
| resetCounters() | ||
| sharedPreferences.edit { | ||
| remove(ENCODED_PIN_CODE_KEY) | ||
| override suspend fun deleteEncodedPin() { | ||
| withContext(Dispatchers.IO) { | ||
| // Also reset the counters | ||
| resetCounters() | ||
| sharedPreferences.edit { | ||
| remove(ENCODED_PIN_CODE_KEY) | ||
| } | ||
| awaitPinCodeCallback<Boolean> { | ||
| PFSecurityManager.getInstance().pinCodeHelper.delete(it) | ||
| } | ||
| } | ||
| awaitPinCodeCallback<Boolean> { | ||
| PFSecurityManager.getInstance().pinCodeHelper.delete(it) | ||
| } | ||
| return@withContext | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line was a bit useless to me (?) |
||
| listeners.forEach { it.onPinSetUpChange(isConfigured = false) } | ||
| } | ||
|
|
||
| override fun getEncodedPin(): String? { | ||
|
|
@@ -124,6 +139,14 @@ class SharedPrefPinCodeStore @Inject constructor(private val sharedPreferences: | |
| } | ||
| } | ||
|
|
||
| override fun addListener(listener: PinCodeStoreListener) { | ||
| listeners.add(listener) | ||
| } | ||
|
|
||
| override fun removeListener(listener: PinCodeStoreListener) { | ||
| listeners.remove(listener) | ||
| } | ||
|
|
||
| private suspend inline fun <T> awaitPinCodeCallback(crossinline callback: (PFPinCodeHelperCallback<T>) -> Unit) = suspendCoroutine<PFResult<T>> { cont -> | ||
| callback(PFPinCodeHelperCallback<T> { result -> cont.resume(result) }) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getPagedRoomSummariesLivewas used before, which was not correct, only the first 20 rooms was returnedThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need to know about all the rooms? the shortcut menu only holds a few entries~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, because we have to check if a room has been left (so not in the list anymore) to disable any related shortcuts pinned to the home