diff --git a/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivity.kt b/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivity.kt index 4b85c045b..708a05238 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivity.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivity.kt @@ -27,6 +27,7 @@ import android.view.MenuItem import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat @@ -36,13 +37,14 @@ import androidx.core.os.bundleOf import androidx.lifecycle.lifecycleScope import androidx.navigation.NavController import androidx.navigation.NavDestination -import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.compose.rememberNavController import eu.mhutti1.utils.storage.StorageDevice import eu.mhutti1.utils.storage.StorageDeviceUtils import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.kiwix.kiwixmobile.BuildConfig import org.kiwix.kiwixmobile.R +import org.kiwix.kiwixmobile.core.CoreApp import org.kiwix.kiwixmobile.core.R.drawable import org.kiwix.kiwixmobile.core.R.id import org.kiwix.kiwixmobile.core.R.mipmap @@ -53,6 +55,7 @@ import org.kiwix.kiwixmobile.core.downloader.downloadManager.DOWNLOAD_NOTIFICATI import org.kiwix.kiwixmobile.core.extensions.toast import org.kiwix.kiwixmobile.core.main.ACTION_NEW_TAB import org.kiwix.kiwixmobile.core.main.CoreMainActivity +import org.kiwix.kiwixmobile.core.main.DrawerMenuItem import org.kiwix.kiwixmobile.core.main.NEW_TAB_SHORTCUT_ID import org.kiwix.kiwixmobile.core.main.ZIM_FILE_URI_KEY import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.handleLocaleChange @@ -82,12 +85,6 @@ class KiwixMainActivity : CoreMainActivity() { // activityKiwixMainBinding.readerDrawerNavView // } - override val navController: NavController by lazy { - val fragment = supportFragmentManager.findFragmentById(id.nav_host_fragment) - val navHostFragment = requireNotNull(fragment) as NavHostFragment - return@lazy navHostFragment.navController - } - @Inject lateinit var libkiwixBookOnDisk: LibkiwixBookOnDisk override val mainActivity: AppCompatActivity by lazy { this } @@ -117,15 +114,24 @@ class KiwixMainActivity : CoreMainActivity() { cachedComponent.inject(this) super.onCreate(savedInstanceState) setContent { + navController = rememberNavController() KiwixMainActivityScreen( navController = navController, topLevelDestinations = topLevelDestinations.toList(), isBottomBarVisible = isBottomBarVisible.value, - leftDrawerContent = { }, + leftDrawerContent = rightNavigationDrawerMenuItems, rightDrawerContent = { } ) + LaunchedEffect(navController) { + navController.addOnDestinationChangedListener(finishActionModeOnDestinationChange) + navController.addOnDestinationChangedListener { _, destination, _ -> + isBottomBarVisible.value = destination.id in topLevelDestinations + if (destination.id !in topLevelDestinations) { + handleDrawerOnNavigation() + } + } + } } - navController.addOnDestinationChangedListener(finishActionModeOnDestinationChange) // activityKiwixMainBinding.drawerNavView.apply { // setupWithNavController(navController) // setNavigationItemSelectedListener { item -> @@ -207,12 +213,6 @@ class KiwixMainActivity : CoreMainActivity() { override fun onStart() { super.onStart() - // navController.addOnDestinationChangedListener { _, destination, _ -> - // isBottomBarVisible.value = destination.id in topLevelDestinations - // if (destination.id !in topLevelDestinations) { - // handleDrawerOnNavigation() - // } - // } if (sharedPreferenceUtil.showIntro() && !isIntroScreenNotVisible()) { // navigate(KiwixReaderFragmentDirections.actionReaderFragmentToIntroFragment()) } @@ -337,6 +337,32 @@ class KiwixMainActivity : CoreMainActivity() { return true } + override val zimHostDrawerMenuItem: DrawerMenuItem? = DrawerMenuItem( + title = CoreApp.instance.getString(string.menu_wifi_hotspot), + iconRes = drawable.ic_mobile_screen_share_24px, + true, + onClick = { openZimHostFragment() } + ) + + override val helpDrawerMenuItem: DrawerMenuItem? = DrawerMenuItem( + title = CoreApp.instance.getString(string.menu_help), + iconRes = drawable.ic_help_24px, + true, + onClick = { openHelpFragment() } + ) + + override val supportDrawerMenuItem: DrawerMenuItem? = DrawerMenuItem( + title = CoreApp.instance.getString(string.menu_support_kiwix), + iconRes = drawable.ic_support_24px, + true, + onClick = { openSupportKiwixExternalLink() } + ) + + /** + * In kiwix app we are not showing the "About app" item so returning null. + */ + override val aboutAppDrawerMenuItem: DrawerMenuItem? = null + private fun openZimHostFragment() { disableDrawer() navigate(R.id.zimHostFragment) diff --git a/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivityScreen.kt b/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivityScreen.kt index 41728d8e3..eaf51fd52 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivityScreen.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivityScreen.kt @@ -18,8 +18,6 @@ package org.kiwix.kiwixmobile.main -import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope @@ -44,35 +42,31 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.viewinterop.AndroidView -import androidx.fragment.app.FragmentContainerView -import androidx.navigation.NavController +import androidx.navigation.NavHostController import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.fragment.NavHostFragment import org.kiwix.kiwixmobile.R.drawable import org.kiwix.kiwixmobile.R.id import org.kiwix.kiwixmobile.core.R -import org.kiwix.kiwixmobile.R.navigation +import org.kiwix.kiwixmobile.core.main.DrawerMenuGroup +import org.kiwix.kiwixmobile.core.main.LeftDrawerMenu import org.kiwix.kiwixmobile.core.ui.theme.KiwixTheme import org.kiwix.kiwixmobile.core.utils.ComposeDimens.NAVIGATION_DRAWER_WIDTH +import org.kiwix.kiwixmobile.ui.KiwixNavGraph @OptIn(ExperimentalMaterial3Api::class) @Composable fun KiwixMainActivityScreen( - navController: NavController, + navController: NavHostController, topLevelDestinations: List, - leftDrawerContent: @Composable ColumnScope.() -> Unit, + leftDrawerContent: List, rightDrawerContent: @Composable ColumnScope.() -> Unit, isBottomBarVisible: Boolean = true ) { val rightDrawerState = rememberDrawerState(DrawerValue.Closed) val coroutineScope = rememberCoroutineScope() val scrollingBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior() - val context = LocalContext.current - val fragmentManager = (context as AppCompatActivity).supportFragmentManager KiwixTheme { ModalNavigationDrawer( drawerContent = { @@ -81,7 +75,7 @@ fun KiwixMainActivityScreen( .fillMaxHeight() .width(NAVIGATION_DRAWER_WIDTH) ) { - leftDrawerContent() + LeftDrawerMenu(leftDrawerContent) } }, gesturesEnabled = true @@ -99,21 +93,9 @@ fun KiwixMainActivityScreen( } ) { paddingValues -> Box(modifier = Modifier.padding(paddingValues)) { - // AndroidView to host your FragmentContainerView with nav graph - AndroidView( - modifier = Modifier.fillMaxSize(), - factory = { ctx -> - FragmentContainerView(ctx).apply { - id = R.id.nav_host_fragment - if (fragmentManager.findFragmentById(id) == null) { - val navHostFragment = NavHostFragment.create(navigation.kiwix_nav_graph) - fragmentManager.beginTransaction() - .replace(id, navHostFragment) - .setPrimaryNavigationFragment(navHostFragment) - .commitNow() - } - } - } + KiwixNavGraph( + navController = navController, + modifier = Modifier.fillMaxSize() ) } } @@ -136,7 +118,7 @@ fun KiwixMainActivityScreen( @OptIn(ExperimentalMaterial3Api::class) @Composable fun BottomNavigationBar( - navController: NavController, + navController: NavHostController, scrollBehavior: BottomAppBarScrollBehavior, topLevelDestinations: List ) { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt index cf7cbeb2c..fe8578255 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt @@ -29,16 +29,11 @@ import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AppCompatActivity import androidx.core.net.toUri import androidx.core.os.bundleOf -import androidx.core.view.GravityCompat -import androidx.drawerlayout.widget.DrawerLayout -import androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED -import androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_UNLOCKED import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentContainerView import androidx.lifecycle.lifecycleScope -import androidx.navigation.NavController import androidx.navigation.NavDestination import androidx.navigation.NavDirections +import androidx.navigation.NavHostController import androidx.navigation.NavOptions import com.google.android.material.navigation.NavigationView import kotlinx.coroutines.CoroutineScope @@ -90,7 +85,12 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider { private var drawerToggle: ActionBarDrawerToggle? = null @Inject lateinit var zimReaderContainer: ZimReaderContainer - abstract val navController: NavController + + /** + * We have migrated the UI in compose, so providing the compose based navigation to activity + * is responsibility of child activities such as KiwixMainActivity, and CustomMainActivity. + */ + lateinit var navController: NavHostController // abstract val drawerContainerLayout: DrawerLayout // abstract val drawerNavView: NavigationView @@ -102,6 +102,7 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider { abstract val helpFragmentResId: Int abstract val cachedComponent: CoreActivityComponent abstract val topLevelDestinations: Set + // abstract val navHostContainer: FragmentContainerView abstract val mainActivity: AppCompatActivity abstract val appName: String @@ -115,7 +116,7 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider { @Suppress("InjectDispatcher") override fun onCreate(savedInstanceState: Bundle?) { - // setTheme(R.style.KiwixTheme) + setTheme(R.style.KiwixTheme) super.onCreate(savedInstanceState) if (!BuildConfig.DEBUG) { val appContext = applicationContext @@ -322,7 +323,7 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider { return true } - private fun openHelpFragment() { + protected fun openHelpFragment() { navigate(helpFragmentResId) handleDrawerOnNavigation() } @@ -490,6 +491,84 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider { navigate(readerFragmentResId, bundleOf(FIND_IN_PAGE_SEARCH_STRING to searchString)) } + private val bookRelatedDrawerGroup = DrawerMenuGroup( + listOfNotNull( + DrawerMenuItem( + title = CoreApp.instance.getString(R.string.bookmarks), + iconRes = R.drawable.ic_bookmark_black_24dp, + true, + onClick = { openBookmarks() } + ), + DrawerMenuItem( + title = CoreApp.instance.getString(R.string.history), + iconRes = R.drawable.ic_history_24px, + true, + onClick = { openHistory() } + ), + DrawerMenuItem( + title = CoreApp.instance.getString(R.string.pref_notes), + iconRes = R.drawable.ic_add_note, + true, + onClick = { openNotes() } + ), + zimHostDrawerMenuItem + ) + ) + + private val settingDrawerGroup = DrawerMenuGroup( + listOf( + DrawerMenuItem( + title = CoreApp.instance.getString(R.string.menu_settings), + iconRes = R.drawable.ic_settings_24px, + true, + onClick = { openSettings() } + ) + ) + ) + + open val helpAndSupportDrawerGroup = DrawerMenuGroup( + listOfNotNull( + helpDrawerMenuItem, + supportDrawerMenuItem, + aboutAppDrawerMenuItem + ) + ) + + /** + * Returns the "Wi-Fi Hotspot" menu item in the left drawer. + * Currently, this feature is only included in the main Kiwix app. + * Custom apps do not include this item. + */ + abstract val zimHostDrawerMenuItem: DrawerMenuItem? + + /** + * Returns the "Help" menu item in the left drawer. + * In custom apps, this item is hidden. + * Each app (main Kiwix or custom) provides its own implementation. + */ + abstract val helpDrawerMenuItem: DrawerMenuItem? + + /** + * Returns the "Support" menu item in the left drawer. + * In custom apps, this item displays the application name dynamically. + * Child activities are responsible for defining this drawer item. + */ + abstract val supportDrawerMenuItem: DrawerMenuItem? + + /** + * Returns the "About App" menu item in the left drawer. + * For custom apps, this item is shown if configured. + * It is not included in the main Kiwix app. + * Child activities are responsible for defining this drawer item. + */ + abstract val aboutAppDrawerMenuItem: DrawerMenuItem? + + val rightNavigationDrawerMenuItems = listOf( + bookRelatedDrawerGroup, + settingDrawerGroup, + helpAndSupportDrawerGroup + ) + protected abstract fun getIconResId(): Int abstract val readerFragmentResId: Int abstract fun createApplicationShortcuts() diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/DrawerMenuItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/DrawerMenuItem.kt index 8eb9e8c98..c4924f128 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/DrawerMenuItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/DrawerMenuItem.kt @@ -18,10 +18,11 @@ package org.kiwix.kiwixmobile.core.main +import androidx.annotation.DrawableRes + data class DrawerMenuItem( - val id: Int, val title: String, - val iconRes: Int, + @DrawableRes val iconRes: Int, val visible: Boolean = true, val onClick: () -> Unit ) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainDrawerMenu.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainDrawerMenu.kt index bfb79e4c9..9d383043d 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainDrawerMenu.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainDrawerMenu.kt @@ -28,16 +28,18 @@ import androidx.compose.material3.Icon import androidx.compose.material3.ListItem import androidx.compose.material3.Surface import androidx.compose.material3.Text -import org.kiwix.kiwixmobile.core.R import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource +import org.kiwix.kiwixmobile.core.R +import org.kiwix.kiwixmobile.core.ui.models.IconItem +import org.kiwix.kiwixmobile.core.ui.models.toPainter import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP import org.kiwix.kiwixmobile.core.utils.ComposeDimens.NAVIGATION_DRAWER_WIDTH @Composable -fun MainDrawerMenu(drawerMenuGroupList: List) { +fun LeftDrawerMenu(drawerMenuGroupList: List) { Surface( modifier = Modifier .width(NAVIGATION_DRAWER_WIDTH) @@ -47,7 +49,7 @@ fun MainDrawerMenu(drawerMenuGroupList: List) { Column { // Banner image at the top Image( - painter = painterResource(id = R.drawable.ic_home_kiwix_banner), + painter = IconItem.MipmapImage(R.drawable.ic_home_kiwix_banner).toPainter(), contentDescription = null, contentScale = ContentScale.FillWidth, modifier = Modifier.fillMaxWidth() @@ -60,7 +62,7 @@ fun MainDrawerMenu(drawerMenuGroupList: List) { } @Composable -fun DrawerGroup(items: List) { +private fun DrawerGroup(items: List) { Column { items.filter { it.visible }.forEach { item -> DrawerMenuItemView(item) @@ -69,7 +71,7 @@ fun DrawerGroup(items: List) { } @Composable -fun DrawerMenuItemView(item: DrawerMenuItem) { +private fun DrawerMenuItemView(item: DrawerMenuItem) { ListItem( leadingContent = { Icon( diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/ui/models/IconItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/ui/models/IconItem.kt index fd47ac52d..448ed758e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/ui/models/IconItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/ui/models/IconItem.kt @@ -18,6 +18,7 @@ package org.kiwix.kiwixmobile.core.ui.models +import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.drawable.AdaptiveIconDrawable import android.graphics.drawable.BitmapDrawable @@ -57,20 +58,19 @@ fun IconItem.toPainter(): Painter { is IconItem.Drawable -> painterResource(drawableRes) is IconItem.ImageBitmap -> remember { BitmapPainter(bitmap) } is IconItem.MipmapImage -> { - val drawable = ContextCompat.getDrawable(LocalContext.current, mipmapResId) - val imageBitmap = when { - drawable is BitmapDrawable -> drawable.bitmap.asImageBitmap() - Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && drawable is AdaptiveIconDrawable -> { - val bitmap = createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight) - val canvas = Canvas(bitmap) - drawable.setBounds(0, 0, canvas.width, canvas.height) - drawable.draw(canvas) - bitmap.asImageBitmap() - } - - else -> { - createBitmap(0, 0).asImageBitmap() - } + val context = LocalContext.current + val drawable = ContextCompat.getDrawable(context, mipmapResId) + val imageBitmap = drawable?.let { + val width = it.intrinsicWidth.takeIf { w -> w > 0 } ?: 100 + val height = it.intrinsicHeight.takeIf { h -> h > 0 } ?: 100 + val bitmap = createBitmap(width, height) + val canvas = Canvas(bitmap) + it.setBounds(0, 0, canvas.width, canvas.height) + it.draw(canvas) + bitmap.asImageBitmap() + } ?: run { + // fallback empty bitmap if drawable is null + createBitmap(1, 1).asImageBitmap() } return remember { BitmapPainter(imageBitmap) } } diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt index e561fda2b..b06afa437 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt @@ -30,6 +30,7 @@ import androidx.drawerlayout.widget.DrawerLayout import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment import com.google.android.material.navigation.NavigationView +import org.kiwix.kiwixmobile.core.CoreApp import org.kiwix.kiwixmobile.core.R.drawable import org.kiwix.kiwixmobile.core.R.string import org.kiwix.kiwixmobile.core.extensions.applyEdgeToEdgeInsets @@ -37,6 +38,7 @@ import org.kiwix.kiwixmobile.core.extensions.browserIntent import org.kiwix.kiwixmobile.core.extensions.getDialogHostComposeView import org.kiwix.kiwixmobile.core.main.ACTION_NEW_TAB import org.kiwix.kiwixmobile.core.main.CoreMainActivity +import org.kiwix.kiwixmobile.core.main.DrawerMenuItem import org.kiwix.kiwixmobile.core.main.NEW_TAB_SHORTCUT_ID import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower import org.kiwix.kiwixmobile.custom.BuildConfig @@ -50,7 +52,7 @@ class CustomMainActivity : CoreMainActivity() { supportFragmentManager.findFragmentById( R.id.custom_nav_controller ) as NavHostFragment - ) + ) .navController } override val drawerContainerLayout: DrawerLayout by lazy { @@ -192,6 +194,73 @@ class CustomMainActivity : CoreMainActivity() { override fun getIconResId() = R.mipmap.ic_launcher + /** + * Hide the 'ZimHostFragment' option from the navigation menu + * because we are now using fd (FileDescriptor) + * to read the zim file from the asset folder. Currently, + * 'KiwixServer' is unable to host zim files via fd. + * This feature is temporarily removed for custom apps. + * We will re-enable it for custom apps once the issue is resolved. + * For more info see https://github.com/kiwix/kiwix-android/pull/3516, + * https://github.com/kiwix/kiwix-android/issues/4026 + */ + override val zimHostDrawerMenuItem: DrawerMenuItem? = null + + /** + * Hide the `HelpFragment` from custom apps. + * We have not removed the relevant code for `HelpFragment` from custom apps. + * If, in the future, we need to display this for all/some custom apps, + * we can either remove the line below or configure it according to the requirements. + * For more information, see https://github.com/kiwix/kiwix-android/issues/3584 + */ + override val helpDrawerMenuItem: DrawerMenuItem? = null + + override val supportDrawerMenuItem: DrawerMenuItem? = + /** + * If custom app is configured to show the "Support app_name" in navigation + * then show it navigation. "app_name" will be replaced with custom app name. + */ + if (BuildConfig.SUPPORT_URL.isNotEmpty()) { + DrawerMenuItem( + title = CoreApp.instance.getString( + string.menu_support_kiwix_for_custom_apps, + CoreApp.instance.getString(R.string.app_name) + ), + iconRes = drawable.ic_support_24px, + true, + onClick = { + externalLinkOpener.openExternalUrl(BuildConfig.SUPPORT_URL.toUri().browserIntent(), false) + } + ) + } else { + /** + * If custom app is not configured to show the "Support app_name" in navigation + * then remove it from navigation. + */ + null + } + + /** + * If custom app is configured to show the "About app_name app" in navigation + * then show it navigation. "app_name" will be replaced with custom app name. + */ + override val aboutAppDrawerMenuItem: DrawerMenuItem? = + if (BuildConfig.ABOUT_APP_URL.isNotEmpty()) { + DrawerMenuItem( + title = CoreApp.instance.getString( + string.menu_about_app, + CoreApp.instance.getString(R.string.app_name) + ), + iconRes = drawable.ic_baseline_info, + true, + onClick = { + externalLinkOpener.openExternalUrl(BuildConfig.SUPPORT_URL.toUri().browserIntent(), false) + } + ) + } else { + null + } + override fun createApplicationShortcuts() { // Remove previously added dynamic shortcuts for old ids if any found. removeOutdatedIdShortcuts()