Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
1 change: 1 addition & 0 deletions changelog.d/4170.bugfix
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
Expand Up @@ -94,13 +94,15 @@ interface RoomService {
* Get a snapshot list of room summaries.
* @return the immutable list of [RoomSummary]
*/
fun getRoomSummaries(queryParams: RoomSummaryQueryParams): List<RoomSummary>
fun getRoomSummaries(queryParams: RoomSummaryQueryParams,
sortOrder: RoomSortOrder = RoomSortOrder.NONE): List<RoomSummary>

/**
* Get a live list of room summaries. This list is refreshed as soon as the data changes.
* @return the [LiveData] of List[RoomSummary]
*/
fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams): LiveData<List<RoomSummary>>
fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams,
sortOrder: RoomSortOrder = RoomSortOrder.ACTIVITY): LiveData<List<RoomSummary>>

/**
* Get a snapshot list of Breadcrumbs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package org.matrix.android.sdk.api.session.space
import android.net.Uri
import androidx.lifecycle.LiveData
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.RoomSortOrder
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult
Expand Down Expand Up @@ -74,9 +75,11 @@ interface SpaceService {
* Get a live list of space summaries. This list is refreshed as soon as the data changes.
* @return the [LiveData] of List[SpaceSummary]
*/
fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams): LiveData<List<RoomSummary>>
fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams,
sortOrder: RoomSortOrder = RoomSortOrder.NONE): LiveData<List<RoomSummary>>

fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams): List<RoomSummary>
fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams,
sortOrder: RoomSortOrder = RoomSortOrder.NONE): List<RoomSummary>

suspend fun joinSpace(spaceIdOrAlias: String,
reason: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,14 @@ internal class DefaultRoomService @Inject constructor(
return roomSummaryDataSource.getRoomSummary(roomIdOrAlias)
}

override fun getRoomSummaries(queryParams: RoomSummaryQueryParams): List<RoomSummary> {
return roomSummaryDataSource.getRoomSummaries(queryParams)
override fun getRoomSummaries(queryParams: RoomSummaryQueryParams,
sortOrder: RoomSortOrder): List<RoomSummary> {
return roomSummaryDataSource.getRoomSummaries(queryParams, sortOrder)
}

override fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams): LiveData<List<RoomSummary>> {
return roomSummaryDataSource.getRoomSummariesLive(queryParams)
override fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams,
sortOrder: RoomSortOrder): LiveData<List<RoomSummary>> {
return roomSummaryDataSource.getRoomSummariesLive(queryParams, sortOrder)
}

override fun getPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import androidx.paging.PagedList
import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.Sort
import io.realm.kotlin.where
import org.matrix.android.sdk.api.query.ActiveSpaceFilter
import org.matrix.android.sdk.api.query.RoomCategoryFilter
Expand Down Expand Up @@ -80,25 +79,27 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
}
}

fun getRoomSummaries(queryParams: RoomSummaryQueryParams): List<RoomSummary> {
fun getRoomSummaries(queryParams: RoomSummaryQueryParams,
sortOrder: RoomSortOrder = RoomSortOrder.NONE): List<RoomSummary> {
return monarchy.fetchAllMappedSync(
{ roomSummariesQuery(it, queryParams) },
{ roomSummariesQuery(it, queryParams).process(sortOrder) },
{ roomSummaryMapper.map(it) }
)
}

fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams): LiveData<List<RoomSummary>> {
fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams,
sortOrder: RoomSortOrder = RoomSortOrder.NONE): LiveData<List<RoomSummary>> {
return monarchy.findAllMappedWithChanges(
{
roomSummariesQuery(it, queryParams)
.sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING)
roomSummariesQuery(it, queryParams).process(sortOrder)
},
{ roomSummaryMapper.map(it) }
)
}

fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams): LiveData<List<RoomSummary>> {
return getRoomSummariesLive(queryParams)
fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams,
sortOrder: RoomSortOrder = RoomSortOrder.NONE): LiveData<List<RoomSummary>> {
return getRoomSummariesLive(queryParams, sortOrder)
}

fun getSpaceSummary(roomIdOrAlias: String): RoomSummary? {
Expand All @@ -122,8 +123,9 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
}
}

fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams): List<RoomSummary> {
return getRoomSummaries(spaceSummaryQueryParams)
fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams,
sortOrder: RoomSortOrder = RoomSortOrder.NONE): List<RoomSummary> {
return getRoomSummaries(spaceSummaryQueryParams, sortOrder)
}

fun getRootSpaceSummaries(): List<RoomSummary> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.RoomSortOrder
import org.matrix.android.sdk.api.session.room.model.GuestAccess
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
Expand Down Expand Up @@ -94,12 +95,14 @@ internal class DefaultSpaceService @Inject constructor(
return spaceGetter.get(spaceId)
}

override fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams): LiveData<List<RoomSummary>> {
return roomSummaryDataSource.getSpaceSummariesLive(queryParams)
override fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams,
sortOrder: RoomSortOrder): LiveData<List<RoomSummary>> {
return roomSummaryDataSource.getSpaceSummariesLive(queryParams, sortOrder)
}

override fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams): List<RoomSummary> {
return roomSummaryDataSource.getSpaceSummaries(spaceSummaryQueryParams)
override fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams,
sortOrder: RoomSortOrder): List<RoomSummary> {
return roomSummaryDataSource.getSpaceSummaries(spaceSummaryQueryParams, sortOrder)
}

override fun getRootSpaceSummaries(): List<RoomSummary> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getPagedRoomSummariesLive was used before, which was not correct, only the first 20 rooms was returned

Copy link
Copy Markdown
Contributor

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~

Copy link
Copy Markdown
Member Author

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

roomSummaryQueryParams {
memberships = listOf(Membership.JOIN)
},
sortOrder = RoomSortOrder.PRIORITY_AND_ACTIVITY
)
.asObservable()
.doOnDispose {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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 

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please be aware the code will change with the Rx-Flow migration

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I can do this small change.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

disableShortcuts is also called now


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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not exactly true.
If a PIN code is set it will still be possible to pin (the term is the same :/) a room from the room profile screen, so inside the app, i.e. once the PIN code has been entered. But the limited list of shortcuts (room) that we can see when long pressing on the app icon will not be visible anymore.

So a PIN code prevent a few rooms (5 on my phone) to be disclosed without having to enter the PIN code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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() {
Expand All @@ -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 {
Expand All @@ -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
}
}
47 changes: 35 additions & 12 deletions vector/src/main/java/im/vector/app/features/pin/PinCodeStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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? {
Expand Down Expand Up @@ -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) })
}
Expand Down