Remove shortcut as soon as a PIN code is set

This commit is contained in:
Benoit Marty 2021-10-27 16:17:53 +02:00
parent 3a48e33c81
commit 6691edb59d
2 changed files with 58 additions and 15 deletions

View File

@ -23,6 +23,7 @@ import androidx.core.content.getSystemService
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.features.pin.PinCodeStore import im.vector.app.features.pin.PinCodeStore
import im.vector.app.features.pin.PinCodeStoreListener
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.disposables.Disposables import io.reactivex.disposables.Disposables
import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.RoomSortOrder
@ -38,16 +39,22 @@ class ShortcutsHandler @Inject constructor(
private val shortcutCreator: ShortcutCreator, private val shortcutCreator: ShortcutCreator,
private val activeSessionHolder: ActiveSessionHolder, private val activeSessionHolder: ActiveSessionHolder,
private val pinCodeStore: PinCodeStore private val pinCodeStore: PinCodeStore
) { ) : PinCodeStoreListener {
private val isRequestPinShortcutSupported = ShortcutManagerCompat.isRequestPinShortcutSupported(context) private val isRequestPinShortcutSupported = ShortcutManagerCompat.isRequestPinShortcutSupported(context)
// Value will be set correctly if necessary
private var hasPinCode = true
fun observeRoomsAndBuildShortcuts(): Disposable { fun observeRoomsAndBuildShortcuts(): Disposable {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) {
// No op // No op
return Disposables.empty() return Disposables.empty()
} }
hasPinCode = pinCodeStore.getEncodedPin() != null
val session = activeSessionHolder.getSafeActiveSession() ?: return Disposables.empty() val session = activeSessionHolder.getSafeActiveSession() ?: return Disposables.empty()
pinCodeStore.addListener(this)
return session.getRoomSummariesLive( return session.getRoomSummariesLive(
roomSummaryQueryParams { roomSummaryQueryParams {
memberships = listOf(Membership.JOIN) memberships = listOf(Membership.JOIN)
@ -55,6 +62,9 @@ class ShortcutsHandler @Inject constructor(
sortOrder = RoomSortOrder.PRIORITY_AND_ACTIVITY sortOrder = RoomSortOrder.PRIORITY_AND_ACTIVITY
) )
.asObservable() .asObservable()
.doOnDispose {
pinCodeStore.removeListener(this)
}
.subscribe { rooms -> .subscribe { rooms ->
// Remove dead shortcuts (i.e. deleted rooms) // Remove dead shortcuts (i.e. deleted rooms)
removeDeadShortcut(rooms.map { it.roomId }) removeDeadShortcut(rooms.map { it.roomId })
@ -81,7 +91,7 @@ class ShortcutsHandler @Inject constructor(
} }
private fun createShortcuts(rooms: List<RoomSummary>) { private fun createShortcuts(rooms: List<RoomSummary>) {
if (pinCodeStore.getEncodedPin() != null) { if (hasPinCode) {
// No shortcut in this case (privacy) // No shortcut in this case (privacy)
ShortcutManagerCompat.removeAllDynamicShortcuts(context) ShortcutManagerCompat.removeAllDynamicShortcuts(context)
} else { } else {
@ -117,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
}
} }

View File

@ -25,6 +25,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
@ -56,26 +57,40 @@ interface PinCodeStore {
* Will reset the counters * Will reset the counters
*/ */
fun resetCounters() fun resetCounters()
fun addListener(listener: PinCodeStoreListener)
fun removeListener(listener: PinCodeStoreListener)
} }
class SharedPrefPinCodeStore @Inject constructor(private val sharedPreferences: SharedPreferences) : PinCodeStore { interface PinCodeStoreListener {
fun onPinSetUpChange(isConfigured: Boolean)
}
override suspend fun storeEncodedPin(encodePin: String) = withContext(Dispatchers.IO) { @Singleton
sharedPreferences.edit { class SharedPrefPinCodeStore @Inject constructor(private val sharedPreferences: SharedPreferences) : PinCodeStore {
putString(ENCODED_PIN_CODE_KEY, encodePin) private val listeners = mutableSetOf<PinCodeStoreListener>()
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) { override suspend fun deleteEncodedPin() {
// Also reset the counters withContext(Dispatchers.IO) {
resetCounters() // Also reset the counters
sharedPreferences.edit { resetCounters()
remove(ENCODED_PIN_CODE_KEY) sharedPreferences.edit {
remove(ENCODED_PIN_CODE_KEY)
}
awaitPinCodeCallback<Boolean> {
PFSecurityManager.getInstance().pinCodeHelper.delete(it)
}
} }
awaitPinCodeCallback<Boolean> { listeners.forEach { it.onPinSetUpChange(isConfigured = false) }
PFSecurityManager.getInstance().pinCodeHelper.delete(it)
}
return@withContext
} }
override fun getEncodedPin(): String? { 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 -> private suspend inline fun <T> awaitPinCodeCallback(crossinline callback: (PFPinCodeHelperCallback<T>) -> Unit) = suspendCoroutine<PFResult<T>> { cont ->
callback(PFPinCodeHelperCallback<T> { result -> cont.resume(result) }) callback(PFPinCodeHelperCallback<T> { result -> cont.resume(result) })
} }