From 6e0ca3958b1d47cd4877c12cf4b505e58b392c6b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 28 Jul 2022 16:03:13 +0200 Subject: [PATCH 1/3] Shortcuts must first start MainActivity to ensure the session is restored. --- .../im/vector/app/features/MainActivity.kt | 17 +++++++++++++++++ .../app/features/home/ShortcutCreator.kt | 4 ++-- .../home/room/detail/RoomDetailActivity.kt | 18 +----------------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index 61127e2c82..0f658a2d8c 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -86,6 +86,8 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity private const val EXTRA_ARGS = "EXTRA_ARGS" private const val EXTRA_NEXT_INTENT = "EXTRA_NEXT_INTENT" private const val EXTRA_INIT_SESSION = "EXTRA_INIT_SESSION" + private const val EXTRA_ROOM_ID = "EXTRA_ROOM_ID" + private const val ACTION_ROOM_DETAILS_FROM_SHORTCUT = "ROOM_DETAILS_FROM_SHORTCUT" // Special action to clear cache and/or clear credentials fun restartApp(activity: Activity, args: MainActivityArgs) { @@ -107,6 +109,14 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity intent.putExtra(EXTRA_NEXT_INTENT, nextIntent) return intent } + + // Shortcuts can't have intents with parcelables + fun shortcutIntent(context: Context, roomId: String): Intent { + return Intent(context, MainActivity::class.java).apply { + action = ACTION_ROOM_DETAILS_FROM_SHORTCUT + putExtra(EXTRA_ROOM_ID, roomId) + } + } } private val startAppViewModel: StartAppViewModel by viewModel() @@ -171,6 +181,13 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity } else if (intent.hasExtra(EXTRA_INIT_SESSION)) { setResult(RESULT_OK) finish() + } else if (intent.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) { + val roomId = intent.getStringExtra(EXTRA_ROOM_ID) + if (roomId?.isNotEmpty() == true) { + // TODO Add a trigger Shortcut to the analytics. + navigator.openRoom(this, roomId) + } + finish() } else { args = parseArgs() if (args.clearCredentials || args.isUserLoggedOut || args.clearCache) { diff --git a/vector/src/main/java/im/vector/app/features/home/ShortcutCreator.kt b/vector/src/main/java/im/vector/app/features/home/ShortcutCreator.kt index 23cbe3dfa2..a51b8eff63 100644 --- a/vector/src/main/java/im/vector/app/features/home/ShortcutCreator.kt +++ b/vector/src/main/java/im/vector/app/features/home/ShortcutCreator.kt @@ -27,7 +27,7 @@ import androidx.core.graphics.drawable.IconCompat import im.vector.app.BuildConfig import im.vector.app.core.glide.GlideApp import im.vector.app.core.utils.DimensionConverter -import im.vector.app.features.home.room.detail.RoomDetailActivity +import im.vector.app.features.MainActivity import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject @@ -58,7 +58,7 @@ class ShortcutCreator @Inject constructor( @WorkerThread fun create(roomSummary: RoomSummary, rank: Int = 1): ShortcutInfoCompat { - val intent = RoomDetailActivity.shortcutIntent(context, roomSummary.roomId) + val intent = MainActivity.shortcutIntent(context, roomSummary.roomId) val bitmap = try { val glideRequests = GlideApp.with(context) val matrixItem = roomSummary.toMatrixItem() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt index a58eed42e1..75cda67ce6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt @@ -99,12 +99,7 @@ class RoomDetailActivity : super.onCreate(savedInstanceState) supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false) waitingView = views.waitingView.waitingView - val timelineArgs: TimelineArgs? = if (intent?.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) { - TimelineArgs(roomId = intent?.extras?.getString(EXTRA_ROOM_ID)!!) - } else { - intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS) - } - if (timelineArgs == null) return + val timelineArgs: TimelineArgs = intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS) ?: return intent.putExtra(Mavericks.KEY_ARG, timelineArgs) currentRoomId = timelineArgs.roomId @@ -187,10 +182,7 @@ class RoomDetailActivity : } companion object { - const val EXTRA_ROOM_DETAIL_ARGS = "EXTRA_ROOM_DETAIL_ARGS" - const val EXTRA_ROOM_ID = "EXTRA_ROOM_ID" - const val ACTION_ROOM_DETAILS_FROM_SHORTCUT = "ROOM_DETAILS_FROM_SHORTCUT" fun newIntent(context: Context, timelineArgs: TimelineArgs, firstStartMainActivity: Boolean): Intent { val intent = Intent(context, RoomDetailActivity::class.java).apply { @@ -202,14 +194,6 @@ class RoomDetailActivity : intent } } - - // Shortcuts can't have intents with parcelables - fun shortcutIntent(context: Context, roomId: String): Intent { - return Intent(context, RoomDetailActivity::class.java).apply { - action = ACTION_ROOM_DETAILS_FROM_SHORTCUT - putExtra(EXTRA_ROOM_ID, roomId) - } - } } override fun mxToBottomSheetNavigateToRoom(roomId: String, trigger: ViewRoom.Trigger?) { From eee418d1fd26093e9f7a0484529d57e5980739ba Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 28 Jul 2022 18:14:54 +0200 Subject: [PATCH 2/3] Migrate previous pinned Shortcut to the new Intent --- .../im/vector/app/features/MainActivity.kt | 2 ++ .../app/features/home/ShortcutsHandler.kt | 34 ++++++++++++++++++- .../features/settings/VectorPreferences.kt | 5 ++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index 0f658a2d8c..14fae80325 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -141,6 +141,8 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + shortcutsHandler.updateShortcutsWithPreviousIntent() + startAppViewModel.onEach { renderState(it) } diff --git a/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt b/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt index cae2ca51c8..db0d7e07bc 100644 --- a/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt +++ b/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt @@ -17,14 +17,20 @@ package im.vector.app.features.home import android.content.Context +import android.content.SharedPreferences import android.content.pm.ShortcutManager import android.os.Build +import androidx.core.content.edit import androidx.core.content.getSystemService +import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.di.DefaultPreferences import im.vector.app.core.dispatchers.CoroutineDispatchers import im.vector.app.core.resources.StringProvider +import im.vector.app.features.MainActivity +import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.pin.PinCodeStore import im.vector.app.features.pin.PinCodeStoreListener import kotlinx.coroutines.CoroutineScope @@ -35,6 +41,7 @@ import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.extensions.tryOrNull 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 @@ -50,7 +57,9 @@ class ShortcutsHandler @Inject constructor( private val appDispatchers: CoroutineDispatchers, private val shortcutCreator: ShortcutCreator, private val activeSessionHolder: ActiveSessionHolder, - private val pinCodeStore: PinCodeStore + private val pinCodeStore: PinCodeStore, + @DefaultPreferences + private val sharedPreferences: SharedPreferences, ) : PinCodeStoreListener { private val isRequestPinShortcutSupported = ShortcutManagerCompat.isRequestPinShortcutSupported(context) @@ -87,6 +96,25 @@ class ShortcutsHandler @Inject constructor( .launchIn(coroutineScope) } + fun updateShortcutsWithPreviousIntent() { + // Check if it's been already done + if (sharedPreferences.getBoolean(SHARED_PREF_KEY, false)) return + ShortcutManagerCompat.getShortcuts(context, ShortcutManagerCompat.FLAG_MATCH_PINNED) + .filter { it.intent.component?.className == RoomDetailActivity::class.qualifiedName } + .mapNotNull { + it.intent.getStringExtra("EXTRA_ROOM_ID")?.let { roomId -> + ShortcutInfoCompat.Builder(context, it.toShortcutInfo()) + .setIntent(MainActivity.shortcutIntent(context, roomId)) + .build() + } + } + .takeIf { it.isNotEmpty() } + ?.also { Timber.d("Update ${it.size} shortcut(s)") } + ?.let { tryOrNull("Error") { ShortcutManagerCompat.updateShortcuts(context, it) } } + ?.also { Timber.d("Update shortcuts with success: $it") } + sharedPreferences.edit { putBoolean(SHARED_PREF_KEY, true) } + } + private fun removeDeadShortcuts(roomIds: List) { val deadShortcutIds = ShortcutManagerCompat.getShortcuts(context, ShortcutManagerCompat.FLAG_MATCH_DYNAMIC) .map { it.id } @@ -163,4 +191,8 @@ class ShortcutsHandler @Inject constructor( // Else shortcut will be created next time any room summary is updated, or // next time the app is started which is acceptable } + + companion object { + const val SHARED_PREF_KEY = "ROOM_DETAIL_ACTIVITY_SHORTCUT_UPDATED" + } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index 587e6a59b4..3b202969e5 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -28,6 +28,7 @@ import im.vector.app.R import im.vector.app.core.di.DefaultSharedPreferences import im.vector.app.core.time.Clock import im.vector.app.features.disclaimer.SHARED_PREF_KEY +import im.vector.app.features.home.ShortcutsHandler import im.vector.app.features.homeserver.ServerUrlsRepository import im.vector.app.features.themes.ThemeUtils import org.matrix.android.sdk.api.extensions.tryOrNull @@ -268,7 +269,9 @@ class VectorPreferences @Inject constructor( SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY, SETTINGS_USE_RAGE_SHAKE_KEY, - SETTINGS_SECURITY_USE_FLAG_SECURE + SETTINGS_SECURITY_USE_FLAG_SECURE, + + ShortcutsHandler.SHARED_PREF_KEY, ) } From a2e85066006e0a1fe0c156597e3538f9540a382a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 28 Jul 2022 19:03:05 +0200 Subject: [PATCH 3/3] Fix and suppress lint issue. RestrictedApi is for the usage of ShortcutInfoCompat.Builder. --- .../main/java/im/vector/app/features/home/ShortcutsHandler.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt b/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt index db0d7e07bc..3a72907467 100644 --- a/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt +++ b/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home +import android.annotation.SuppressLint import android.content.Context import android.content.SharedPreferences import android.content.pm.ShortcutManager @@ -96,7 +97,9 @@ class ShortcutsHandler @Inject constructor( .launchIn(coroutineScope) } + @SuppressLint("RestrictedApi") fun updateShortcutsWithPreviousIntent() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) return // Check if it's been already done if (sharedPreferences.getBoolean(SHARED_PREF_KEY, false)) return ShortcutManagerCompat.getShortcuts(context, ShortcutManagerCompat.FLAG_MATCH_PINNED)