From ef2d86e72c3f0f886d6dd4c6214a427d3a16e6d7 Mon Sep 17 00:00:00 2001 From: Neer-rn Date: Fri, 10 Apr 2026 23:18:34 +0545 Subject: [PATCH 01/12] Fix #6209: Add EDGE_TO_EDGE feature flag for edge-to-edge display support --- .../android/config/platform/feature_flags.textproto | 5 +++++ .../platformparameter/FeatureFlagBindingModule.kt | 6 ++++++ .../FeatureFlagsMapBindingModule.kt | 9 +++++++++ .../oppialogger/analytics/FeatureFlagsLoggerTest.kt | 3 ++- model/src/main/proto/platform_parameter.proto | 3 +++ .../TestPlatformParameterModule.kt | 5 +++++ .../util/platformparameter/FeatureFlagConstants.kt | 13 +++++++++++++ 7 files changed, 43 insertions(+), 1 deletion(-) diff --git a/config/src/java/org/oppia/android/config/platform/feature_flags.textproto b/config/src/java/org/oppia/android/config/platform/feature_flags.textproto index 764763671e2..c566358d4d6 100644 --- a/config/src/java/org/oppia/android/config/platform/feature_flags.textproto +++ b/config/src/java/org/oppia/android/config/platform/feature_flags.textproto @@ -73,3 +73,8 @@ feature_flag_definition { remote_server_name: "android_enable_topic_practice_tab" default_is_enabled: false } +feature_flag_definition { + id: EDGE_TO_EDGE + remote_server_name: "android_enable_edge_to_edge" + default_is_enabled: true +} diff --git a/domain/src/main/java/org/oppia/android/domain/platformparameter/FeatureFlagBindingModule.kt b/domain/src/main/java/org/oppia/android/domain/platformparameter/FeatureFlagBindingModule.kt index 56137e6f915..af9b35161b9 100644 --- a/domain/src/main/java/org/oppia/android/domain/platformparameter/FeatureFlagBindingModule.kt +++ b/domain/src/main/java/org/oppia/android/domain/platformparameter/FeatureFlagBindingModule.kt @@ -5,6 +5,7 @@ import dagger.Provides import org.oppia.android.app.model.FeatureFlagId import org.oppia.android.util.platformparameter.EnableAppAndOsDeprecation import org.oppia.android.util.platformparameter.EnableDownloadsSupport +import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.EnableEditAccountsOptionsUi import org.oppia.android.util.platformparameter.EnableFastLanguageSwitchingInLesson import org.oppia.android.util.platformparameter.EnableFlashbackSupport @@ -101,6 +102,11 @@ class FeatureFlagBindingModule { fun provideEnableTopicPracticeTab(processState: PlatformParameterProcessState) = processState.retrieveFeatureFlag(FeatureFlagId.TOPIC_PRACTICE_TAB) + @Provides + @EnableEdgeToEdge + fun provideEnableEdgeToEdge(processState: PlatformParameterProcessState) = + processState.retrieveFeatureFlag(FeatureFlagId.EDGE_TO_EDGE) + private companion object { private fun PlatformParameterProcessState.retrieveFeatureFlag( featureFlagId: FeatureFlagId diff --git a/domain/src/main/java/org/oppia/android/domain/platformparameter/FeatureFlagsMapBindingModule.kt b/domain/src/main/java/org/oppia/android/domain/platformparameter/FeatureFlagsMapBindingModule.kt index 31583782cf0..2a3f9f1dad4 100644 --- a/domain/src/main/java/org/oppia/android/domain/platformparameter/FeatureFlagsMapBindingModule.kt +++ b/domain/src/main/java/org/oppia/android/domain/platformparameter/FeatureFlagsMapBindingModule.kt @@ -6,6 +6,7 @@ import dagger.multibindings.IntoMap import org.oppia.android.app.model.FeatureFlagId import org.oppia.android.util.platformparameter.EnableAppAndOsDeprecation import org.oppia.android.util.platformparameter.EnableDownloadsSupport +import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.EnableEditAccountsOptionsUi import org.oppia.android.util.platformparameter.EnableFastLanguageSwitchingInLesson import org.oppia.android.util.platformparameter.EnableFlashbackSupport @@ -144,4 +145,12 @@ interface FeatureFlagsMapBindingModule { fun bindTopicPracticeTab( @EnableTopicPracticeTab param: PlatformParameterValue ): PlatformParameterValue + + @Binds + @IntoMap + @FeatureFlags + @FeatureFlagIdKey(FeatureFlagId.EDGE_TO_EDGE) + fun bindEdgeToEdge( + @EnableEdgeToEdge param: PlatformParameterValue + ): PlatformParameterValue } diff --git a/domain/src/test/java/org/oppia/android/domain/oppialogger/analytics/FeatureFlagsLoggerTest.kt b/domain/src/test/java/org/oppia/android/domain/oppialogger/analytics/FeatureFlagsLoggerTest.kt index 3bac46f37f0..613852b4b42 100644 --- a/domain/src/test/java/org/oppia/android/domain/oppialogger/analytics/FeatureFlagsLoggerTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/oppialogger/analytics/FeatureFlagsLoggerTest.kt @@ -141,7 +141,7 @@ class FeatureFlagsLoggerTest { @Test fun testLogFeatureFlags_correctNumberOfFeatureFlagsIsLogged() { - val expectedFeatureFlagCount = 15 + val expectedFeatureFlagCount = 16 featureFlagsLogger.logAllFeatureFlags(TEST_SESSION_ID) testCoroutineDispatchers.runCurrent() @@ -168,6 +168,7 @@ class FeatureFlagsLoggerTest { @Iteration("flashback_support", "index=12", "flagId=FLASHBACK_SUPPORT") @Iteration("topic_info_tab", "index=13", "flagId=TOPIC_INFO_TAB") @Iteration("topic_practice_tab", "index=14", "flagId=TOPIC_PRACTICE_TAB") + @Iteration("edge_to_edge", "index=15", "flagId=EDGE_TO_EDGE") fun testLogFeatureFlags_allFeatureFlagIdsAreLogged() { featureFlagsLogger.logAllFeatureFlags(TEST_SESSION_ID) diff --git a/model/src/main/proto/platform_parameter.proto b/model/src/main/proto/platform_parameter.proto index 6390feb98bc..6f447a93936 100644 --- a/model/src/main/proto/platform_parameter.proto +++ b/model/src/main/proto/platform_parameter.proto @@ -149,6 +149,9 @@ enum FeatureFlagId { // Corresponds to a topic activity feature that enables the practice tab. TOPIC_PRACTICE_TAB = 18; + + // Corresponds to a feature enabling edge-to-edge display support for Android 15+ compatibility. + EDGE_TO_EDGE = 19; } // Corresponds to a compile-time definition of a platform parameter which is a mechanism by which to diff --git a/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt b/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt index 8fe3503e6c3..9f54961bcb6 100644 --- a/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt +++ b/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import org.oppia.android.app.model.FeatureFlagId.APP_AND_OS_DEPRECATION import org.oppia.android.app.model.FeatureFlagId.DOWNLOADS_SUPPORT +import org.oppia.android.app.model.FeatureFlagId.EDGE_TO_EDGE import org.oppia.android.app.model.FeatureFlagId.EDIT_ACCOUNTS_OPTIONS_UI import org.oppia.android.app.model.FeatureFlagId.FAST_LANGUAGE_SWITCHING_IN_LESSON import org.oppia.android.app.model.FeatureFlagId.FLASHBACK_SUPPORT @@ -150,6 +151,10 @@ class TestPlatformParameterModule { TestPlatformParameterConfigRetriever.setFlagOverride(TOPIC_PRACTICE_TAB, value) } + fun forceEnableEdgeToEdge(value: Boolean) { + TestPlatformParameterConfigRetriever.setFlagOverride(EDGE_TO_EDGE, value) + } + fun reset() { TestPlatformParameterConfigRetriever.reset() } diff --git a/utility/src/main/java/org/oppia/android/util/platformparameter/FeatureFlagConstants.kt b/utility/src/main/java/org/oppia/android/util/platformparameter/FeatureFlagConstants.kt index 61fc10281f9..d7507544b05 100644 --- a/utility/src/main/java/org/oppia/android/util/platformparameter/FeatureFlagConstants.kt +++ b/utility/src/main/java/org/oppia/android/util/platformparameter/FeatureFlagConstants.kt @@ -201,3 +201,16 @@ const val ENABLE_TOPIC_PRACTICE_TAB = "enable_topic_practice_tab" /** Default value for the feature flag corresponding to [EnableTopicPracticeTab]. */ const val ENABLE_TOPIC_PRACTICE_TAB_DEFAULT_VALUE = false + +/** + * Qualifier for the feature flag that controls whether edge-to-edge display support + * is enabled for Android 15+ (API 35+) compatibility. + */ +@Qualifier +annotation class EnableEdgeToEdge + +/** Name of the feature flag that controls whether to enable edge-to-edge display support. */ +const val EDGE_TO_EDGE = "android_enable_edge_to_edge" + +/** Default value for the feature flag corresponding to [EnableEdgeToEdge]. */ +const val ENABLE_EDGE_TO_EDGE_DEFAULT_VALUE = false From 5a2f32bab10ddd34919300613f9eb640643ccd7f Mon Sep 17 00:00:00 2001 From: Neer-rn Date: Sat, 11 Apr 2026 16:47:56 +0545 Subject: [PATCH 02/12] Fix #5940: Add edge-to-edge window insets for drawer-based activities --- .../AdministratorControlsActivityPresenter.kt | 49 +++++++++- .../ClassroomListActivityPresenter.kt | 54 ++++++++++- .../DeveloperOptionsActivityPresenter.kt | 50 +++++++++- .../NavigationDrawerFragmentPresenter.kt | 92 +++++++++++++++---- .../android/app/help/HelpActivityPresenter.kt | 52 ++++++++++- .../android/app/home/HomeActivityPresenter.kt | 53 ++++++++++- .../app/options/OptionsActivityPresenter.kt | 52 ++++++++++- 7 files changed, 376 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt index a7095bc04f3..79a8439ca61 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt @@ -1,8 +1,13 @@ package org.oppia.android.app.administratorcontrols +import android.os.Build import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import androidx.databinding.DataBindingUtil import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.administratorcontrols.appversion.AppVersionFragment @@ -17,6 +22,8 @@ import org.oppia.android.app.settings.profile.ProfileListFragment import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R import org.oppia.android.util.extensions.putProto +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId import javax.inject.Inject @@ -24,7 +31,8 @@ import javax.inject.Inject @ActivityScope class AdministratorControlsActivityPresenter @Inject constructor( private val activity: AppCompatActivity, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var navigationDrawerFragment: NavigationDrawerFragment private var isMultipane = false @@ -47,6 +55,9 @@ class AdministratorControlsActivityPresenter @Inject constructor( R.layout.administrator_controls_activity ) setUpNavigationDrawer() + if (enableEdgeToEdge.value) { + applyEdgeToEdgeInsets() + } this.lastLoadedFragment = lastLoadedFragment this.selectedProfileId = selectedProfileId this.isProfileDeletionDialogVisible = isProfileDeletionDialogVisible @@ -199,6 +210,42 @@ class AdministratorControlsActivityPresenter @Inject constructor( binding.extraControlsTitle?.text = title } + private fun applyEdgeToEdgeInsets() { + val toolbar = binding.administratorControlsActivityToolbar + val contentLayout = toolbar.parent as android.widget.LinearLayout + val statusBarBackground = View(activity).apply { + setBackgroundColor( + ContextCompat.getColor( + activity, + R.color.component_color_shared_activity_status_bar_color + ) + ) + } + contentLayout.addView(statusBarBackground, 0) + ViewCompat.setOnApplyWindowInsetsListener(statusBarBackground) { view, insets -> + val systemBars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or + WindowInsetsCompat.Type.displayCutout() + ) + view.layoutParams.height = systemBars.top + view.requestLayout() + insets + } + ViewCompat.setOnApplyWindowInsetsListener( + binding.administratorControlsActivityDrawerLayout + ) { view, insets -> + val systemBars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or + WindowInsetsCompat.Type.displayCutout() + ) + view.updatePadding(bottom = systemBars.bottom) + insets + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + activity.window.isNavigationBarContrastEnforced = false + } + } + /** Saves the state of the views on configuration changes. */ fun handleOnSaveInstanceState(outState: Bundle) { val titleTextView = binding.extraControlsTitle diff --git a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt index e887df1b8dc..7e05b4e459d 100644 --- a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt @@ -1,18 +1,28 @@ package org.oppia.android.app.classroom +import android.os.Build import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar +import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import androidx.drawerlayout.widget.DrawerLayout import org.oppia.android.app.drawer.NavigationDrawerFragment import org.oppia.android.app.ui.R +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** Tag for identifying the [ClassroomListFragment] in transactions. */ private const val TAG_CLASSROOM_LIST_FRAGMENT = "CLASSROOM_LIST_FRAGMENT" /** The presenter for [ClassroomListActivity]. */ -class ClassroomListActivityPresenter @Inject constructor(private val activity: AppCompatActivity) { +class ClassroomListActivityPresenter @Inject constructor( + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue +) { private var navigationDrawerFragment: NavigationDrawerFragment? = null /** @@ -22,6 +32,9 @@ class ClassroomListActivityPresenter @Inject constructor(private val activity: A fun handleOnCreate() { activity.setContentView(R.layout.classroom_list_activity) setUpNavigationDrawer() + if (enableEdgeToEdge.value) { + applyEdgeToEdgeInsets() + } if (getClassroomListFragment() == null) { activity.supportFragmentManager.beginTransaction().add( R.id.classroom_list_fragment_placeholder, @@ -51,4 +64,43 @@ class ClassroomListActivityPresenter @Inject constructor(private val activity: A R.id.classroom_list_fragment_placeholder ) as ClassroomListFragment? } + + private fun applyEdgeToEdgeInsets() { + val toolbar = activity.findViewById( + R.id.classroom_list_activity_toolbar + ) + val contentLayout = toolbar.parent as android.widget.LinearLayout + val statusBarBackground = View(activity).apply { + setBackgroundColor( + ContextCompat.getColor( + activity, + R.color.component_color_shared_activity_status_bar_color + ) + ) + } + contentLayout.addView(statusBarBackground, 0) + ViewCompat.setOnApplyWindowInsetsListener(statusBarBackground) { view, insets -> + val systemBars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or + WindowInsetsCompat.Type.displayCutout() + ) + view.layoutParams.height = systemBars.top + view.requestLayout() + insets + } + val drawerLayout = activity.findViewById( + R.id.classroom_list_activity_drawer_layout + ) + ViewCompat.setOnApplyWindowInsetsListener(drawerLayout) { view, insets -> + val systemBars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or + WindowInsetsCompat.Type.displayCutout() + ) + view.updatePadding(bottom = systemBars.bottom) + insets + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + activity.window.isNavigationBarContrastEnforced = false + } + } } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt index d0fa5b177f4..aaee99a300d 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt @@ -1,13 +1,21 @@ package org.oppia.android.app.devoptions import android.content.Intent +import android.os.Build +import android.view.View import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import androidx.databinding.DataBindingUtil import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.DeveloperOptionsActivityBinding import org.oppia.android.app.drawer.NavigationDrawerFragment import org.oppia.android.app.splash.SplashActivity import org.oppia.android.app.ui.R +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject import kotlin.system.exitProcess @@ -17,7 +25,8 @@ const val TAG_FORCE_DOWNLOAD_DIALOG = "FORCE_DOWNLOAD_DIALOG_TAG" /** The presenter for [DeveloperOptionsActivity]. */ @ActivityScope class DeveloperOptionsActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var navigationDrawerFragment: NavigationDrawerFragment private lateinit var binding: DeveloperOptionsActivityBinding @@ -28,6 +37,9 @@ class DeveloperOptionsActivityPresenter @Inject constructor( R.layout.developer_options_activity ) setUpNavigationDrawer() + if (enableEdgeToEdge.value) { + applyEdgeToEdgeInsets() + } val previousFragment = getDeveloperOptionsFragment() if (previousFragment == null) { activity.supportFragmentManager.beginTransaction().add( @@ -71,6 +83,42 @@ class DeveloperOptionsActivityPresenter @Inject constructor( dialog.showNow(activity.supportFragmentManager, TAG_FORCE_DOWNLOAD_DIALOG) } + private fun applyEdgeToEdgeInsets() { + val toolbar = binding.developerOptionsActivityToolbar + val contentLayout = toolbar.parent as android.widget.LinearLayout + val statusBarBackground = View(activity).apply { + setBackgroundColor( + ContextCompat.getColor( + activity, + R.color.component_color_shared_activity_status_bar_color + ) + ) + } + contentLayout.addView(statusBarBackground, 0) + ViewCompat.setOnApplyWindowInsetsListener(statusBarBackground) { view, insets -> + val systemBars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or + WindowInsetsCompat.Type.displayCutout() + ) + view.layoutParams.height = systemBars.top + view.requestLayout() + insets + } + ViewCompat.setOnApplyWindowInsetsListener( + binding.developerOptionsActivityDrawerLayout + ) { view, insets -> + val systemBars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or + WindowInsetsCompat.Type.displayCutout() + ) + view.updatePadding(bottom = systemBars.bottom) + insets + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + activity.window.isNavigationBarContrastEnforced = false + } + } + /** Called when restart is triggered by [ForceDownloadRemoteParametersDialogFragment]. */ fun restartApp() { val intent = Intent(activity, SplashActivity::class.java).also { diff --git a/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt index 44a88f9cb39..16179c74730 100644 --- a/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt @@ -7,7 +7,10 @@ import android.view.ViewGroup import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat import androidx.core.view.forEach +import androidx.core.view.updatePadding import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.Fragment import androidx.lifecycle.LiveData @@ -38,6 +41,7 @@ import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.domain.topic.TopicController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.EnableMultipleClassrooms import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId @@ -58,6 +62,7 @@ class NavigationDrawerFragmentPresenter @Inject constructor( private val footerViewModel: NavigationDrawerFooterViewModel, private val developerOptionsStarter: Optional, @EnableMultipleClassrooms private val enableMultipleClassrooms: PlatformParameterValue, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue, ) : NavigationView.OnNavigationItemSelectedListener { private lateinit var drawerToggle: ActionBarDrawerToggle private lateinit var drawerLayout: DrawerLayout @@ -93,9 +98,48 @@ class NavigationDrawerFragmentPresenter @Inject constructor( // TODO(#3382): Remove debug only code from prod build (also check imports, constructor and drawer_fragment.xml) setIfDeveloperOptionsMenuItemListener() + if (enableEdgeToEdge.value) { + applyEdgeToEdgeInsetsToDrawer() + } + return binding.root } + private fun applyEdgeToEdgeInsetsToDrawer() { + // Disable fitsSystemWindows on the inner NavigationView since we handle insets manually. + binding.fragmentDrawerNavView.fitsSystemWindows = false + + // Add a colored View behind the transparent status bar to match the drawer header color. + val drawerLinearLayout = + binding.drawerNestedScrollView.getChildAt(0) as android.widget.LinearLayout + val statusBarBackground = android.view.View(activity).apply { + setBackgroundColor( + androidx.core.content.ContextCompat.getColor( + activity, + R.color.component_color_shared_slide_drawer_open_status_bar_color + ) + ) + } + drawerLinearLayout.addView(statusBarBackground, 0) + ViewCompat.setOnApplyWindowInsetsListener(statusBarBackground) { view, insets -> + val systemBars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() + ) + view.layoutParams.height = systemBars.top + view.requestLayout() + insets + } + + // Handle bottom padding for the navigation bar. + ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, insets -> + val systemBarInsets = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() + ) + view.updatePadding(bottom = systemBarInsets.bottom) + insets + } + } + // TODO(#3382): Remove debug only code from prod build (also check imports, constructor and drawer_fragment.xml) private fun setIfDeveloperOptionsMenuItemListener() { developerOptionsStarter.asSet().forEach { starter -> @@ -409,11 +453,13 @@ class NavigationDrawerFragmentPresenter @Inject constructor( override fun onDrawerOpened(drawerView: View) { super.onDrawerOpened(drawerView) fragment.requireActivity().invalidateOptionsMenu() - StatusBarColor.statusBarColorUpdate( - R.color.component_color_shared_slide_drawer_open_status_bar_color, - activity, - false - ) + if (!enableEdgeToEdge.value) { + StatusBarColor.statusBarColorUpdate( + R.color.component_color_shared_slide_drawer_open_status_bar_color, + activity, + false + ) + } } override fun onDrawerClosed(drawerView: View) { @@ -421,11 +467,13 @@ class NavigationDrawerFragmentPresenter @Inject constructor( // It's possible in some rare cases for the activity to be gone while the drawer is // closing (possibly an out-of-lifecycle call from the AndroidX component). fragment.activity?.invalidateOptionsMenu() - StatusBarColor.statusBarColorUpdate( - R.color.component_color_shared_activity_status_bar_color, - activity, - false - ) + if (!enableEdgeToEdge.value) { + StatusBarColor.statusBarColorUpdate( + R.color.component_color_shared_activity_status_bar_color, + activity, + false + ) + } } } drawerLayout.addDrawerListener(drawerToggle) @@ -448,21 +496,25 @@ class NavigationDrawerFragmentPresenter @Inject constructor( override fun onDrawerOpened(drawerView: View) { super.onDrawerOpened(drawerView) fragment.requireActivity().invalidateOptionsMenu() - StatusBarColor.statusBarColorUpdate( - R.color.component_color_shared_slide_drawer_open_status_bar_color, - activity, - false - ) + if (!enableEdgeToEdge.value) { + StatusBarColor.statusBarColorUpdate( + R.color.component_color_shared_slide_drawer_open_status_bar_color, + activity, + false + ) + } } override fun onDrawerClosed(drawerView: View) { super.onDrawerClosed(drawerView) fragment.requireActivity().invalidateOptionsMenu() - StatusBarColor.statusBarColorUpdate( - R.color.component_color_shared_activity_status_bar_color, - activity, - false - ) + if (!enableEdgeToEdge.value) { + StatusBarColor.statusBarColorUpdate( + R.color.component_color_shared_activity_status_bar_color, + activity, + false + ) + } } } drawerLayout.addDrawerListener(drawerToggle) diff --git a/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt index 6428ada3b35..65b2096a9b0 100644 --- a/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt @@ -1,5 +1,6 @@ package org.oppia.android.app.help +import android.os.Build import android.os.Bundle import android.view.View import android.widget.FrameLayout @@ -7,6 +8,10 @@ import android.widget.ImageButton import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar +import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.Fragment import org.oppia.android.app.activity.ActivityScope @@ -24,13 +29,16 @@ import org.oppia.android.app.policies.PoliciesFragment import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R import org.oppia.android.util.extensions.putProto +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [HelpActivity]. */ @ActivityScope class HelpActivityPresenter @Inject constructor( private val activity: AppCompatActivity, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var navigationDrawerFragment: NavigationDrawerFragment private lateinit var toolbar: Toolbar @@ -61,6 +69,9 @@ class HelpActivityPresenter @Inject constructor( activity.setContentView(R.layout.help_activity) setUpToolbar() setUpNavigationDrawer() + if (enableEdgeToEdge.value) { + applyEdgeToEdgeInsets(R.id.help_activity_drawer_layout) + } } else { activity.setContentView(R.layout.help_without_drawer_activity) setUpToolbar() @@ -68,6 +79,9 @@ class HelpActivityPresenter @Inject constructor( toolbar.setNavigationOnClickListener { activity.finish() } + if (enableEdgeToEdge.value) { + applyEdgeToEdgeInsets(drawerLayoutId = null) + } } val titleTextView = activity.findViewById(R.id.options_activity_selected_options_title) @@ -354,4 +368,40 @@ class HelpActivityPresenter @Inject constructor( selectedFragmentTag = POLICIES_FRAGMENT_TAG selectedHelpOptionTitle = getMultipaneContainerTitle() } + + private fun applyEdgeToEdgeInsets(drawerLayoutId: Int?) { + val statusBarBackground = View(activity).apply { + setBackgroundColor( + ContextCompat.getColor( + activity, + R.color.component_color_shared_activity_status_bar_color + ) + ) + } + val contentLayout = toolbar.parent as android.widget.LinearLayout + contentLayout.addView(statusBarBackground, 0) + ViewCompat.setOnApplyWindowInsetsListener(statusBarBackground) { view, insets -> + val systemBars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or + WindowInsetsCompat.Type.displayCutout() + ) + view.layoutParams.height = systemBars.top + view.requestLayout() + insets + } + if (drawerLayoutId != null) { + val drawerLayout = activity.findViewById(drawerLayoutId) + ViewCompat.setOnApplyWindowInsetsListener(drawerLayout) { view, insets -> + val systemBars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or + WindowInsetsCompat.Type.displayCutout() + ) + view.updatePadding(bottom = systemBars.bottom) + insets + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + activity.window.isNavigationBarContrastEnforced = false + } + } } diff --git a/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt index 98455074306..482a4347c8d 100644 --- a/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt @@ -1,26 +1,38 @@ package org.oppia.android.app.home +import android.os.Build import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import androidx.drawerlayout.widget.DrawerLayout import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.drawer.NavigationDrawerFragment import org.oppia.android.app.spotlight.SpotlightFragment import org.oppia.android.app.spotlight.SpotlightManager import org.oppia.android.app.ui.R +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject const val TAG_HOME_FRAGMENT = "HOME_FRAGMENT" /** The presenter for [HomeActivity]. */ @ActivityScope -class HomeActivityPresenter @Inject constructor(private val activity: AppCompatActivity) { +class HomeActivityPresenter @Inject constructor( + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue +) { private var navigationDrawerFragment: NavigationDrawerFragment? = null fun handleOnCreate(internalProfileId: Int) { activity.setContentView(R.layout.home_activity) setUpNavigationDrawer() + if (enableEdgeToEdge.value) { + applyEdgeToEdgeInsets() + } if (getHomeFragment() == null) { activity.supportFragmentManager.beginTransaction().add( R.id.home_fragment_placeholder, @@ -62,4 +74,43 @@ class HomeActivityPresenter @Inject constructor(private val activity: AppCompatA R.id.home_spotlight_fragment_placeholder ) as? SpotlightFragment } + + private fun applyEdgeToEdgeInsets() { + val toolbar = activity.findViewById(R.id.home_activity_toolbar) + val contentLayout = toolbar.parent as android.widget.LinearLayout + + // Add a colored View behind the transparent status bar to restore the darker status bar color. + val statusBarBackground = View(activity).apply { + setBackgroundColor( + androidx.core.content.ContextCompat.getColor( + activity, + R.color.component_color_shared_activity_status_bar_color + ) + ) + } + contentLayout.addView(statusBarBackground, 0) + ViewCompat.setOnApplyWindowInsetsListener(statusBarBackground) { view, insets -> + val systemBars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() + ) + view.layoutParams.height = systemBars.top + view.requestLayout() + insets + } + + val drawerLayout = + activity.findViewById(R.id.home_activity_drawer_layout) + ViewCompat.setOnApplyWindowInsetsListener(drawerLayout) { view, insets -> + val systemBarInsets = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() + ) + view.updatePadding(bottom = systemBarInsets.bottom) + insets + } + + // Make the navigation bar fully transparent instead of translucent. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + activity.window.isNavigationBarContrastEnforced = false + } + } } diff --git a/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt index cbd6515d5a9..cf2d9c3b90f 100644 --- a/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt @@ -1,10 +1,15 @@ package org.oppia.android.app.options +import android.os.Build import android.view.View import android.widget.FrameLayout import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar +import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import androidx.drawerlayout.widget.DrawerLayout import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.drawer.NavigationDrawerFragment @@ -14,12 +19,15 @@ import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.model.OppiaLanguage import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.ui.R +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [OptionsActivity]. */ @ActivityScope class OptionsActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private var navigationDrawerFragment: NavigationDrawerFragment? = null private lateinit var toolbar: Toolbar @@ -37,6 +45,9 @@ class OptionsActivityPresenter @Inject constructor( activity.setContentView(R.layout.option_activity) setUpToolbar() setUpNavigationDrawer() + if (enableEdgeToEdge.value) { + applyEdgeToEdgeInsets(R.id.options_activity_drawer_layout) + } } else { activity.setContentView(R.layout.options_without_drawer_activity) setUpToolbar() @@ -44,6 +55,9 @@ class OptionsActivityPresenter @Inject constructor( toolbar.setNavigationOnClickListener { activity.finish() } + if (enableEdgeToEdge.value) { + applyEdgeToEdgeInsets(drawerLayoutId = null) + } } val titleTextView = activity.findViewById(R.id.options_activity_selected_options_title) @@ -154,4 +168,40 @@ class OptionsActivityPresenter @Inject constructor( fun setExtraOptionTitle(title: String) { activity.findViewById(R.id.options_activity_selected_options_title).text = title } + + private fun applyEdgeToEdgeInsets(drawerLayoutId: Int?) { + val statusBarBackground = View(activity).apply { + setBackgroundColor( + ContextCompat.getColor( + activity, + R.color.component_color_shared_activity_status_bar_color + ) + ) + } + val contentLayout = toolbar.parent as android.widget.LinearLayout + contentLayout.addView(statusBarBackground, 0) + ViewCompat.setOnApplyWindowInsetsListener(statusBarBackground) { view, insets -> + val systemBars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or + WindowInsetsCompat.Type.displayCutout() + ) + view.layoutParams.height = systemBars.top + view.requestLayout() + insets + } + if (drawerLayoutId != null) { + val drawerLayout = activity.findViewById(drawerLayoutId) + ViewCompat.setOnApplyWindowInsetsListener(drawerLayout) { view, insets -> + val systemBars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or + WindowInsetsCompat.Type.displayCutout() + ) + view.updatePadding(bottom = systemBars.bottom) + insets + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + activity.window.isNavigationBarContrastEnforced = false + } + } } From 259c63218fff0f50bbdd906b4305d67c2507afd1 Mon Sep 17 00:00:00 2001 From: Neer-rn Date: Tue, 14 Apr 2026 11:21:52 +0545 Subject: [PATCH 03/12] Fix #5940: Fix landscape drawer shade by overriding inner NavigationView insets listener --- .../app/drawer/NavigationDrawerFragmentPresenter.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt index 16179c74730..714c6400eaf 100644 --- a/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt @@ -130,6 +130,15 @@ class NavigationDrawerFragmentPresenter @Inject constructor( insets } + // Override the inner NavigationView's insets listener to prevent ScrimInsetsFrameLayout's + // constructor-set listener from storing insets and drawing the default #4000 scrim in + // system bar inset areas. Setting fitsSystemWindows=false alone is not sufficient because + // the OnApplyWindowInsetsListener set in ScrimInsetsFrameLayout's constructor is + // independent of the fitsSystemWindows flag. + ViewCompat.setOnApplyWindowInsetsListener(binding.fragmentDrawerNavView) { _, insets -> + insets + } + // Handle bottom padding for the navigation bar. ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, insets -> val systemBarInsets = insets.getInsets( From 4f5a95f75aab383d366ca5d9d3bcb04228ff7eb5 Mon Sep 17 00:00:00 2001 From: Neer-rn Date: Sat, 18 Apr 2026 09:24:41 +0545 Subject: [PATCH 04/12] Fix #5940: Fix landscape drawer padding by targeting contentLayout instead of DrawerLayout --- .../AdministratorControlsActivityPresenter.kt | 12 +++++--- .../ClassroomListActivityPresenter.kt | 13 +++++---- .../DeveloperOptionsActivityPresenter.kt | 12 +++++--- .../NavigationDrawerFragmentPresenter.kt | 11 +++++-- .../android/app/help/HelpActivityPresenter.kt | 29 ++++++++++--------- .../android/app/home/HomeActivityPresenter.kt | 12 +++++--- .../app/options/OptionsActivityPresenter.kt | 29 ++++++++++--------- 7 files changed, 73 insertions(+), 45 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt index 79a8439ca61..c6b82195fb0 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt @@ -231,14 +231,18 @@ class AdministratorControlsActivityPresenter @Inject constructor( view.requestLayout() insets } - ViewCompat.setOnApplyWindowInsetsListener( - binding.administratorControlsActivityDrawerLayout - ) { view, insets -> + // Apply padding to the content area instead of the DrawerLayout. Adding left/right + // padding to DrawerLayout shrinks the drawer panel and hides the toolbar hamburger icon. + ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> val systemBars = insets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() ) - view.updatePadding(bottom = systemBars.bottom) + view.updatePadding( + left = systemBars.left, + right = systemBars.right, + bottom = systemBars.bottom + ) insets } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { diff --git a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt index 7e05b4e459d..5e80ce13193 100644 --- a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt @@ -88,15 +88,18 @@ class ClassroomListActivityPresenter @Inject constructor( view.requestLayout() insets } - val drawerLayout = activity.findViewById( - R.id.classroom_list_activity_drawer_layout - ) - ViewCompat.setOnApplyWindowInsetsListener(drawerLayout) { view, insets -> + // Apply padding to the content area instead of the DrawerLayout. Adding left/right + // padding to DrawerLayout shrinks the drawer panel and hides the toolbar hamburger icon. + ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> val systemBars = insets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() ) - view.updatePadding(bottom = systemBars.bottom) + view.updatePadding( + left = systemBars.left, + right = systemBars.right, + bottom = systemBars.bottom + ) insets } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { diff --git a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt index aaee99a300d..ae51ab683ce 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt @@ -104,14 +104,18 @@ class DeveloperOptionsActivityPresenter @Inject constructor( view.requestLayout() insets } - ViewCompat.setOnApplyWindowInsetsListener( - binding.developerOptionsActivityDrawerLayout - ) { view, insets -> + // Apply padding to the content area instead of the DrawerLayout. Adding left/right + // padding to DrawerLayout shrinks the drawer panel and hides the toolbar hamburger icon. + ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> val systemBars = insets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() ) - view.updatePadding(bottom = systemBars.bottom) + view.updatePadding( + left = systemBars.left, + right = systemBars.right, + bottom = systemBars.bottom + ) insets } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { diff --git a/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt index 714c6400eaf..0c5768d3c61 100644 --- a/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt @@ -139,12 +139,19 @@ class NavigationDrawerFragmentPresenter @Inject constructor( insets } - // Handle bottom padding for the navigation bar. + // The drawer panel slides from the start edge and never reaches the end edge, so only + // apply start-side padding (for system bars or cutouts on the drawer's own edge) and + // bottom padding. Applying end-side padding would incorrectly shrink the drawer content. ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, insets -> val systemBarInsets = insets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() ) - view.updatePadding(bottom = systemBarInsets.bottom) + val isRtl = ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL + view.updatePadding( + left = if (isRtl) 0 else systemBarInsets.left, + right = if (isRtl) systemBarInsets.right else 0, + bottom = systemBarInsets.bottom + ) insets } } diff --git a/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt index 65b2096a9b0..cdc36768bf7 100644 --- a/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt @@ -70,7 +70,7 @@ class HelpActivityPresenter @Inject constructor( setUpToolbar() setUpNavigationDrawer() if (enableEdgeToEdge.value) { - applyEdgeToEdgeInsets(R.id.help_activity_drawer_layout) + applyEdgeToEdgeInsets() } } else { activity.setContentView(R.layout.help_without_drawer_activity) @@ -80,7 +80,7 @@ class HelpActivityPresenter @Inject constructor( activity.finish() } if (enableEdgeToEdge.value) { - applyEdgeToEdgeInsets(drawerLayoutId = null) + applyEdgeToEdgeInsets() } } val titleTextView = @@ -369,7 +369,7 @@ class HelpActivityPresenter @Inject constructor( selectedHelpOptionTitle = getMultipaneContainerTitle() } - private fun applyEdgeToEdgeInsets(drawerLayoutId: Int?) { + private fun applyEdgeToEdgeInsets() { val statusBarBackground = View(activity).apply { setBackgroundColor( ContextCompat.getColor( @@ -389,16 +389,19 @@ class HelpActivityPresenter @Inject constructor( view.requestLayout() insets } - if (drawerLayoutId != null) { - val drawerLayout = activity.findViewById(drawerLayoutId) - ViewCompat.setOnApplyWindowInsetsListener(drawerLayout) { view, insets -> - val systemBars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or - WindowInsetsCompat.Type.displayCutout() - ) - view.updatePadding(bottom = systemBars.bottom) - insets - } + // Apply padding to the content area instead of the DrawerLayout. Adding left/right + // padding to DrawerLayout shrinks the drawer panel and hides the toolbar hamburger icon. + ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> + val systemBars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or + WindowInsetsCompat.Type.displayCutout() + ) + view.updatePadding( + left = systemBars.left, + right = systemBars.right, + bottom = systemBars.bottom + ) + insets } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { activity.window.isNavigationBarContrastEnforced = false diff --git a/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt index 482a4347c8d..f7c13ec31c5 100644 --- a/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt @@ -98,13 +98,17 @@ class HomeActivityPresenter @Inject constructor( insets } - val drawerLayout = - activity.findViewById(R.id.home_activity_drawer_layout) - ViewCompat.setOnApplyWindowInsetsListener(drawerLayout) { view, insets -> + // Apply padding to the content area instead of the DrawerLayout. Adding left/right + // padding to DrawerLayout shrinks the drawer panel and hides the toolbar hamburger icon. + ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> val systemBarInsets = insets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() ) - view.updatePadding(bottom = systemBarInsets.bottom) + view.updatePadding( + left = systemBarInsets.left, + right = systemBarInsets.right, + bottom = systemBarInsets.bottom + ) insets } diff --git a/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt index cf2d9c3b90f..c6525e5b6d4 100644 --- a/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt @@ -46,7 +46,7 @@ class OptionsActivityPresenter @Inject constructor( setUpToolbar() setUpNavigationDrawer() if (enableEdgeToEdge.value) { - applyEdgeToEdgeInsets(R.id.options_activity_drawer_layout) + applyEdgeToEdgeInsets() } } else { activity.setContentView(R.layout.options_without_drawer_activity) @@ -56,7 +56,7 @@ class OptionsActivityPresenter @Inject constructor( activity.finish() } if (enableEdgeToEdge.value) { - applyEdgeToEdgeInsets(drawerLayoutId = null) + applyEdgeToEdgeInsets() } } val titleTextView = @@ -169,7 +169,7 @@ class OptionsActivityPresenter @Inject constructor( activity.findViewById(R.id.options_activity_selected_options_title).text = title } - private fun applyEdgeToEdgeInsets(drawerLayoutId: Int?) { + private fun applyEdgeToEdgeInsets() { val statusBarBackground = View(activity).apply { setBackgroundColor( ContextCompat.getColor( @@ -189,16 +189,19 @@ class OptionsActivityPresenter @Inject constructor( view.requestLayout() insets } - if (drawerLayoutId != null) { - val drawerLayout = activity.findViewById(drawerLayoutId) - ViewCompat.setOnApplyWindowInsetsListener(drawerLayout) { view, insets -> - val systemBars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or - WindowInsetsCompat.Type.displayCutout() - ) - view.updatePadding(bottom = systemBars.bottom) - insets - } + // Apply padding to the content area instead of the DrawerLayout. Adding left/right + // padding to DrawerLayout shrinks the drawer panel and hides the toolbar hamburger icon. + ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> + val systemBars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() or + WindowInsetsCompat.Type.displayCutout() + ) + view.updatePadding( + left = systemBars.left, + right = systemBars.right, + bottom = systemBars.bottom + ) + insets } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { activity.window.isNavigationBarContrastEnforced = false From d93cb5f8f7dc2bc7ec9ea89d0684f6c1532e0541 Mon Sep 17 00:00:00 2001 From: Neer-rn Date: Wed, 22 Apr 2026 11:15:43 +0545 Subject: [PATCH 05/12] Fix #5940: Extract edge-to-edge insets into EdgeToEdgeHelper --- .../AdministratorControlsActivityPresenter.kt | 55 +----- .../appversion/AppVersionActivityPresenter.kt | 23 ++- .../ProfileAndDeviceIdActivityPresenter.kt | 25 ++- .../ClassroomListActivityPresenter.kt | 62 ++---- .../DeveloperOptionsActivityPresenter.kt | 56 +----- .../NavigationDrawerFragmentPresenter.kt | 64 +------ .../android/app/help/HelpActivityPresenter.kt | 63 ++----- .../app/help/faq/FAQListActivityPresenter.kt | 17 +- .../faqsingle/FAQSingleActivityPresenter.kt | 17 +- .../LicenseListActivityPresenter.kt | 17 +- .../LicenseTextViewerActivityPresenter.kt | 17 +- ...irdPartyDependencyListActivityPresenter.kt | 17 +- .../android/app/home/HomeActivityPresenter.kt | 58 +----- .../RecentlyPlayedActivityPresenter.kt | 19 +- .../options/AppLanguageActivityPresenter.kt | 19 +- .../options/AudioLanguageActivityPresenter.kt | 20 +- .../app/options/OptionsActivityPresenter.kt | 61 ++---- .../ReadingTextSizeActivityPresenter.kt | 22 ++- .../ExplorationActivityPresenter.kt | 17 +- .../app/policies/PoliciesActivityPresenter.kt | 17 +- .../profile/AddProfileActivityPresenter.kt | 18 +- .../app/profile/AdminAuthActivityPresenter.kt | 17 ++ .../app/profile/AdminPinActivityPresenter.kt | 17 +- .../ProfilePictureActivityPresenter.kt | 32 +++- .../ProfileProgressActivityPresenter.kt | 16 +- .../ResumeLessonActivityPresenter.kt | 17 +- .../profile/ProfileEditActivityPresenter.kt | 16 +- .../profile/ProfileRenameActivityPresenter.kt | 21 ++- .../ProfileResetPinActivityPresenter.kt | 16 ++ .../QuestionPlayerActivityPresenter.kt | 17 +- .../RevisionCardActivityPresenter.kt | 15 ++ .../android/app/utility/EdgeToEdgeHelper.kt | 178 ++++++++++++++++++ 32 files changed, 666 insertions(+), 380 deletions(-) create mode 100644 app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt index c6b82195fb0..815c8ed55ee 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt @@ -1,13 +1,8 @@ package org.oppia.android.app.administratorcontrols -import android.os.Build import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.ContextCompat -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updatePadding import androidx.databinding.DataBindingUtil import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.administratorcontrols.appversion.AppVersionFragment @@ -21,6 +16,7 @@ import org.oppia.android.app.settings.profile.ProfileEditFragment import org.oppia.android.app.settings.profile.ProfileListFragment import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.util.extensions.putProto import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue @@ -50,13 +46,20 @@ class AdministratorControlsActivityPresenter @Inject constructor( selectedProfileId: LegacyProfileId, isProfileDeletionDialogVisible: Boolean ) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } binding = DataBindingUtil.setContentView( activity, R.layout.administrator_controls_activity ) setUpNavigationDrawer() if (enableEdgeToEdge.value) { - applyEdgeToEdgeInsets() + EdgeToEdgeHelper.applyToToolbarContainer( + activity, + binding.administratorControlsActivityToolbar, + R.color.component_color_shared_activity_status_bar_color + ) } this.lastLoadedFragment = lastLoadedFragment this.selectedProfileId = selectedProfileId @@ -210,46 +213,6 @@ class AdministratorControlsActivityPresenter @Inject constructor( binding.extraControlsTitle?.text = title } - private fun applyEdgeToEdgeInsets() { - val toolbar = binding.administratorControlsActivityToolbar - val contentLayout = toolbar.parent as android.widget.LinearLayout - val statusBarBackground = View(activity).apply { - setBackgroundColor( - ContextCompat.getColor( - activity, - R.color.component_color_shared_activity_status_bar_color - ) - ) - } - contentLayout.addView(statusBarBackground, 0) - ViewCompat.setOnApplyWindowInsetsListener(statusBarBackground) { view, insets -> - val systemBars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or - WindowInsetsCompat.Type.displayCutout() - ) - view.layoutParams.height = systemBars.top - view.requestLayout() - insets - } - // Apply padding to the content area instead of the DrawerLayout. Adding left/right - // padding to DrawerLayout shrinks the drawer panel and hides the toolbar hamburger icon. - ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> - val systemBars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or - WindowInsetsCompat.Type.displayCutout() - ) - view.updatePadding( - left = systemBars.left, - right = systemBars.right, - bottom = systemBars.bottom - ) - insets - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - activity.window.isNavigationBarContrastEnforced = false - } - } - /** Saves the state of the views on configuration changes. */ fun handleOnSaveInstanceState(outState: Bundle) { val titleTextView = binding.extraControlsTitle diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivityPresenter.kt index d57e9fb0656..5013a29b61c 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivityPresenter.kt @@ -4,16 +4,32 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [AppVersionActivity]. */ @ActivityScope -class AppVersionActivityPresenter @Inject constructor(private val activity: AppCompatActivity) { +class AppVersionActivityPresenter @Inject constructor( + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue +) { /** Initializes the [AppVersionActivity] views and binds [AppVersionFragment]. */ fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.app_version_activity) - setToolbar() + val toolbar = setToolbar() + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + toolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } if (getAppVersionFragment() == null) { activity.supportFragmentManager.beginTransaction().add( R.id.app_version_fragment_placeholder, @@ -22,10 +38,11 @@ class AppVersionActivityPresenter @Inject constructor(private val activity: AppC } } - private fun setToolbar() { + private fun setToolbar(): Toolbar { val appVersionToolbar: Toolbar = activity.findViewById(R.id.app_version_toolbar) as Toolbar activity.setSupportActionBar(appVersionToolbar) activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) + return appVersionToolbar } private fun getAppVersionFragment(): AppVersionFragment? { diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityPresenter.kt index c435f1bfb08..d2c975a86a2 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityPresenter.kt @@ -3,16 +3,30 @@ package org.oppia.android.app.administratorcontrols.learneranalytics import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for arranging the UI of [ProfileAndDeviceIdActivity]. */ class ProfileAndDeviceIdActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { /** Handles [ProfileAndDeviceIdActivity]'s creation flow. */ fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.profile_and_device_id_activity) - setToolbar() + val toolbar = setToolbar() + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + toolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } if (getProfileAndDeviceIdFragment() == null) { activity.supportFragmentManager.beginTransaction().add( R.id.profile_and_device_id_fragment_placeholder, @@ -21,10 +35,11 @@ class ProfileAndDeviceIdActivityPresenter @Inject constructor( } } - private fun setToolbar() { - val appVersionToolbar = activity.findViewById(R.id.profile_and_device_id_toolbar) as Toolbar - activity.setSupportActionBar(appVersionToolbar) + private fun setToolbar(): Toolbar { + val toolbar = activity.findViewById(R.id.profile_and_device_id_toolbar) as Toolbar + activity.setSupportActionBar(toolbar) activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) + return toolbar } private fun getProfileAndDeviceIdFragment(): ProfileAndDeviceIdFragment? { diff --git a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt index 5e80ce13193..95deaaf292f 100644 --- a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt @@ -1,16 +1,12 @@ package org.oppia.android.app.classroom -import android.os.Build import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar -import androidx.core.content.ContextCompat -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updatePadding import androidx.drawerlayout.widget.DrawerLayout import org.oppia.android.app.drawer.NavigationDrawerFragment import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject @@ -30,10 +26,17 @@ class ClassroomListActivityPresenter @Inject constructor( * and adds the [ClassroomListFragment] if it's not already added. */ fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.classroom_list_activity) - setUpNavigationDrawer() + val toolbar = setUpNavigationDrawer() if (enableEdgeToEdge.value) { - applyEdgeToEdgeInsets() + EdgeToEdgeHelper.applyToToolbarContainer( + activity, + toolbar, + R.color.component_color_shared_activity_status_bar_color + ) } if (getClassroomListFragment() == null) { activity.supportFragmentManager.beginTransaction().add( @@ -44,7 +47,7 @@ class ClassroomListActivityPresenter @Inject constructor( } } - private fun setUpNavigationDrawer() { + private fun setUpNavigationDrawer(): Toolbar { val toolbar = activity.findViewById(R.id.classroom_list_activity_toolbar) as Toolbar activity.setSupportActionBar(toolbar) activity.supportActionBar!!.setDisplayShowHomeEnabled(true) @@ -57,6 +60,7 @@ class ClassroomListActivityPresenter @Inject constructor( activity.findViewById(R.id.classroom_list_activity_drawer_layout) as DrawerLayout, toolbar, R.id.nav_home ) + return toolbar } private fun getClassroomListFragment(): ClassroomListFragment? { @@ -64,46 +68,4 @@ class ClassroomListActivityPresenter @Inject constructor( R.id.classroom_list_fragment_placeholder ) as ClassroomListFragment? } - - private fun applyEdgeToEdgeInsets() { - val toolbar = activity.findViewById( - R.id.classroom_list_activity_toolbar - ) - val contentLayout = toolbar.parent as android.widget.LinearLayout - val statusBarBackground = View(activity).apply { - setBackgroundColor( - ContextCompat.getColor( - activity, - R.color.component_color_shared_activity_status_bar_color - ) - ) - } - contentLayout.addView(statusBarBackground, 0) - ViewCompat.setOnApplyWindowInsetsListener(statusBarBackground) { view, insets -> - val systemBars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or - WindowInsetsCompat.Type.displayCutout() - ) - view.layoutParams.height = systemBars.top - view.requestLayout() - insets - } - // Apply padding to the content area instead of the DrawerLayout. Adding left/right - // padding to DrawerLayout shrinks the drawer panel and hides the toolbar hamburger icon. - ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> - val systemBars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or - WindowInsetsCompat.Type.displayCutout() - ) - view.updatePadding( - left = systemBars.left, - right = systemBars.right, - bottom = systemBars.bottom - ) - insets - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - activity.window.isNavigationBarContrastEnforced = false - } - } } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt index ae51ab683ce..92d37d3a315 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt @@ -1,19 +1,14 @@ package org.oppia.android.app.devoptions import android.content.Intent -import android.os.Build -import android.view.View import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.ContextCompat -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updatePadding import androidx.databinding.DataBindingUtil import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.DeveloperOptionsActivityBinding import org.oppia.android.app.drawer.NavigationDrawerFragment import org.oppia.android.app.splash.SplashActivity import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject @@ -32,13 +27,20 @@ class DeveloperOptionsActivityPresenter @Inject constructor( private lateinit var binding: DeveloperOptionsActivityBinding fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } binding = DataBindingUtil.setContentView( activity, R.layout.developer_options_activity ) setUpNavigationDrawer() if (enableEdgeToEdge.value) { - applyEdgeToEdgeInsets() + EdgeToEdgeHelper.applyToToolbarContainer( + activity, + binding.developerOptionsActivityToolbar, + R.color.component_color_shared_activity_status_bar_color + ) } val previousFragment = getDeveloperOptionsFragment() if (previousFragment == null) { @@ -83,46 +85,6 @@ class DeveloperOptionsActivityPresenter @Inject constructor( dialog.showNow(activity.supportFragmentManager, TAG_FORCE_DOWNLOAD_DIALOG) } - private fun applyEdgeToEdgeInsets() { - val toolbar = binding.developerOptionsActivityToolbar - val contentLayout = toolbar.parent as android.widget.LinearLayout - val statusBarBackground = View(activity).apply { - setBackgroundColor( - ContextCompat.getColor( - activity, - R.color.component_color_shared_activity_status_bar_color - ) - ) - } - contentLayout.addView(statusBarBackground, 0) - ViewCompat.setOnApplyWindowInsetsListener(statusBarBackground) { view, insets -> - val systemBars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or - WindowInsetsCompat.Type.displayCutout() - ) - view.layoutParams.height = systemBars.top - view.requestLayout() - insets - } - // Apply padding to the content area instead of the DrawerLayout. Adding left/right - // padding to DrawerLayout shrinks the drawer panel and hides the toolbar hamburger icon. - ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> - val systemBars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or - WindowInsetsCompat.Type.displayCutout() - ) - view.updatePadding( - left = systemBars.left, - right = systemBars.right, - bottom = systemBars.bottom - ) - insets - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - activity.window.isNavigationBarContrastEnforced = false - } - } - /** Called when restart is triggered by [ForceDownloadRemoteParametersDialogFragment]. */ fun restartApp() { val intent = Intent(activity, SplashActivity::class.java).also { diff --git a/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt index 0c5768d3c61..2d341f6cd87 100644 --- a/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt @@ -4,13 +4,11 @@ import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup +import android.widget.LinearLayout import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat import androidx.core.view.forEach -import androidx.core.view.updatePadding import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.Fragment import androidx.lifecycle.LiveData @@ -36,6 +34,7 @@ import org.oppia.android.app.options.OptionsActivity import org.oppia.android.app.profileprogress.ProfileProgressActivity import org.oppia.android.app.topic.TopicActivity import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.domain.topic.TopicController @@ -99,61 +98,16 @@ class NavigationDrawerFragmentPresenter @Inject constructor( setIfDeveloperOptionsMenuItemListener() if (enableEdgeToEdge.value) { - applyEdgeToEdgeInsetsToDrawer() - } - - return binding.root - } - - private fun applyEdgeToEdgeInsetsToDrawer() { - // Disable fitsSystemWindows on the inner NavigationView since we handle insets manually. - binding.fragmentDrawerNavView.fitsSystemWindows = false - - // Add a colored View behind the transparent status bar to match the drawer header color. - val drawerLinearLayout = - binding.drawerNestedScrollView.getChildAt(0) as android.widget.LinearLayout - val statusBarBackground = android.view.View(activity).apply { - setBackgroundColor( - androidx.core.content.ContextCompat.getColor( - activity, - R.color.component_color_shared_slide_drawer_open_status_bar_color - ) - ) - } - drawerLinearLayout.addView(statusBarBackground, 0) - ViewCompat.setOnApplyWindowInsetsListener(statusBarBackground) { view, insets -> - val systemBars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() + EdgeToEdgeHelper.applyToNavigationDrawer( + activity = activity, + drawerRoot = binding.root, + drawerContentLayout = binding.drawerNestedScrollView.getChildAt(0) as LinearLayout, + navigationView = binding.fragmentDrawerNavView, + statusBarColorRes = R.color.component_color_shared_slide_drawer_open_status_bar_color ) - view.layoutParams.height = systemBars.top - view.requestLayout() - insets - } - - // Override the inner NavigationView's insets listener to prevent ScrimInsetsFrameLayout's - // constructor-set listener from storing insets and drawing the default #4000 scrim in - // system bar inset areas. Setting fitsSystemWindows=false alone is not sufficient because - // the OnApplyWindowInsetsListener set in ScrimInsetsFrameLayout's constructor is - // independent of the fitsSystemWindows flag. - ViewCompat.setOnApplyWindowInsetsListener(binding.fragmentDrawerNavView) { _, insets -> - insets } - // The drawer panel slides from the start edge and never reaches the end edge, so only - // apply start-side padding (for system bars or cutouts on the drawer's own edge) and - // bottom padding. Applying end-side padding would incorrectly shrink the drawer content. - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, insets -> - val systemBarInsets = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() - ) - val isRtl = ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL - view.updatePadding( - left = if (isRtl) 0 else systemBarInsets.left, - right = if (isRtl) systemBarInsets.right else 0, - bottom = systemBarInsets.bottom - ) - insets - } + return binding.root } // TODO(#3382): Remove debug only code from prod build (also check imports, constructor and drawer_fragment.xml) diff --git a/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt index cdc36768bf7..5484fe45b63 100644 --- a/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt @@ -1,6 +1,5 @@ package org.oppia.android.app.help -import android.os.Build import android.os.Bundle import android.view.View import android.widget.FrameLayout @@ -8,10 +7,6 @@ import android.widget.ImageButton import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar -import androidx.core.content.ContextCompat -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updatePadding import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.Fragment import org.oppia.android.app.activity.ActivityScope @@ -28,6 +23,7 @@ import org.oppia.android.app.model.PolicyPage import org.oppia.android.app.policies.PoliciesFragment import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.util.extensions.putProto import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue @@ -65,13 +61,14 @@ class HelpActivityPresenter @Inject constructor( internalPolicyPage = policiesActivityParams.policyPage } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } + if (isFromNavigationDrawer) { activity.setContentView(R.layout.help_activity) setUpToolbar() setUpNavigationDrawer() - if (enableEdgeToEdge.value) { - applyEdgeToEdgeInsets() - } } else { activity.setContentView(R.layout.help_without_drawer_activity) setUpToolbar() @@ -79,9 +76,14 @@ class HelpActivityPresenter @Inject constructor( toolbar.setNavigationOnClickListener { activity.finish() } - if (enableEdgeToEdge.value) { - applyEdgeToEdgeInsets() - } + } + + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToToolbarContainer( + activity, + toolbar, + R.color.component_color_shared_activity_status_bar_color + ) } val titleTextView = activity.findViewById(R.id.options_activity_selected_options_title) @@ -368,43 +370,4 @@ class HelpActivityPresenter @Inject constructor( selectedFragmentTag = POLICIES_FRAGMENT_TAG selectedHelpOptionTitle = getMultipaneContainerTitle() } - - private fun applyEdgeToEdgeInsets() { - val statusBarBackground = View(activity).apply { - setBackgroundColor( - ContextCompat.getColor( - activity, - R.color.component_color_shared_activity_status_bar_color - ) - ) - } - val contentLayout = toolbar.parent as android.widget.LinearLayout - contentLayout.addView(statusBarBackground, 0) - ViewCompat.setOnApplyWindowInsetsListener(statusBarBackground) { view, insets -> - val systemBars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or - WindowInsetsCompat.Type.displayCutout() - ) - view.layoutParams.height = systemBars.top - view.requestLayout() - insets - } - // Apply padding to the content area instead of the DrawerLayout. Adding left/right - // padding to DrawerLayout shrinks the drawer panel and hides the toolbar hamburger icon. - ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> - val systemBars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or - WindowInsetsCompat.Type.displayCutout() - ) - view.updatePadding( - left = systemBars.left, - right = systemBars.right, - bottom = systemBars.bottom - ) - insets - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - activity.window.isNavigationBarContrastEnforced = false - } - } } diff --git a/app/src/main/java/org/oppia/android/app/help/faq/FAQListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/faq/FAQListActivityPresenter.kt index 19f0f43056f..34e6bf136f8 100644 --- a/app/src/main/java/org/oppia/android/app/help/faq/FAQListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/faq/FAQListActivityPresenter.kt @@ -7,17 +7,24 @@ import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.FaqListActivityBinding import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [FAQListActivity]. */ @ActivityScope class FAQListActivityPresenter @Inject constructor( private val activity: AppCompatActivity, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var faqListActivityToolbar: Toolbar fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } val binding = DataBindingUtil.setContentView(activity, R.layout.faq_list_activity) binding.apply { @@ -34,6 +41,14 @@ class FAQListActivityPresenter @Inject constructor( (activity as FAQListActivity).finish() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + faqListActivityToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + if (getFAQListFragment() == null) { activity.supportFragmentManager.beginTransaction().add( R.id.faq_list_fragment_placeholder, diff --git a/app/src/main/java/org/oppia/android/app/help/faq/faqsingle/FAQSingleActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/faq/faqsingle/FAQSingleActivityPresenter.kt index 74087fcd225..1b98bad88b3 100644 --- a/app/src/main/java/org/oppia/android/app/help/faq/faqsingle/FAQSingleActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/faq/faqsingle/FAQSingleActivityPresenter.kt @@ -8,8 +8,11 @@ import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.FaqSingleActivityBinding import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.util.gcsresource.DefaultResourceBucketName import org.oppia.android.util.parser.html.HtmlParser +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [FAQSingleActivity]. */ @@ -18,12 +21,17 @@ class FAQSingleActivityPresenter @Inject constructor( private val activity: AppCompatActivity, private val htmlParserFactory: HtmlParser.Factory, @DefaultResourceBucketName private val resourceBucketName: String, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var faqSingleActivityToolbar: Toolbar fun handleOnCreate(question: String, answer: String) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } val binding = DataBindingUtil.setContentView( activity, R.layout.faq_single_activity @@ -41,6 +49,13 @@ class FAQSingleActivityPresenter @Inject constructor( binding.faqSingleActivityToolbar.setNavigationOnClickListener { (activity as FAQSingleActivity).finish() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + faqSingleActivityToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } activity.findViewById(R.id.faq_question_text_view).text = question // NOTE: Here entityType and entityId can be anything as it will actually not get used. diff --git a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListActivityPresenter.kt index 203da25e090..c2df9af0f29 100644 --- a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListActivityPresenter.kt @@ -6,17 +6,25 @@ import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.LicenseListActivityBinding import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [LicenseListActivity]. */ @ActivityScope class LicenseListActivityPresenter @Inject constructor( private val activity: AppCompatActivity, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { /** Handles onCreate() method of the [LicenseListActivity]. */ fun handleOnCreate(dependencyIndex: Int, isMultipane: Boolean) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } val binding = DataBindingUtil.setContentView( activity, @@ -36,6 +44,13 @@ class LicenseListActivityPresenter @Inject constructor( binding.licenseListActivityToolbar.setNavigationOnClickListener { (activity as LicenseListActivity).finish() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + licenseListActivityToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } if (getLicenseListFragment() == null) { val licenseListFragment = LicenseListFragment.newInstance(dependencyIndex, isMultipane) activity.supportFragmentManager.beginTransaction() diff --git a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerActivityPresenter.kt index 2148cfcc9a7..8e92b7f10ff 100644 --- a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerActivityPresenter.kt @@ -6,19 +6,27 @@ import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.LicenseTextViewerActivityBinding import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.util.accessibility.AccessibilityService +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [LicenseTextViewerActivity]. */ @ActivityScope class LicenseTextViewerActivityPresenter @Inject constructor( private val activity: AppCompatActivity, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { @Inject lateinit var accessibilityService: AccessibilityService /** Handles onCreate() method of the [LicenseTextViewerActivity]. */ fun handleOnCreate(dependencyIndex: Int, licenseIndex: Int) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } val binding = DataBindingUtil.setContentView( activity, @@ -45,6 +53,13 @@ class LicenseTextViewerActivityPresenter @Inject constructor( binding.licenseTextViewerActivityToolbar.setNavigationOnClickListener { (activity as LicenseTextViewerActivity).finish() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + licenseTextViewerActivityToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } if (!accessibilityService.isScreenReaderEnabled()) { binding.licenseTextViewerActivityToolbarTitle.setOnClickListener { diff --git a/app/src/main/java/org/oppia/android/app/help/thirdparty/ThirdPartyDependencyListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/thirdparty/ThirdPartyDependencyListActivityPresenter.kt index 38605a1c859..c6b0759565d 100644 --- a/app/src/main/java/org/oppia/android/app/help/thirdparty/ThirdPartyDependencyListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/thirdparty/ThirdPartyDependencyListActivityPresenter.kt @@ -6,17 +6,25 @@ import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.ThirdPartyDependencyListActivityBinding import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [ThirdPartyDependencyListActivity]. */ @ActivityScope class ThirdPartyDependencyListActivityPresenter @Inject constructor( private val activity: AppCompatActivity, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { /** Handles onCreate() method of the [ThirdPartyDependencyListActivity]. */ fun handleOnCreate(isMultipane: Boolean) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } val binding = DataBindingUtil.setContentView( activity, @@ -37,6 +45,13 @@ class ThirdPartyDependencyListActivityPresenter @Inject constructor( binding.thirdPartyDependencyListActivityToolbar.setNavigationOnClickListener { (activity as ThirdPartyDependencyListActivity).finish() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + thirdPartyDependencyListActivityToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } if (getThirdPartyDependencyListFragment() == null) { val thirdPartyDependencyListFragment = diff --git a/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt index f7c13ec31c5..37e0bbcd55e 100644 --- a/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt @@ -1,18 +1,15 @@ package org.oppia.android.app.home -import android.os.Build import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updatePadding import androidx.drawerlayout.widget.DrawerLayout import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.drawer.NavigationDrawerFragment import org.oppia.android.app.spotlight.SpotlightFragment import org.oppia.android.app.spotlight.SpotlightManager import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject @@ -28,10 +25,18 @@ class HomeActivityPresenter @Inject constructor( private var navigationDrawerFragment: NavigationDrawerFragment? = null fun handleOnCreate(internalProfileId: Int) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.home_activity) setUpNavigationDrawer() if (enableEdgeToEdge.value) { - applyEdgeToEdgeInsets() + val toolbar = activity.findViewById(R.id.home_activity_toolbar) + EdgeToEdgeHelper.applyToToolbarContainer( + activity, + toolbar, + R.color.component_color_shared_activity_status_bar_color + ) } if (getHomeFragment() == null) { activity.supportFragmentManager.beginTransaction().add( @@ -74,47 +79,4 @@ class HomeActivityPresenter @Inject constructor( R.id.home_spotlight_fragment_placeholder ) as? SpotlightFragment } - - private fun applyEdgeToEdgeInsets() { - val toolbar = activity.findViewById(R.id.home_activity_toolbar) - val contentLayout = toolbar.parent as android.widget.LinearLayout - - // Add a colored View behind the transparent status bar to restore the darker status bar color. - val statusBarBackground = View(activity).apply { - setBackgroundColor( - androidx.core.content.ContextCompat.getColor( - activity, - R.color.component_color_shared_activity_status_bar_color - ) - ) - } - contentLayout.addView(statusBarBackground, 0) - ViewCompat.setOnApplyWindowInsetsListener(statusBarBackground) { view, insets -> - val systemBars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() - ) - view.layoutParams.height = systemBars.top - view.requestLayout() - insets - } - - // Apply padding to the content area instead of the DrawerLayout. Adding left/right - // padding to DrawerLayout shrinks the drawer panel and hides the toolbar hamburger icon. - ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> - val systemBarInsets = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() - ) - view.updatePadding( - left = systemBarInsets.left, - right = systemBarInsets.right, - bottom = systemBarInsets.bottom - ) - insets - } - - // Make the navigation bar fully transparent instead of translucent. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - activity.window.isNavigationBarContrastEnforced = false - } - } } diff --git a/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedActivityPresenter.kt index b99d5d0e33e..9537f787646 100644 --- a/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedActivityPresenter.kt @@ -8,18 +8,26 @@ import org.oppia.android.app.model.RecentlyPlayedActivityParams import org.oppia.android.app.model.RecentlyPlayedActivityTitle import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [RecentlyPlayedActivity]. */ @ActivityScope class RecentlyPlayedActivityPresenter @Inject constructor( private val activity: AppCompatActivity, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { fun handleOnCreate(recentlyPlayedActivityParams: RecentlyPlayedActivityParams) { activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) activity.title = getTitle(recentlyPlayedActivityParams) + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } val binding = DataBindingUtil.setContentView( activity, R.layout.recently_played_activity @@ -29,6 +37,15 @@ class RecentlyPlayedActivityPresenter @Inject constructor( (activity as RecentlyPlayedActivity).finish() } binding.recentlyPlayedToolbar.title = getTitle(recentlyPlayedActivityParams) + + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.recentlyPlayedToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + if (getRecentlyPlayedFragment() == null) { activity.supportFragmentManager.beginTransaction().add( R.id.recently_played_fragment_placeholder, diff --git a/app/src/main/java/org/oppia/android/app/options/AppLanguageActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/options/AppLanguageActivityPresenter.kt index 8c166b4aeb2..192c6cbdd7e 100644 --- a/app/src/main/java/org/oppia/android/app/options/AppLanguageActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/AppLanguageActivityPresenter.kt @@ -6,20 +6,37 @@ import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.AppLanguageActivityBinding import org.oppia.android.app.model.OppiaLanguage import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [AppLanguageActivity]. */ @ActivityScope -class AppLanguageActivityPresenter @Inject constructor(private val activity: AppCompatActivity) { +class AppLanguageActivityPresenter @Inject constructor( + private val activity: AppCompatActivity, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue +) { private lateinit var oppiaLanguage: OppiaLanguage /** Initializes and creates the views for [AppLanguageActivity]. */ fun handleOnCreate(oppiaLanguage: OppiaLanguage, profileId: Int) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } val binding: AppLanguageActivityBinding = DataBindingUtil.setContentView( activity, R.layout.app_language_activity, ) binding.appLanguageToolbar.setNavigationOnClickListener { activity.finish() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.appLanguageToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } setLanguageSelected(oppiaLanguage) if (getAppLanguageFragment() == null) { val appLanguageFragment = AppLanguageFragment.newInstance(oppiaLanguage, profileId) diff --git a/app/src/main/java/org/oppia/android/app/options/AudioLanguageActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/options/AudioLanguageActivityPresenter.kt index 2cccdedb196..d5b983996ac 100644 --- a/app/src/main/java/org/oppia/android/app/options/AudioLanguageActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/AudioLanguageActivityPresenter.kt @@ -11,12 +11,19 @@ import org.oppia.android.app.model.AudioLanguageActivityParams import org.oppia.android.app.model.AudioLanguageActivityResultBundle import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.util.extensions.putProtoExtra +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [AudioLanguageActivity]. */ @ActivityScope -class AudioLanguageActivityPresenter @Inject constructor(private val activity: AppCompatActivity) { +class AudioLanguageActivityPresenter @Inject constructor( + private val activity: AppCompatActivity, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue +) { private lateinit var audioLanguage: AudioLanguage /** Handles when the activity is first created. */ @@ -27,12 +34,23 @@ class AudioLanguageActivityPresenter @Inject constructor(private val activity: A ) { this.audioLanguage = audioLanguage + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } val binding: AudioLanguageActivityBinding = DataBindingUtil.setContentView(activity, R.layout.audio_language_activity) binding.audioLanguageToolbar.setNavigationOnClickListener { finishWithResult() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.audioLanguageToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + if (getAudioLanguageFragment() == null) { val audioLanguageFragment = AudioLanguageFragment.newInstance( audioLanguage, diff --git a/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt index c6525e5b6d4..8cc1b20ff98 100644 --- a/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt @@ -1,15 +1,10 @@ package org.oppia.android.app.options -import android.os.Build import android.view.View import android.widget.FrameLayout import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar -import androidx.core.content.ContextCompat -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updatePadding import androidx.drawerlayout.widget.DrawerLayout import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.drawer.NavigationDrawerFragment @@ -19,6 +14,7 @@ import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.model.OppiaLanguage import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject @@ -41,13 +37,13 @@ class OptionsActivityPresenter @Inject constructor( selectedFragment: String, profileId: Int ) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } if (isFromNavigationDrawer) { activity.setContentView(R.layout.option_activity) setUpToolbar() setUpNavigationDrawer() - if (enableEdgeToEdge.value) { - applyEdgeToEdgeInsets() - } } else { activity.setContentView(R.layout.options_without_drawer_activity) setUpToolbar() @@ -55,9 +51,13 @@ class OptionsActivityPresenter @Inject constructor( toolbar.setNavigationOnClickListener { activity.finish() } - if (enableEdgeToEdge.value) { - applyEdgeToEdgeInsets() - } + } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToToolbarContainer( + activity, + toolbar, + R.color.component_color_shared_activity_status_bar_color + ) } val titleTextView = activity.findViewById(R.id.options_activity_selected_options_title) @@ -168,43 +168,4 @@ class OptionsActivityPresenter @Inject constructor( fun setExtraOptionTitle(title: String) { activity.findViewById(R.id.options_activity_selected_options_title).text = title } - - private fun applyEdgeToEdgeInsets() { - val statusBarBackground = View(activity).apply { - setBackgroundColor( - ContextCompat.getColor( - activity, - R.color.component_color_shared_activity_status_bar_color - ) - ) - } - val contentLayout = toolbar.parent as android.widget.LinearLayout - contentLayout.addView(statusBarBackground, 0) - ViewCompat.setOnApplyWindowInsetsListener(statusBarBackground) { view, insets -> - val systemBars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or - WindowInsetsCompat.Type.displayCutout() - ) - view.layoutParams.height = systemBars.top - view.requestLayout() - insets - } - // Apply padding to the content area instead of the DrawerLayout. Adding left/right - // padding to DrawerLayout shrinks the drawer panel and hides the toolbar hamburger icon. - ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> - val systemBars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() or - WindowInsetsCompat.Type.displayCutout() - ) - view.updatePadding( - left = systemBars.left, - right = systemBars.right, - bottom = systemBars.bottom - ) - insets - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - activity.window.isNavigationBarContrastEnforced = false - } - } } diff --git a/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivityPresenter.kt index 3b50a987ef2..1123185cb8e 100644 --- a/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivityPresenter.kt @@ -5,18 +5,33 @@ import androidx.appcompat.widget.Toolbar import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [ReadingTextSizeActivity]. */ @ActivityScope class ReadingTextSizeActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var fontSize: ReadingTextSize fun handleOnCreate(preferredTextSize: ReadingTextSize) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.reading_text_size_activity) - setToolbar() + val toolbar = setToolbar() + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + toolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } fontSize = preferredTextSize if (getReadingTextSizeFragment() == null) { val readingTextSizeFragment = ReadingTextSizeFragment.newInstance(preferredTextSize) @@ -25,11 +40,12 @@ class ReadingTextSizeActivityPresenter @Inject constructor( } } - private fun setToolbar() { + private fun setToolbar(): Toolbar { val readingTextSizeToolbar: Toolbar = activity.findViewById(R.id.reading_text_size_toolbar) readingTextSizeToolbar.setNavigationOnClickListener { activity.onBackPressedDispatcher.onBackPressed() } + return readingTextSizeToolbar } fun setSelectedReadingTextSize(fontSize: ReadingTextSize) { diff --git a/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt index 2f11fa4580b..45a99a3dc49 100644 --- a/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt @@ -33,6 +33,7 @@ import org.oppia.android.app.survey.TAG_SURVEY_WELCOME_DIALOG import org.oppia.android.app.topic.TopicActivity import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.FontScaleConfigurationUtil import org.oppia.android.domain.exploration.ExplorationDataController import org.oppia.android.domain.oppialogger.OppiaLogger @@ -42,6 +43,8 @@ import org.oppia.android.domain.translation.TranslationController import org.oppia.android.util.accessibility.AccessibilityService import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject private const val TAG_UNSAVED_EXPLORATION_DIALOG = "UNSAVED_EXPLORATION_DIALOG" @@ -62,7 +65,9 @@ class ExplorationActivityPresenter @Inject constructor( private val oppiaLogger: OppiaLogger, private val learnerAnalyticsLogger: LearnerAnalyticsLogger, private val resourceHandler: AppLanguageResourceHandler, - private val surveyGatingController: SurveyGatingController + private val surveyGatingController: SurveyGatingController, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { @Inject lateinit var accessibilityService: AccessibilityService @@ -93,6 +98,9 @@ class ExplorationActivityPresenter @Inject constructor( parentScreen: ExplorationActivityParams.ParentScreen, isCheckpointingEnabled: Boolean ) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } binding = DataBindingUtil.setContentView( activity, R.layout.exploration_activity @@ -105,6 +113,13 @@ class ExplorationActivityPresenter @Inject constructor( explorationToolbar = binding.explorationToolbar explorationToolbarTitle = binding.explorationToolbarTitle activity.setSupportActionBar(explorationToolbar) + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + explorationToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } if (!accessibilityService.isScreenReaderEnabled()) { binding.explorationToolbarTitle.setOnClickListener { diff --git a/app/src/main/java/org/oppia/android/app/policies/PoliciesActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/policies/PoliciesActivityPresenter.kt index 74e6b999a52..fedd2260c84 100644 --- a/app/src/main/java/org/oppia/android/app/policies/PoliciesActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/policies/PoliciesActivityPresenter.kt @@ -8,16 +8,23 @@ import org.oppia.android.app.model.PoliciesFragmentArguments import org.oppia.android.app.model.PolicyPage import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [PoliciesActivity]. */ class PoliciesActivityPresenter @Inject constructor( private val activity: AppCompatActivity, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { /** Handles onCreate() method of the [PoliciesActivity]. */ fun handleOnCreate(policiesActivityParams: PoliciesActivityParams) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.policies_activity) val toolbar = setUpToolbar(policiesActivityParams.policyPage) activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true) @@ -26,6 +33,14 @@ class PoliciesActivityPresenter @Inject constructor( activity.finish() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + toolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + if (getPoliciesFragment() == null) { val policiesFragmentArguments = PoliciesFragmentArguments diff --git a/app/src/main/java/org/oppia/android/app/profile/AddProfileActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/profile/AddProfileActivityPresenter.kt index 7cfbd1abf60..65ea4955df0 100644 --- a/app/src/main/java/org/oppia/android/app/profile/AddProfileActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profile/AddProfileActivityPresenter.kt @@ -28,12 +28,14 @@ import org.oppia.android.app.model.AddProfileActivityParams import org.oppia.android.app.profile.AddProfileActivity.Companion.ADD_PROFILE_ACTIVITY_PARAMS_KEY import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.TextInputEditTextHelper.Companion.onTextChanged import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData import org.oppia.android.util.extensions.getProtoExtra import org.oppia.android.util.platformparameter.EnableDownloadsSupport +import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject @@ -44,7 +46,10 @@ class AddProfileActivityPresenter @Inject constructor( private val profileManagementController: ProfileManagementController, private val resourceHandler: AppLanguageResourceHandler, private val profileViewModel: AddProfileViewModel, - @EnableDownloadsSupport private val enableDownloadsSupport: PlatformParameterValue + @EnableDownloadsSupport + private val enableDownloadsSupport: PlatformParameterValue, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var uploadImageView: ImageView private var selectedImage: Uri? = null @@ -60,6 +65,9 @@ class AddProfileActivityPresenter @Inject constructor( lateinit var resultLauncher: ActivityResultLauncher fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } val binding = DataBindingUtil.setContentView( activity, R.layout.add_profile_activity @@ -91,6 +99,14 @@ class AddProfileActivityPresenter @Inject constructor( activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_close_white_24dp) activity.supportActionBar?.setHomeActionContentDescription(R.string.admin_auth_close) + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToToolbarContainer( + activity, + toolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + uploadImageView = binding.addProfileActivityUserImageView Glide.with(activity) .load(R.drawable.ic_default_avatar) diff --git a/app/src/main/java/org/oppia/android/app/profile/AdminAuthActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/profile/AdminAuthActivityPresenter.kt index e928bb4563f..cea1a7bd746 100644 --- a/app/src/main/java/org/oppia/android/app/profile/AdminAuthActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profile/AdminAuthActivityPresenter.kt @@ -12,11 +12,14 @@ import org.oppia.android.app.model.AdminAuthActivityParams import org.oppia.android.app.profile.AdminAuthActivity.Companion.ADMIN_AUTH_ACTIVITY_PARAMS_KEY import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.TextInputEditTextHelper.Companion.onTextChanged import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData import org.oppia.android.util.extensions.getProtoExtra +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.extractCurrentUserProfileId import javax.inject.Inject @@ -28,6 +31,8 @@ class AdminAuthActivityPresenter @Inject constructor( private val authViewModel: AdminAuthViewModel, private val resourceHandler: AppLanguageResourceHandler, private val profileManagementController: ProfileManagementController, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var binding: AdminAuthActivityBinding private val args by lazy { @@ -39,6 +44,9 @@ class AdminAuthActivityPresenter @Inject constructor( /** Binds ViewModel and sets up text and button listeners. */ fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } binding = DataBindingUtil.setContentView( activity, R.layout.admin_auth_activity @@ -46,6 +54,15 @@ class AdminAuthActivityPresenter @Inject constructor( binding.adminAuthToolbar.setNavigationOnClickListener { (activity as AdminAuthActivity).finish() } + + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.adminAuthToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + val adminPin = checkNotNull(args?.adminPin) { "Expected AdminAuthActivity.admin_auth_admin_pin to be in intent extras." } diff --git a/app/src/main/java/org/oppia/android/app/profile/AdminPinActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/profile/AdminPinActivityPresenter.kt index f4b659e56f2..35fd0c82f67 100644 --- a/app/src/main/java/org/oppia/android/app/profile/AdminPinActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profile/AdminPinActivityPresenter.kt @@ -14,11 +14,14 @@ import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.profile.AdminPinActivity.Companion.ADMIN_PIN_ACTIVITY_PARAMS_KEY import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.TextInputEditTextHelper.Companion.onTextChanged import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData import org.oppia.android.util.extensions.getProtoExtra +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [AdminPinActivity]. */ @@ -28,7 +31,9 @@ class AdminPinActivityPresenter @Inject constructor( private val activity: AppCompatActivity, private val profileManagementController: ProfileManagementController, private val adminViewModel: AdminPinViewModel, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { private var inputtedPin = false @@ -43,10 +48,20 @@ class AdminPinActivityPresenter @Inject constructor( /** Binds ViewModel and sets up text and button listeners. */ fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } val binding = DataBindingUtil.setContentView(activity, R.layout.admin_pin_activity) activity.setSupportActionBar(binding.adminPinToolbar) + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToToolbarContainer( + activity, + binding.adminPinToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_close_white_24dp) activity.supportActionBar?.setHomeActionContentDescription(R.string.admin_auth_close) diff --git a/app/src/main/java/org/oppia/android/app/profileprogress/ProfilePictureActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/profileprogress/ProfilePictureActivityPresenter.kt index 463716051da..a4237437afa 100644 --- a/app/src/main/java/org/oppia/android/app/profileprogress/ProfilePictureActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profileprogress/ProfilePictureActivityPresenter.kt @@ -13,10 +13,13 @@ import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.model.Profile import org.oppia.android.app.model.ProfileAvatar import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.statusbar.StatusBarColor import javax.inject.Inject @@ -25,15 +28,24 @@ import javax.inject.Inject class ProfilePictureActivityPresenter @Inject constructor( private val activity: AppCompatActivity, private val profileManagementController: ProfileManagementController, - private val oppiaLogger: OppiaLogger + private val oppiaLogger: OppiaLogger, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var profilePictureActivityViewModel: ProfilePictureActivityViewModel private lateinit var profileId: LegacyProfileId fun handleOnCreate(internalProfileId: Int) { - StatusBarColor.statusBarColorUpdate( - R.color.component_color_shared_profile_status_bar_color, activity, false - ) + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } else { + StatusBarColor.statusBarColorUpdate( + R.color + .component_color_shared_profile_status_bar_color, + activity, + false + ) + } val binding = DataBindingUtil .setContentView( activity, @@ -48,10 +60,17 @@ class ProfilePictureActivityPresenter @Inject constructor( profileId = LegacyProfileId.newBuilder().setInternalId(internalProfileId).build() subscribeToProfileLiveData() - setUpToolbar() + val toolbar = setUpToolbar() + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + toolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } } - private fun setUpToolbar() { + private fun setUpToolbar(): Toolbar { val toolbar = activity.findViewById( R.id.profile_picture_activity_toolbar ) as Toolbar @@ -59,6 +78,7 @@ class ProfilePictureActivityPresenter @Inject constructor( toolbar.setNavigationOnClickListener { activity.finish() } + return toolbar } private val profileLiveData: LiveData by lazy { diff --git a/app/src/main/java/org/oppia/android/app/profileprogress/ProfileProgressActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/profileprogress/ProfileProgressActivityPresenter.kt index 618d301dd74..8ce059a4b08 100644 --- a/app/src/main/java/org/oppia/android/app/profileprogress/ProfileProgressActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profileprogress/ProfileProgressActivityPresenter.kt @@ -7,19 +7,26 @@ import androidx.appcompat.widget.Toolbar import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.domain.profile.ProfileManagementController +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [ProfileProgressActivity]. */ @ActivityScope class ProfileProgressActivityPresenter @Inject constructor( private val activity: AppCompatActivity, - private val profileManagementController: ProfileManagementController + private val profileManagementController: ProfileManagementController, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var profileId: LegacyProfileId fun handleOnCreate(internalProfileId: Int) { profileId = LegacyProfileId.newBuilder().setInternalId(internalProfileId).build() + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.profile_progress_activity) if (getProfileProgressFragment() == null) { activity.supportFragmentManager.beginTransaction().add( @@ -41,6 +48,13 @@ class ProfileProgressActivityPresenter @Inject constructor( toolbar.setNavigationOnClickListener { activity.finish() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToToolbarContainer( + activity, + toolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } } private fun getProfileProgressFragment(): ProfileProgressFragment? { diff --git a/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivityPresenter.kt index 74aa09c82c1..93bdac8af5d 100644 --- a/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivityPresenter.kt @@ -13,11 +13,14 @@ import org.oppia.android.app.model.Profile import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.player.exploration.DefaultFontSizeStateListener import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.FontScaleConfigurationUtil import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject private const val RESUME_LESSON_TAG = "ResumeLesson" @@ -27,7 +30,9 @@ class ResumeLessonActivityPresenter @Inject constructor( private val activity: AppCompatActivity, private val profileManagementController: ProfileManagementController, private val fontScaleConfigurationUtil: FontScaleConfigurationUtil, - private val oppiaLogger: OppiaLogger + private val oppiaLogger: OppiaLogger, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var profileId: LegacyProfileId private lateinit var classroomId: String @@ -47,6 +52,9 @@ class ResumeLessonActivityPresenter @Inject constructor( parentScreen: ExplorationActivityParams.ParentScreen, explorationCheckpoint: ExplorationCheckpoint ) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } val binding = DataBindingUtil.setContentView( activity, R.layout.resume_lesson_activity @@ -60,6 +68,13 @@ class ResumeLessonActivityPresenter @Inject constructor( this.parentScreen = parentScreen val resumeLessonToolbar = binding.resumeLessonActivityToolbar activity.setSupportActionBar(resumeLessonToolbar) + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + resumeLessonToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } retrieveReadingTextSize().observe( activity as ResumeLessonActivity, diff --git a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileEditActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileEditActivityPresenter.kt index ae929bfacfd..748e21324f5 100644 --- a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileEditActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileEditActivityPresenter.kt @@ -7,19 +7,26 @@ import androidx.appcompat.widget.Toolbar import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.model.ProfileEditActivityParams import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.util.extensions.getProtoExtra +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [ProfileEditActivity]. */ @ActivityScope class ProfileEditActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var toolbar: Toolbar /** Handles onCreate function of [ProfileEditActivity]. */ fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.profile_edit_activity) setUpToolbar() val args = activity.intent.getProtoExtra( @@ -50,6 +57,13 @@ class ProfileEditActivityPresenter @Inject constructor( private fun setUpToolbar() { toolbar = activity.findViewById(R.id.profile_edit_toolbar) as Toolbar activity.setSupportActionBar(toolbar) + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + toolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } } private fun getProfileEditFragment(): ProfileEditFragment? { diff --git a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileRenameActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileRenameActivityPresenter.kt index bfa944b5463..df3d816ef45 100644 --- a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileRenameActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileRenameActivityPresenter.kt @@ -5,14 +5,24 @@ import androidx.databinding.DataBindingUtil import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.ProfileRenameActivityBinding import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [ProfileRenameActivity]. */ @ActivityScope -class ProfileRenameActivityPresenter @Inject constructor(private val activity: AppCompatActivity) { +class ProfileRenameActivityPresenter @Inject constructor( + private val activity: AppCompatActivity, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue +) { /** Handles onCreate() of [ProfileRenameActivity]. */ fun handleOnCreate(profileId: Int) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) @@ -29,6 +39,15 @@ class ProfileRenameActivityPresenter @Inject constructor(private val activity: A binding.profileRenameToolbar.setNavigationOnClickListener { (activity as ProfileRenameActivity).finish() } + + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.profileRenameToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + if (getProfileRenameFragment() == null) { val profileRenameFragment = ProfileRenameFragment.newInstance(profileId) activity.supportFragmentManager.beginTransaction() diff --git a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileResetPinActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileResetPinActivityPresenter.kt index b69fbcbb278..b98b9a013d0 100644 --- a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileResetPinActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileResetPinActivityPresenter.kt @@ -7,8 +7,11 @@ import org.oppia.android.app.databinding.databinding.ProfileResetPinActivityBind import org.oppia.android.app.model.ProfileResetPinActivityParams import org.oppia.android.app.settings.profile.ProfileResetPinActivity.Companion.PROFILE_RESET_PIN_ACTIVITY_PARAMS_KEY import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.extensions.getProtoExtra +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [ProfileResetPinActivity]. */ @@ -16,10 +19,15 @@ import javax.inject.Inject class ProfileResetPinActivityPresenter @Inject constructor( private val activity: AppCompatActivity, private val profileManagementController: ProfileManagementController, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { /** Handles onCreate() method of the [ProfileResetPinActivity]. */ fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) @@ -41,6 +49,14 @@ class ProfileResetPinActivityPresenter @Inject constructor( (activity as ProfileResetPinActivity).finish() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.profileResetPinToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + binding.apply { lifecycleOwner = activity } diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt index fb2f7f6be9e..0b7e8e17c1d 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt @@ -20,6 +20,7 @@ import org.oppia.android.app.player.exploration.DefaultFontSizeStateListener import org.oppia.android.app.player.exploration.TAG_HINTS_AND_SOLUTION_DIALOG import org.oppia.android.app.topic.questionplayer.QuestionPlayerActivity.Companion.QUESTION_PLAYER_ACTIVITY_PARAMS_KEY import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.FontScaleConfigurationUtil import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.profile.ProfileManagementController @@ -27,6 +28,8 @@ import org.oppia.android.domain.question.QuestionTrainingController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData import org.oppia.android.util.extensions.getProtoExtra +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject const val TAG_QUESTION_PLAYER_FRAGMENT = "TAG_QUESTION_PLAYER_FRAGMENT" @@ -39,7 +42,9 @@ class QuestionPlayerActivityPresenter @Inject constructor( private val questionTrainingController: QuestionTrainingController, private val oppiaLogger: OppiaLogger, private val profileManagementController: ProfileManagementController, - private val fontScaleConfigurationUtil: FontScaleConfigurationUtil + private val fontScaleConfigurationUtil: FontScaleConfigurationUtil, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var profileId: LegacyProfileId private lateinit var state: State @@ -49,6 +54,9 @@ class QuestionPlayerActivityPresenter @Inject constructor( fun handleOnCreate(profileId: LegacyProfileId) { this.profileId = profileId + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } val binding = DataBindingUtil.setContentView( activity, R.layout.question_player_activity @@ -59,6 +67,13 @@ class QuestionPlayerActivityPresenter @Inject constructor( } activity.setSupportActionBar(binding.questionPlayerToolbar) + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.questionPlayerToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } binding.questionPlayerToolbar.setNavigationOnClickListener { activity.onBackPressedDispatcher.onBackPressed() diff --git a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt index a08900313cd..0141433c3ee 100644 --- a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt @@ -18,6 +18,7 @@ import org.oppia.android.app.options.OptionsActivity import org.oppia.android.app.player.exploration.BottomSheetOptionsMenu import org.oppia.android.app.player.exploration.DefaultFontSizeStateListener import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.FontScaleConfigurationUtil import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.oppialogger.analytics.AnalyticsController @@ -27,6 +28,8 @@ import org.oppia.android.domain.translation.TranslationController import org.oppia.android.util.accessibility.AccessibilityService import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [RevisionCardActivity]. */ @@ -39,6 +42,8 @@ class RevisionCardActivityPresenter @Inject constructor( private val translationController: TranslationController, private val profileManagementController: ProfileManagementController, private val fontScaleConfigurationUtil: FontScaleConfigurationUtil, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { @Inject lateinit var accessibilityService: AccessibilityService @@ -56,6 +61,9 @@ class RevisionCardActivityPresenter @Inject constructor( subtopicId: Int, subtopicListSize: Int ) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } val binding = DataBindingUtil.setContentView( activity, R.layout.revision_card_activity @@ -78,6 +86,13 @@ class RevisionCardActivityPresenter @Inject constructor( revisionCardToolbar = binding.revisionCardToolbar revisionCardToolbarTitle = binding.revisionCardToolbarTitle activity.setSupportActionBar(revisionCardToolbar) + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + revisionCardToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } activity.supportActionBar?.setDisplayShowTitleEnabled(false) binding.revisionCardToolbar.setNavigationOnClickListener { diff --git a/app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt b/app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt new file mode 100644 index 00000000000..8c629f7c780 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt @@ -0,0 +1,178 @@ +package org.oppia.android.app.utility + +import android.os.Build +import android.view.View +import android.widget.LinearLayout +import androidx.annotation.ColorRes +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.Toolbar +import androidx.core.content.ContextCompat +import androidx.core.graphics.Insets +import androidx.core.view.ViewCompat +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding + +/** + * Utility that dispatches edge-to-edge window insets to activity toolbars and the navigation + * drawer while the `EnableEdgeToEdge` platform parameter gates the Android 15 rollout. + */ +object EdgeToEdgeHelper { + /** + * Enables manual edge-to-edge window dispatch for [activity]. Must be called before + * [AppCompatActivity.setContentView]. + */ + fun enableEdgeToEdgeDispatch(activity: AppCompatActivity) { + WindowCompat.setDecorFitsSystemWindows(activity.window, false) + } + + /** + * Applies edge-to-edge insets for activities whose root vertical `LinearLayout` directly + * contains the [toolbar] (e.g. Home, Options). Inserts a status-bar spacer colored with + * [statusBarColorRes] at the top of that `LinearLayout`. + */ + fun applyToToolbarContainer( + activity: AppCompatActivity, + toolbar: Toolbar, + @ColorRes statusBarColorRes: Int + ) { + val contentLayout = toolbar.parent as LinearLayout + val spacer = createStatusBarSpacer(activity, statusBarColorRes) + contentLayout.addView(spacer, 0) + + ViewCompat.setOnApplyWindowInsetsListener(spacer) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.layoutParams.height = bars.top + view.requestLayout() + insets + } + ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.updatePadding(left = bars.left, right = bars.right, bottom = bars.bottom) + insets + } + + disableDecorAncestorFits(activity) + applyRootInsetsIfAvailable(contentLayout) { bars -> + spacer.layoutParams.height = bars.top + spacer.requestLayout() + contentLayout.updatePadding(left = bars.left, right = bars.right, bottom = bars.bottom) + } + disableNavBarContrast(activity) + } + + /** + * Applies edge-to-edge insets for activities where the [toolbar] is wrapped in an app-bar + * `LinearLayout` or `AppBarLayout` (e.g. AppLanguage, FAQ). Paints the wrapper with + * [statusBarColorRes] and adds top padding equal to the status-bar inset; cutout-horizontal + * and navigation-bar-bottom padding is applied to `android.R.id.content`. + */ + fun applyToAppBarLayout( + activity: AppCompatActivity, + toolbar: Toolbar, + @ColorRes statusBarColorRes: Int + ) { + val appBarLayout = toolbar.parent as LinearLayout + appBarLayout.setBackgroundColor(ContextCompat.getColor(activity, statusBarColorRes)) + val contentRoot = activity.findViewById(android.R.id.content) + + ViewCompat.setOnApplyWindowInsetsListener(appBarLayout) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.updatePadding(top = bars.top) + insets + } + ViewCompat.setOnApplyWindowInsetsListener(contentRoot) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.setPadding(bars.left, 0, bars.right, bars.bottom) + insets + } + + disableDecorAncestorFits(activity) + applyRootInsetsIfAvailable(appBarLayout) { bars -> + appBarLayout.updatePadding(top = bars.top) + contentRoot.setPadding(bars.left, 0, bars.right, bars.bottom) + } + disableNavBarContrast(activity) + } + + /** + * Applies edge-to-edge insets for the side navigation drawer. Inserts a status-bar spacer + * colored with [statusBarColorRes] at the top of [drawerContentLayout], clears + * [navigationView]'s `fitsSystemWindows` and replaces its inset listener with a no-op so its + * default scrim does not paint over the drawer's system-bar area, and applies RTL-aware side + * and bottom padding to [drawerRoot]. + */ + fun applyToNavigationDrawer( + activity: AppCompatActivity, + drawerRoot: View, + drawerContentLayout: LinearLayout, + navigationView: View, + @ColorRes statusBarColorRes: Int + ) { + navigationView.fitsSystemWindows = false + + val spacer = createStatusBarSpacer(activity, statusBarColorRes) + drawerContentLayout.addView(spacer, 0) + ViewCompat.setOnApplyWindowInsetsListener(spacer) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.layoutParams.height = bars.top + view.requestLayout() + insets + } + + ViewCompat.setOnApplyWindowInsetsListener(navigationView) { _, insets -> insets } + + ViewCompat.setOnApplyWindowInsetsListener(drawerRoot) { view, insets -> + val bars = insets.systemBarsWithCutout() + val isRtl = ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL + view.updatePadding( + left = if (isRtl) 0 else bars.left, + right = if (isRtl) bars.right else 0, + bottom = bars.bottom + ) + insets + } + } + + private fun createStatusBarSpacer( + activity: AppCompatActivity, + @ColorRes statusBarColorRes: Int + ): View = View(activity).apply { + setBackgroundColor(ContextCompat.getColor(activity, statusBarColorRes)) + // Explicit 0-height; default is MATCH_PARENT which paints the whole screen until the inset + // listener resizes the spacer. + layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0) + } + + private fun WindowInsetsCompat.systemBarsWithCutout(): Insets = getInsets( + WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() + ) + + // AppCompat Bridge themes re-apply status-bar top padding via legacy fitsSystemWindows + // dispatch on ancestors like FitWindowsLinearLayout; clearing those flags avoids double-counting + // the top inset. + private fun disableDecorAncestorFits(activity: AppCompatActivity) { + val contentRoot = activity.findViewById(android.R.id.content) + var ancestor: View? = contentRoot.parent as? View + while (ancestor != null) { + if (ancestor.fitsSystemWindows) { + ancestor.fitsSystemWindows = false + ancestor.setPadding(0, 0, 0, 0) + } + ancestor = ancestor.parent as? View + } + } + + // On activity-recreate the dispatch listener may not fire on the first frame; seed the spacer + // and padding synchronously from getRootWindowInsets to avoid a momentary full-screen flash. + private fun applyRootInsetsIfAvailable(view: View, apply: (Insets) -> Unit) { + val rootInsets = ViewCompat.getRootWindowInsets(view) ?: return + apply(rootInsets.systemBarsWithCutout()) + } + + private fun disableNavBarContrast(activity: AppCompatActivity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + activity.window.isNavigationBarContrastEnforced = false + } + } +} From 40065b35b02604ca377e6fd0d3eeb356fd5b6e5a Mon Sep 17 00:00:00 2001 From: Neer-rn Date: Thu, 23 Apr 2026 20:34:01 +0545 Subject: [PATCH 06/12] Fix #5940: Add edge-to-edge insets to remaining toolbar screens --- .../CompletedStoryListActivityPresenter.kt | 9 ++++++++- .../CompletedStoryListFragmentPresenter.kt | 14 +++++++++++++- .../MarkChaptersCompletedActivityPresenter.kt | 9 ++++++++- .../MarkChaptersCompletedFragmentPresenter.kt | 14 +++++++++++++- .../MarkStoriesCompletedActivityPresenter.kt | 9 ++++++++- .../MarkStoriesCompletedFragmentPresenter.kt | 15 ++++++++++++++- .../MarkTopicsCompletedActivityPresenter.kt | 9 ++++++++- .../MarkTopicsCompletedFragmentPresenter.kt | 15 ++++++++++++++- .../OngoingTopicListActivityPresenter.kt | 9 ++++++++- .../OngoingTopicListFragmentPresenter.kt | 14 +++++++++++++- .../app/player/state/StateFragmentPresenter.kt | 11 ++++++++++- .../app/profile/PinPasswordActivityPresenter.kt | 16 +++++++++++++++- .../profile/ProfileListActivityPresenter.kt | 9 ++++++++- .../profile/ProfileListFragmentPresenter.kt | 17 ++++++++++++++++- .../android/app/story/StoryActivityPresenter.kt | 11 ++++++++++- .../android/app/story/StoryFragmentPresenter.kt | 14 +++++++++++++- .../android/app/topic/TopicActivityPresenter.kt | 11 ++++++++++- .../android/app/topic/TopicFragmentPresenter.kt | 13 ++++++++++++- 18 files changed, 201 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityPresenter.kt index 04ade2649d0..373aefd5982 100644 --- a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityPresenter.kt @@ -4,16 +4,23 @@ import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [CompletedStoryListActivity]. */ @ActivityScope class CompletedStoryListActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { /** Initializes views for [CompletedStoryListActivity] and binds [CompletedStoryListFragment]. */ fun handleOnCreate(profileId: LegacyProfileId) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.completed_story_list_activity) if (getCompletedStoryListFragment() == null) { activity diff --git a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragmentPresenter.kt index bf94a7dcfcc..b326b956a2e 100644 --- a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragmentPresenter.kt @@ -11,6 +11,9 @@ import org.oppia.android.app.databinding.databinding.CompletedStoryListFragmentB import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [CompletedStoryListFragment]. */ @@ -18,7 +21,8 @@ class CompletedStoryListFragmentPresenter @Inject constructor( private val activity: AppCompatActivity, private val fragment: Fragment, private val viewModel: CompletedStoryListViewModel, - private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory + private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var binding: CompletedStoryListFragmentBinding @@ -49,6 +53,14 @@ class CompletedStoryListFragmentPresenter @Inject constructor( it.lifecycleOwner = fragment it.viewModel = viewModel } + + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.completedStoryListToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } return binding.root } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivityPresenter.kt index d01b6e91a1f..2d546656428 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivityPresenter.kt @@ -3,15 +3,22 @@ package org.oppia.android.app.devoptions.markchapterscompleted import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [MarkChaptersCompletedActivity]. */ @ActivityScope class MarkChaptersCompletedActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { fun handleOnCreate(internalProfileId: Int, showConfirmationNotice: Boolean) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) activity.setContentView(R.layout.mark_chapters_completed_activity) diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt index 4a480a377a1..32e54fbe0c8 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt @@ -16,7 +16,10 @@ import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.domain.devoptions.ModifyLessonProgressController +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [MarkChaptersCompletedFragment]. */ @@ -27,7 +30,8 @@ class MarkChaptersCompletedFragmentPresenter @Inject constructor( private val viewModel: MarkChaptersCompletedViewModel, private val modifyLessonProgressController: ModifyLessonProgressController, private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var binding: MarkChaptersCompletedFragmentBinding private lateinit var linearLayoutManager: LinearLayoutManager @@ -111,6 +115,14 @@ class MarkChaptersCompletedFragmentPresenter @Inject constructor( } else markChaptersAsCompleted() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.markChaptersCompletedToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + return binding.root } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedActivityPresenter.kt index 351615fc226..6a41f562cbd 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedActivityPresenter.kt @@ -3,15 +3,22 @@ package org.oppia.android.app.devoptions.markstoriescompleted import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [MarkStoriesCompletedActivity]. */ @ActivityScope class MarkStoriesCompletedActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { fun handleOnCreate(internalProfileId: Int) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) activity.setContentView(R.layout.mark_stories_completed_activity) diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedFragmentPresenter.kt index 0186fffe056..0b1a955aa24 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedFragmentPresenter.kt @@ -11,7 +11,11 @@ import org.oppia.android.app.databinding.databinding.MarkStoriesCompletedStorySu import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.recyclerview.BindableAdapter +import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.domain.devoptions.ModifyLessonProgressController +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [MarkStoriesCompletedFragment]. */ @@ -21,7 +25,8 @@ class MarkStoriesCompletedFragmentPresenter @Inject constructor( private val fragment: Fragment, private val viewModel: MarkStoriesCompletedViewModel, private val modifyLessonProgressController: ModifyLessonProgressController, - private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory + private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) : StorySelector { private lateinit var binding: MarkStoriesCompletedFragmentBinding private lateinit var linearLayoutManager: LinearLayoutManager @@ -95,6 +100,14 @@ class MarkStoriesCompletedFragmentPresenter @Inject constructor( activity.finish() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.markStoriesCompletedToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + return binding.root } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedActivityPresenter.kt index 890a038c517..c314a3ce03b 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedActivityPresenter.kt @@ -3,15 +3,22 @@ package org.oppia.android.app.devoptions.marktopicscompleted import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [MarkTopicsCompletedActivity]. */ @ActivityScope class MarkTopicsCompletedActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { fun handleOnCreate(internalProfileId: Int) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) activity.setContentView(R.layout.mark_topics_completed_activity) diff --git a/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedFragmentPresenter.kt index bcf6b3d7af6..fa637c14655 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedFragmentPresenter.kt @@ -11,7 +11,11 @@ import org.oppia.android.app.databinding.databinding.MarkTopicsCompletedTopicVie import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.recyclerview.BindableAdapter +import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.domain.devoptions.ModifyLessonProgressController +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [MarkTopicsCompletedFragment]. */ @@ -21,7 +25,8 @@ class MarkTopicsCompletedFragmentPresenter @Inject constructor( private val fragment: Fragment, private val viewModel: MarkTopicsCompletedViewModel, private val modifyLessonProgressController: ModifyLessonProgressController, - private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory + private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) : TopicSelector { private lateinit var binding: MarkTopicsCompletedFragmentBinding private lateinit var linearLayoutManager: LinearLayoutManager @@ -88,6 +93,14 @@ class MarkTopicsCompletedFragmentPresenter @Inject constructor( activity.finish() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.markTopicsCompletedToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + return binding.root } diff --git a/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivityPresenter.kt index 2bbbd5267ab..7b6cc205b61 100644 --- a/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivityPresenter.kt @@ -3,14 +3,21 @@ package org.oppia.android.app.ongoingtopiclist import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [OngoingTopicListActivity]. */ @ActivityScope class OngoingTopicListActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { fun handleOnCreate(internalProfileId: Int) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.ongoing_topic_list_activity) if (getOngoingTopicListFragment() == null) { activity.supportFragmentManager.beginTransaction().add( diff --git a/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListFragmentPresenter.kt index 2d1ef6694a8..5abe9313f5a 100644 --- a/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListFragmentPresenter.kt @@ -10,6 +10,9 @@ import org.oppia.android.app.databinding.databinding.OngoingTopicItemBinding import org.oppia.android.app.databinding.databinding.OngoingTopicListFragmentBinding import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [OngoingTopicListFragment]. */ @@ -17,7 +20,8 @@ class OngoingTopicListFragmentPresenter @Inject constructor( private val activity: AppCompatActivity, private val fragment: Fragment, private val ongoingTopicListViewModel: OngoingTopicListViewModel, - private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory + private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var binding: OngoingTopicListFragmentBinding @@ -51,6 +55,14 @@ class OngoingTopicListFragmentPresenter @Inject constructor( it.lifecycleOwner = fragment it.viewModel = ongoingTopicListViewModel } + + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.ongoingTopicListToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } return binding.root } diff --git a/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt index 1b6374b1efd..8700f82946a 100755 --- a/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt @@ -55,6 +55,8 @@ import org.oppia.android.util.data.DataProvider import org.oppia.android.util.data.DataProviders.Companion.toLiveData import org.oppia.android.util.gcsresource.DefaultResourceBucketName import org.oppia.android.util.parser.html.ExplorationHtmlParserEntityType +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.system.OppiaClock import javax.inject.Inject @@ -84,7 +86,8 @@ class StateFragmentPresenter @Inject constructor( private val stateViewModel: StateViewModel, private val accessibilityService: AccessibilityService, private val resourceHandler: AppLanguageResourceHandler, - private val surveyGatingController: SurveyGatingController + private val surveyGatingController: SurveyGatingController, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private val routeToHintsAndSolutionListener = activity as RouteToHintsAndSolutionListener @@ -128,6 +131,12 @@ class StateFragmentPresenter @Inject constructor( container, /* attachToRoot= */ false ) + if (enableEdgeToEdge.value) { + // The activity's EdgeToEdgeHelper applies the status-bar inset to the AppBarLayout; the + // legacy fitsSystemWindows on this fragment's root would re-apply the same inset as inner + // top padding, leaving a blank gap below the toolbar. + binding.root.fitsSystemWindows = false + } recyclerViewAssembler = createRecyclerViewAssembler( assemblerBuilderFactory.create(resourceBucketName, entityType, profileId, userAnswerState), binding.congratulationsTextView, diff --git a/app/src/main/java/org/oppia/android/app/profile/PinPasswordActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/profile/PinPasswordActivityPresenter.kt index 44f8774d215..5d708fc23b9 100644 --- a/app/src/main/java/org/oppia/android/app/profile/PinPasswordActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profile/PinPasswordActivityPresenter.kt @@ -17,6 +17,7 @@ import org.oppia.android.app.onboarding.IntroActivity import org.oppia.android.app.profile.PinPasswordActivity.Companion.PIN_PASSWORD_ACTIVITY_PARAMS_KEY import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.TextInputEditTextHelper.Companion.onTextChanged import org.oppia.android.app.utility.lifecycle.LifecycleSafeTimerFactory import org.oppia.android.domain.profile.ProfileManagementController @@ -24,6 +25,7 @@ import org.oppia.android.util.accessibility.AccessibilityService import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData import org.oppia.android.util.extensions.getProtoExtra +import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.EnableMultipleClassrooms import org.oppia.android.util.platformparameter.EnableOnboardingFlowV2 import org.oppia.android.util.platformparameter.PlatformParameterValue @@ -42,7 +44,8 @@ class PinPasswordActivityPresenter @Inject constructor( private val resourceHandler: AppLanguageResourceHandler, private val accessibilityService: AccessibilityService, @EnableMultipleClassrooms private val enableMultipleClassrooms: PlatformParameterValue, - @EnableOnboardingFlowV2 private val enableOnboardingFlowV2: PlatformParameterValue + @EnableOnboardingFlowV2 private val enableOnboardingFlowV2: PlatformParameterValue, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private var internalProfileId = -1 private var profileId = LegacyProfileId.getDefaultInstance() @@ -59,6 +62,9 @@ class PinPasswordActivityPresenter @Inject constructor( internalProfileId = args?.internalProfileId ?: -1 profileId = LegacyProfileId.newBuilder().setInternalId(internalProfileId).build() + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } val binding = DataBindingUtil.setContentView( activity, R.layout.pin_password_activity @@ -74,6 +80,14 @@ class PinPasswordActivityPresenter @Inject constructor( (activity as PinPasswordActivity).finish() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.pinPasswordToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + binding.showPin.setOnClickListener { pinViewModel.showPassword.set(!pinViewModel.showPassword.get()!!) if (!pinViewModel.showPassword.get()!!) { diff --git a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListActivityPresenter.kt index 3137c068ecb..c789889c517 100644 --- a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListActivityPresenter.kt @@ -4,17 +4,24 @@ import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [ProfileListActivity]. */ @ActivityScope class ProfileListActivityPresenter @Inject constructor( private val activity: AppCompatActivity, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { /** Handles onCreate method of [ProfileListActivity]. */ fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.title = resourceHandler.getStringInLocale(R.string.profile_list_activity_title) activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) diff --git a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListFragmentPresenter.kt index 9119d4b37ca..1dd000bd639 100644 --- a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListFragmentPresenter.kt @@ -11,6 +11,10 @@ import org.oppia.android.app.databinding.databinding.ProfileListProfileViewBindi import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.model.Profile import org.oppia.android.app.recyclerview.BindableAdapter +import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [ProfileListFragment]. */ @@ -19,7 +23,8 @@ class ProfileListFragmentPresenter @Inject constructor( private val activity: AppCompatActivity, private val fragment: Fragment, private val profileListViewModel: ProfileListViewModel, - private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory + private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private var isMultipane = false @@ -48,6 +53,16 @@ class ProfileListFragmentPresenter @Inject constructor( binding.profileListRecyclerView.apply { adapter = createRecyclerViewAdapter() } + + if (enableEdgeToEdge.value) { + binding.profileListToolbar?.let { toolbar -> + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + toolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + } return binding.root } diff --git a/app/src/main/java/org/oppia/android/app/story/StoryActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/story/StoryActivityPresenter.kt index d40f887c5ef..af7e0056604 100644 --- a/app/src/main/java/org/oppia/android/app/story/StoryActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/story/StoryActivityPresenter.kt @@ -3,17 +3,26 @@ package org.oppia.android.app.story import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [StoryActivity]. */ @ActivityScope -class StoryActivityPresenter @Inject constructor(private val activity: AppCompatActivity) { +class StoryActivityPresenter @Inject constructor( + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue +) { fun handleOnCreate( internalProfileId: Int, classroomId: String, topicId: String, storyId: String ) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.story_activity) if (getStoryFragment() == null) { activity.supportFragmentManager.beginTransaction().add( diff --git a/app/src/main/java/org/oppia/android/app/story/StoryFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/story/StoryFragmentPresenter.kt index 6e37c11cd27..5b9f7c13ca1 100644 --- a/app/src/main/java/org/oppia/android/app/story/StoryFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/story/StoryFragmentPresenter.kt @@ -32,6 +32,7 @@ import org.oppia.android.app.story.storyitemviewmodel.StoryItemViewModel import org.oppia.android.app.topic.RouteToResumeLessonListener import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.domain.exploration.ExplorationDataController import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.oppialogger.analytics.AnalyticsController @@ -41,6 +42,8 @@ import org.oppia.android.util.data.DataProviders.Companion.toLiveData import org.oppia.android.util.gcsresource.DefaultResourceBucketName import org.oppia.android.util.parser.html.HtmlParser import org.oppia.android.util.parser.html.TopicHtmlParserEntityType +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [StoryFragment]. */ @@ -54,7 +57,8 @@ class StoryFragmentPresenter @Inject constructor( @DefaultResourceBucketName private val resourceBucketName: String, @TopicHtmlParserEntityType private val entityType: String, private val resourceHandler: AppLanguageResourceHandler, - private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory + private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private val routeToExplorationListener = activity as RouteToExplorationListener private val routeToResumeLessonListener = activity as RouteToResumeLessonListener @@ -109,6 +113,14 @@ class StoryFragmentPresenter @Inject constructor( it.lifecycleOwner = fragment it.viewModel = storyViewModel } + + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.storyToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } return binding.root } diff --git a/app/src/main/java/org/oppia/android/app/topic/TopicActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/TopicActivityPresenter.kt index 18b52063bfa..e680305694c 100755 --- a/app/src/main/java/org/oppia/android/app/topic/TopicActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/TopicActivityPresenter.kt @@ -8,7 +8,10 @@ import org.oppia.android.app.model.TopicFragmentArguments import org.oppia.android.app.spotlight.SpotlightFragment import org.oppia.android.app.spotlight.SpotlightManager import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.util.extensions.putProto +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId import javax.inject.Inject @@ -17,7 +20,10 @@ const val TOPIC_FRAGMENT_ARGUMENTS_KEY = "TopicFragment.arguments" /** The presenter for [TopicActivity]. */ @ActivityScope -class TopicActivityPresenter @Inject constructor(private val activity: AppCompatActivity) { +class TopicActivityPresenter @Inject constructor( + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue +) { private lateinit var classroomId: String private lateinit var topicId: String @@ -29,6 +35,9 @@ class TopicActivityPresenter @Inject constructor(private val activity: AppCompat ) { this.topicId = topicId this.classroomId = classroomId + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.topic_activity) if (getTopicFragment() == null) { diff --git a/app/src/main/java/org/oppia/android/app/topic/TopicFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/TopicFragmentPresenter.kt index 7d27b7eeef4..8f2625146df 100644 --- a/app/src/main/java/org/oppia/android/app/topic/TopicFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/TopicFragmentPresenter.kt @@ -18,9 +18,11 @@ import org.oppia.android.app.spotlight.SpotlightShape import org.oppia.android.app.spotlight.SpotlightTarget import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.oppialogger.analytics.AnalyticsController import org.oppia.android.util.accessibility.AccessibilityService +import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.EnableTopicInfoTab import org.oppia.android.util.platformparameter.EnableTopicPracticeTab import org.oppia.android.util.platformparameter.PlatformParameterValue @@ -36,7 +38,8 @@ class TopicFragmentPresenter @Inject constructor( private val analyticsController: AnalyticsController, @EnableTopicInfoTab private val enableTopicInfoTabFlag: PlatformParameterValue, @EnableTopicPracticeTab private val enableTopicPracticeTabFlag: PlatformParameterValue, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { @Inject lateinit var accessibilityService: AccessibilityService @@ -95,6 +98,14 @@ class TopicFragmentPresenter @Inject constructor( ) } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.topicToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + return binding.root } From 018bb18c1f5ca2e10edcf8661e733b01eea86760 Mon Sep 17 00:00:00 2001 From: Neer-rn Date: Fri, 24 Apr 2026 18:14:35 +0545 Subject: [PATCH 07/12] Fix part of #6208: Add edge-to-edge insets to no-toolbar screens --- .../onboarding/OnboardingActivityPresenter.kt | 12 +++- .../OnboardingFragmentPresenterV1.kt | 54 +++++++-------- .../ProfileChooserActivityPresenter.kt | 9 ++- .../ProfileChooserFragmentPresenter.kt | 21 ++++-- .../ProfileChooserFragmentPresenterV1.kt | 21 ++++-- .../app/splash/SplashActivityPresenter.kt | 13 +++- .../android/app/utility/EdgeToEdgeHelper.kt | 69 ++++++++++++++++++- 7 files changed, 159 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/onboarding/OnboardingActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/onboarding/OnboardingActivityPresenter.kt index db4baea145e..0cb4f645280 100644 --- a/app/src/main/java/org/oppia/android/app/onboarding/OnboardingActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/onboarding/OnboardingActivityPresenter.kt @@ -3,12 +3,22 @@ package org.oppia.android.app.onboarding import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [OnboardingActivity]. */ @ActivityScope -class OnboardingActivityPresenter @Inject constructor(private val activity: AppCompatActivity) { +class OnboardingActivityPresenter @Inject constructor( + private val activity: AppCompatActivity, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue +) { fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.onboarding_activity) if (getOnboardingFragment() == null) { activity.supportFragmentManager.beginTransaction().add( diff --git a/app/src/main/java/org/oppia/android/app/onboarding/OnboardingFragmentPresenterV1.kt b/app/src/main/java/org/oppia/android/app/onboarding/OnboardingFragmentPresenterV1.kt index 9ec394a348b..7a0eaad0993 100644 --- a/app/src/main/java/org/oppia/android/app/onboarding/OnboardingFragmentPresenterV1.kt +++ b/app/src/main/java/org/oppia/android/app/onboarding/OnboardingFragmentPresenterV1.kt @@ -6,6 +6,8 @@ import android.view.ViewGroup import android.widget.ImageView import android.widget.LinearLayout import androidx.appcompat.app.AppCompatActivity +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.viewpager2.widget.ViewPager2 import org.oppia.android.app.databinding.databinding.OnboardingFragmentBinding @@ -17,8 +19,11 @@ import org.oppia.android.app.policies.RouteToPoliciesListener import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.util.parser.html.HtmlParser import org.oppia.android.util.parser.html.PolicyType +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.statusbar.StatusBarColor import javax.inject.Inject @@ -31,10 +36,13 @@ class OnboardingFragmentPresenterV1 @Inject constructor( private val onboardingSlideFinalViewModel: OnboardingSlideFinalViewModel, private val resourceHandler: AppLanguageResourceHandler, private val htmlParserFactory: HtmlParser.Factory, - private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory + private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) : OnboardingNavigationListener, HtmlParser.PolicyOppiaTagActionListener { private val dotsList = ArrayList() private lateinit var binding: OnboardingFragmentBinding + private var statusBarSpacer: View? = null fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View { binding = OnboardingFragmentBinding.inflate( @@ -49,6 +57,13 @@ class OnboardingFragmentPresenterV1 @Inject constructor( it.presenter = this it.viewModel = onboardingViewModel } + if (enableEdgeToEdge.value) { + statusBarSpacer = EdgeToEdgeHelper.applyToRootConstraintLayout( + activity, + binding.root as ConstraintLayout, + R.color.component_color_onboarding_1_status_bar_color + ) + } setUpViewPager() addDots() return binding.root @@ -156,32 +171,17 @@ class OnboardingFragmentPresenterV1 @Inject constructor( } private fun onboardingStatusBarColorUpdate(position: Int) { - when (position) { - 0 -> StatusBarColor.statusBarColorUpdate( - R.color.component_color_onboarding_1_status_bar_color, - activity, - false - ) - 1 -> StatusBarColor.statusBarColorUpdate( - R.color.component_color_onboarding_2_status_bar_color, - activity, - false - ) - 2 -> StatusBarColor.statusBarColorUpdate( - R.color.component_color_onboarding_3_status_bar_color, - activity, - false - ) - 3 -> StatusBarColor.statusBarColorUpdate( - R.color.component_color_onboarding_4_status_bar_color, - activity, - false - ) - else -> StatusBarColor.statusBarColorUpdate( - R.color.component_color_shared_activity_status_bar_color, - activity, - false - ) + val colorRes = when (position) { + 0 -> R.color.component_color_onboarding_1_status_bar_color + 1 -> R.color.component_color_onboarding_2_status_bar_color + 2 -> R.color.component_color_onboarding_3_status_bar_color + 3 -> R.color.component_color_onboarding_4_status_bar_color + else -> R.color.component_color_shared_activity_status_bar_color + } + if (enableEdgeToEdge.value) { + statusBarSpacer?.setBackgroundColor(ContextCompat.getColor(activity, colorRes)) + } else { + StatusBarColor.statusBarColorUpdate(colorRes, activity, false) } } diff --git a/app/src/main/java/org/oppia/android/app/profile/ProfileChooserActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/profile/ProfileChooserActivityPresenter.kt index 24a3770fbd5..135c87bc0a7 100644 --- a/app/src/main/java/org/oppia/android/app/profile/ProfileChooserActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profile/ProfileChooserActivityPresenter.kt @@ -8,8 +8,10 @@ import org.oppia.android.app.model.ProfileChooserActivityParams.ParentScreen import org.oppia.android.app.model.ProfileChooserFragmentArguments import org.oppia.android.app.testing.ProfileChooserFragmentTestActivity import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.extensions.putProto +import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.EnableOnboardingFlowV2 import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId @@ -24,7 +26,9 @@ class ProfileChooserActivityPresenter @Inject constructor( private val activity: AppCompatActivity, private val profileManagementController: ProfileManagementController, @EnableOnboardingFlowV2 - private val enableOnboardingFlowV2: PlatformParameterValue + private val enableOnboardingFlowV2: PlatformParameterValue, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { /** Adds [ProfileChooserFragment] to view. */ fun handleOnCreate(profileId: LegacyProfileId, parentScreen: ParentScreen) { @@ -40,6 +44,9 @@ class ProfileChooserActivityPresenter @Inject constructor( ) } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.profile_chooser_activity) if (getProfileChooserFragment() == null) { val profileChooserFragment = ProfileChooserFragment() diff --git a/app/src/main/java/org/oppia/android/app/profile/ProfileChooserFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/profile/ProfileChooserFragmentPresenter.kt index 7aceb527770..8f94af97c9f 100644 --- a/app/src/main/java/org/oppia/android/app/profile/ProfileChooserFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profile/ProfileChooserFragmentPresenter.kt @@ -7,6 +7,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity +import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat import androidx.fragment.app.Fragment @@ -29,11 +30,13 @@ import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.recyclerview.StartSnapHelper import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.oppialogger.analytics.AnalyticsController import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.EnableMultipleClassrooms import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.statusbar.StatusBarColor @@ -78,7 +81,8 @@ class ProfileChooserFragmentPresenter @Inject constructor( private val analyticsController: AnalyticsController, private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory, private val resourceHandler: AppLanguageResourceHandler, - @EnableMultipleClassrooms private val enableMultipleClassrooms: PlatformParameterValue + @EnableMultipleClassrooms private val enableMultipleClassrooms: PlatformParameterValue, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var binding: ProfileSelectionFragmentBinding @@ -93,9 +97,11 @@ class ProfileChooserFragmentPresenter @Inject constructor( adminProfileId: LegacyProfileId, parentScreen: ParentScreen ): View? { - StatusBarColor.statusBarColorUpdate( - R.color.component_color_shared_profile_status_bar_color, activity, false - ) + if (!enableEdgeToEdge.value) { + StatusBarColor.statusBarColorUpdate( + R.color.component_color_shared_profile_status_bar_color, activity, false + ) + } if (parentScreen == ParentScreen.ADMIN_INTRO_SCREEN) { // The admin onboarding ends here in order to prevent the admin from seeing the onboarding @@ -122,6 +128,13 @@ class ProfileChooserFragmentPresenter @Inject constructor( binding.addProfileButton.setOnClickListener { addProfileButtonClickListener() } binding.addProfilePrompt.setOnClickListener { addProfileButtonClickListener() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToRootConstraintLayout( + activity, + binding.root as ConstraintLayout, + R.color.component_color_shared_profile_status_bar_color + ) + } return binding.root } diff --git a/app/src/main/java/org/oppia/android/app/profile/ProfileChooserFragmentPresenterV1.kt b/app/src/main/java/org/oppia/android/app/profile/ProfileChooserFragmentPresenterV1.kt index 8aae2e90c57..13ade2daf6d 100644 --- a/app/src/main/java/org/oppia/android/app/profile/ProfileChooserFragmentPresenterV1.kt +++ b/app/src/main/java/org/oppia/android/app/profile/ProfileChooserFragmentPresenterV1.kt @@ -5,6 +5,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity +import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat import androidx.databinding.ObservableField import androidx.fragment.app.Fragment @@ -23,11 +24,13 @@ import org.oppia.android.app.model.Profile import org.oppia.android.app.model.ProfileChooserUiModel import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.oppialogger.analytics.AnalyticsController import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.EnableMultipleClassrooms import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.statusbar.StatusBarColor @@ -71,16 +74,19 @@ class ProfileChooserFragmentPresenterV1 @Inject constructor( private val oppiaLogger: OppiaLogger, private val analyticsController: AnalyticsController, private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory, - @EnableMultipleClassrooms private val enableMultipleClassrooms: PlatformParameterValue + @EnableMultipleClassrooms private val enableMultipleClassrooms: PlatformParameterValue, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var binding: ProfileChooserFragmentBinding val hasProfileEverBeenAddedValue = ObservableField(true) /** Binds ViewModel and sets up RecyclerView Adapter. */ fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? { - StatusBarColor.statusBarColorUpdate( - R.color.component_color_shared_profile_status_bar_color, activity, false - ) + if (!enableEdgeToEdge.value) { + StatusBarColor.statusBarColorUpdate( + R.color.component_color_shared_profile_status_bar_color, activity, false + ) + } binding = ProfileChooserFragmentBinding.inflate( inflater, container, @@ -97,6 +103,13 @@ class ProfileChooserFragmentPresenterV1 @Inject constructor( binding.profileRecyclerView.apply { adapter = createRecyclerViewAdapter() } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToRootConstraintLayout( + activity, + binding.root as ConstraintLayout, + R.color.component_color_shared_profile_status_bar_color + ) + } return binding.root } diff --git a/app/src/main/java/org/oppia/android/app/splash/SplashActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/splash/SplashActivityPresenter.kt index c50a09a2ddb..def80a7c9b5 100644 --- a/app/src/main/java/org/oppia/android/app/splash/SplashActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/splash/SplashActivityPresenter.kt @@ -37,6 +37,7 @@ import org.oppia.android.app.onboarding.PROFILE_CHOOSER_PARAMS_KEY import org.oppia.android.app.profile.ProfileChooserActivity import org.oppia.android.app.translation.AppLanguageLocaleHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.lifecycle.LifecycleSafeTimerFactory import org.oppia.android.domain.locale.LocaleController import org.oppia.android.domain.onboarding.AppStartupStateController @@ -52,6 +53,7 @@ import org.oppia.android.util.data.DataProviders.Companion.toLiveData import org.oppia.android.util.extensions.putProtoExtra import org.oppia.android.util.locale.OppiaLocale import org.oppia.android.util.platformparameter.EnableAppAndOsDeprecation +import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.EnableMultipleClassrooms import org.oppia.android.util.platformparameter.EnableOnboardingFlowV2 import org.oppia.android.util.platformparameter.PlatformParameterValue @@ -159,7 +161,9 @@ class SplashActivityPresenter @Inject constructor( @EnableOnboardingFlowV2 private val enableOnboardingFlowV2Provider: PlatformParameterValue, @EnableMultipleClassrooms - private val enableMultipleClassroomsProvider: PlatformParameterValue + private val enableMultipleClassroomsProvider: PlatformParameterValue, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var startupMode: StartupMode @@ -243,6 +247,13 @@ class SplashActivityPresenter @Inject constructor( } fun subscribeToOnboardingFlow() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + EdgeToEdgeHelper.applyToRootConstraintLayout( + activity, + R.color.component_color_shared_activity_status_bar_color + ) + } val liveData = computeInitStateDataProvider().toLiveData() liveData.observe( activity, diff --git a/app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt b/app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt index 8c629f7c780..8374e18e51c 100644 --- a/app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt +++ b/app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt @@ -1,11 +1,14 @@ package org.oppia.android.app.utility import android.os.Build +import android.view.Gravity import android.view.View +import android.widget.FrameLayout import android.widget.LinearLayout import androidx.annotation.ColorRes import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar +import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat import androidx.core.graphics.Insets import androidx.core.view.ViewCompat @@ -14,8 +17,9 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding /** - * Utility that dispatches edge-to-edge window insets to activity toolbars and the navigation - * drawer while the `EnableEdgeToEdge` platform parameter gates the Android 15 rollout. + * Utility that dispatches edge-to-edge window insets to activity toolbars, the navigation + * drawer, and no-toolbar ConstraintLayout roots while the `EnableEdgeToEdge` platform parameter + * gates the Android 15 rollout. */ object EdgeToEdgeHelper { /** @@ -144,6 +148,67 @@ object EdgeToEdgeHelper { layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0) } + /** + * Applies edge-to-edge insets for activities/fragments whose root is a no-toolbar + * [ConstraintLayout] (splash, onboarding, profile chooser). Paints the status-bar strip with + * [statusBarColorRes] and pads [rootLayout] on all four sides with the system-bar insets. + * Returns the spacer so the onboarding flow can recolor it on slide change. + */ + fun applyToRootConstraintLayout( + activity: AppCompatActivity, + rootLayout: ConstraintLayout, + @ColorRes statusBarColorRes: Int + ): View { + val contentFrame = activity.findViewById(android.R.id.content) + val spacer = View(activity).apply { + setBackgroundColor(ContextCompat.getColor(activity, statusBarColorRes)) + } + contentFrame.addView( + spacer, + FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, 0, Gravity.TOP) + ) + + ViewCompat.setOnApplyWindowInsetsListener(spacer) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.layoutParams.height = bars.top + view.requestLayout() + insets + } + ViewCompat.setOnApplyWindowInsetsListener(rootLayout) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.updatePadding( + top = bars.top, left = bars.left, right = bars.right, bottom = bars.bottom + ) + insets + } + + disableDecorAncestorFits(activity) + applyRootInsetsIfAvailable(rootLayout) { bars -> + spacer.layoutParams.height = bars.top + spacer.requestLayout() + rootLayout.updatePadding( + top = bars.top, left = bars.left, right = bars.right, bottom = bars.bottom + ) + } + disableNavBarContrast(activity) + + return spacer + } + + /** + * Overload of [applyToRootConstraintLayout] for call sites that cannot reach the binding (e.g. + * splash, where the inner presenter runs post-parameter-load). Resolves the root from the + * first child of `android.R.id.content`. + */ + fun applyToRootConstraintLayout( + activity: AppCompatActivity, + @ColorRes statusBarColorRes: Int + ): View { + val content = activity.findViewById(android.R.id.content) + val rootLayout = content.getChildAt(0) as ConstraintLayout + return applyToRootConstraintLayout(activity, rootLayout, statusBarColorRes) + } + private fun WindowInsetsCompat.systemBarsWithCutout(): Insets = getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() ) From e240c34a62ba89a5400f51dce92ed0ba0e13746c Mon Sep 17 00:00:00 2001 From: Neer-rn Date: Mon, 27 Apr 2026 08:19:54 +0545 Subject: [PATCH 08/12] Re-trigeer CI From 7f1a68fee8d35f339187a5cc025e1ad277df55ba Mon Sep 17 00:00:00 2001 From: Neer-rn Date: Mon, 27 Apr 2026 08:21:50 +0545 Subject: [PATCH 09/12] Retrigeer CI From 1748cf7aa8a0edc8ca59dd8e31330e710ca09696 Mon Sep 17 00:00:00 2001 From: Neer-rn Date: Mon, 27 Apr 2026 17:15:19 +0545 Subject: [PATCH 10/12] Handle ConstraintLayout toolbar parents --- .../android/app/utility/EdgeToEdgeHelper.kt | 50 +++++++++++++++++-- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt b/app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt index 8c629f7c780..0526a17da4d 100644 --- a/app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt +++ b/app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt @@ -29,14 +29,20 @@ object EdgeToEdgeHelper { /** * Applies edge-to-edge insets for activities whose root vertical `LinearLayout` directly * contains the [toolbar] (e.g. Home, Options). Inserts a status-bar spacer colored with - * [statusBarColorRes] at the top of that `LinearLayout`. + * [statusBarColorRes] at the top of that `LinearLayout`. If the parent is not a `LinearLayout` + * (e.g. `ConstraintLayout` in AdminPin / sw600dp AdminControls), pads the toolbar directly. */ fun applyToToolbarContainer( activity: AppCompatActivity, toolbar: Toolbar, @ColorRes statusBarColorRes: Int ) { - val contentLayout = toolbar.parent as LinearLayout + val parent = toolbar.parent as View + if (parent !is LinearLayout) { + applyAsTopBar(activity, toolbar, statusBarColorRes) + return + } + val contentLayout: LinearLayout = parent val spacer = createStatusBarSpacer(activity, statusBarColorRes) contentLayout.addView(spacer, 0) @@ -65,14 +71,20 @@ object EdgeToEdgeHelper { * Applies edge-to-edge insets for activities where the [toolbar] is wrapped in an app-bar * `LinearLayout` or `AppBarLayout` (e.g. AppLanguage, FAQ). Paints the wrapper with * [statusBarColorRes] and adds top padding equal to the status-bar inset; cutout-horizontal - * and navigation-bar-bottom padding is applied to `android.R.id.content`. + * and navigation-bar-bottom padding is applied to `android.R.id.content`. Same fallback as + * [applyToToolbarContainer] when the parent is not a `LinearLayout`. */ fun applyToAppBarLayout( activity: AppCompatActivity, toolbar: Toolbar, @ColorRes statusBarColorRes: Int ) { - val appBarLayout = toolbar.parent as LinearLayout + val parent = toolbar.parent as View + if (parent !is LinearLayout) { + applyAsTopBar(activity, toolbar, statusBarColorRes) + return + } + val appBarLayout: LinearLayout = parent appBarLayout.setBackgroundColor(ContextCompat.getColor(activity, statusBarColorRes)) val contentRoot = activity.findViewById(android.R.id.content) @@ -134,6 +146,36 @@ object EdgeToEdgeHelper { } } + // Used when the toolbar's parent is not a LinearLayout (e.g. ConstraintLayout). A spacer + // at index 0 doesn't work without constraints, so paint the toolbar itself and pad its top + // by the status-bar inset. Cutout / nav-bar padding goes on android.R.id.content. + private fun applyAsTopBar( + activity: AppCompatActivity, + topBar: View, + @ColorRes statusBarColorRes: Int + ) { + topBar.setBackgroundColor(ContextCompat.getColor(activity, statusBarColorRes)) + val contentRoot = activity.findViewById(android.R.id.content) + + ViewCompat.setOnApplyWindowInsetsListener(topBar) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.updatePadding(top = bars.top) + insets + } + ViewCompat.setOnApplyWindowInsetsListener(contentRoot) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.setPadding(bars.left, 0, bars.right, bars.bottom) + insets + } + + disableDecorAncestorFits(activity) + applyRootInsetsIfAvailable(topBar) { bars -> + topBar.updatePadding(top = bars.top) + contentRoot.setPadding(bars.left, 0, bars.right, bars.bottom) + } + disableNavBarContrast(activity) + } + private fun createStatusBarSpacer( activity: AppCompatActivity, @ColorRes statusBarColorRes: Int From 787fb9e4fa45e0a4ad036ca0afc70588f944a8ab Mon Sep 17 00:00:00 2001 From: Neer-rn Date: Tue, 28 Apr 2026 11:33:43 +0545 Subject: [PATCH 11/12] Re-trigeer CI From b1f3247d0a3a291923a178df98015bda6b3bc002 Mon Sep 17 00:00:00 2001 From: Neer-rn Date: Thu, 23 Jul 2026 12:17:21 +0545 Subject: [PATCH 12/12] edge to edge inset handling and tests --- app/BUILD.bazel | 2 + .../AdministratorControlsActivityPresenter.kt | 2 +- .../appversion/AppVersionActivityPresenter.kt | 2 +- .../ProfileAndDeviceIdActivityPresenter.kt | 2 +- .../ClassroomListActivityPresenter.kt | 2 +- .../CompletedStoryListActivityPresenter.kt | 2 +- .../CompletedStoryListFragmentPresenter.kt | 2 +- .../DeveloperOptionsActivityPresenter.kt | 2 +- .../FeatureFlagsActivityPresenter.kt | 9 +- .../FeatureFlagsFragmentPresenter.kt | 14 +- .../ForceNetworkTypeActivityPresenter.kt | 9 +- .../ForceNetworkTypeFragmentPresenter.kt | 15 +- .../MarkChaptersCompletedActivityPresenter.kt | 2 +- .../MarkChaptersCompletedFragmentPresenter.kt | 2 +- .../MarkStoriesCompletedActivityPresenter.kt | 2 +- .../MarkStoriesCompletedFragmentPresenter.kt | 2 +- .../MarkTopicsCompletedActivityPresenter.kt | 2 +- .../MarkTopicsCompletedFragmentPresenter.kt | 2 +- .../MathExpressionParserActivityPresenter.kt | 9 +- .../MathExpressionParserFragmentPresenter.kt | 14 +- .../PlatformParametersActivityPresenter.kt | 9 +- .../PlatformParametersFragmentPresenter.kt | 15 +- .../ViewEventLogsActivityPresenter.kt | 9 +- .../ViewEventLogsFragmentPresenter.kt | 15 +- .../NavigationDrawerFragmentPresenter.kt | 2 +- .../android/app/help/HelpActivityPresenter.kt | 2 +- .../app/help/faq/FAQListActivityPresenter.kt | 2 +- .../faqsingle/FAQSingleActivityPresenter.kt | 2 +- .../LicenseListActivityPresenter.kt | 2 +- .../LicenseTextViewerActivityPresenter.kt | 2 +- ...irdPartyDependencyListActivityPresenter.kt | 2 +- .../android/app/home/HomeActivityPresenter.kt | 2 +- .../RecentlyPlayedActivityPresenter.kt | 2 +- .../MyDownloadsActivityPresenter.kt | 11 +- .../MyDownloadsFragmentPresenter.kt | 15 +- .../OngoingTopicListActivityPresenter.kt | 2 +- .../OngoingTopicListFragmentPresenter.kt | 2 +- .../options/AppLanguageActivityPresenter.kt | 2 +- .../options/AudioLanguageActivityPresenter.kt | 2 +- .../app/options/OptionsActivityPresenter.kt | 2 +- .../ReadingTextSizeActivityPresenter.kt | 2 +- .../ExplorationActivityPresenter.kt | 2 +- .../app/policies/PoliciesActivityPresenter.kt | 2 +- .../profile/AddProfileActivityPresenter.kt | 2 +- .../app/profile/AdminAuthActivityPresenter.kt | 2 +- .../app/profile/AdminPinActivityPresenter.kt | 2 +- .../profile/PinPasswordActivityPresenter.kt | 2 +- .../ProfilePictureActivityPresenter.kt | 4 +- .../ProfileProgressActivityPresenter.kt | 2 +- .../ResumeLessonActivityPresenter.kt | 2 +- .../profile/ProfileEditActivityPresenter.kt | 2 +- .../profile/ProfileListActivityPresenter.kt | 2 +- .../profile/ProfileListFragmentPresenter.kt | 2 +- .../profile/ProfileRenameActivityPresenter.kt | 2 +- .../ProfileResetPinActivityPresenter.kt | 2 +- .../app/story/StoryActivityPresenter.kt | 2 +- .../app/story/StoryFragmentPresenter.kt | 2 +- .../app/survey/SurveyActivityPresenter.kt | 11 +- .../app/survey/SurveyFragmentPresenter.kt | 14 +- .../app/topic/TopicActivityPresenter.kt | 2 +- .../app/topic/TopicFragmentPresenter.kt | 2 +- .../QuestionPlayerActivityPresenter.kt | 2 +- .../RevisionCardActivityPresenter.kt | 4 +- .../studyguide/StudyGuideActivityPresenter.kt | 15 + .../android/app/utility/EdgeToEdgeHelper.kt | 220 ----------- .../app/utility/edgetoedge/BUILD.bazel | 17 + .../utility/edgetoedge/EdgeToEdgeHelper.kt | 358 ++++++++++++++++++ .../edgetoedge/EdgeToEdgeHelperTest.kt | 254 +++++++++++++ 68 files changed, 851 insertions(+), 284 deletions(-) delete mode 100644 app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt create mode 100644 app/src/main/java/org/oppia/android/app/utility/edgetoedge/BUILD.bazel create mode 100644 app/src/main/java/org/oppia/android/app/utility/edgetoedge/EdgeToEdgeHelper.kt create mode 100644 app/src/test/java/org/oppia/android/app/utility/edgetoedge/EdgeToEdgeHelperTest.kt diff --git a/app/BUILD.bazel b/app/BUILD.bazel index 9cc32896072..16a987aba18 100644 --- a/app/BUILD.bazel +++ b/app/BUILD.bazel @@ -658,6 +658,7 @@ kt_android_library( "//app/src/main/java/org/oppia/android/app/fragment:injectable_fragment", "//app/src/main/java/org/oppia/android/app/shim:prod_modules", "//app/src/main/java/org/oppia/android/app/testing/activity:test_activity", + "//app/src/main/java/org/oppia/android/app/utility/edgetoedge:edge_to_edge_helper", "//domain/src/main/java/org/oppia/android/domain/onboarding:state_controller", "//domain/src/main/java/org/oppia/android/domain/oppialogger:startup_listener", "//domain/src/main/java/org/oppia/android/domain/profile:profile_management_controller", @@ -758,6 +759,7 @@ TEST_DEPS = [ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module", "//app/src/main/java/org/oppia/android/app/testing/activity:test_activity", "//app/src/main/java/org/oppia/android/app/translation/testing:test_module", + "//app/src/main/java/org/oppia/android/app/utility/edgetoedge:edge_to_edge_helper", "//app/src/main/java/org/oppia/android/app/utility/math:math_expression_accessibility_util", "//config/src/java/org/oppia/android/config:all_languages_config", "//data/src/main/java/org/oppia/android/data/backends/gae/testing:network_config_test_module", diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt index 815c8ed55ee..1674e711b57 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityPresenter.kt @@ -16,7 +16,7 @@ import org.oppia.android.app.settings.profile.ProfileEditFragment import org.oppia.android.app.settings.profile.ProfileListFragment import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.extensions.putProto import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivityPresenter.kt index 5013a29b61c..a1c2358a606 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivityPresenter.kt @@ -4,7 +4,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityPresenter.kt index d2c975a86a2..4e9a6b8f2fe 100644 --- a/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityPresenter.kt @@ -3,7 +3,7 @@ package org.oppia.android.app.administratorcontrols.learneranalytics import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt index 95deaaf292f..dfda2a1a35d 100644 --- a/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/classroom/ClassroomListActivityPresenter.kt @@ -6,7 +6,7 @@ import androidx.appcompat.widget.Toolbar import androidx.drawerlayout.widget.DrawerLayout import org.oppia.android.app.drawer.NavigationDrawerFragment import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityPresenter.kt index 373aefd5982..42b8976c98c 100644 --- a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityPresenter.kt @@ -4,7 +4,7 @@ import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragmentPresenter.kt index b326b956a2e..5ee5ee7b9c5 100644 --- a/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragmentPresenter.kt @@ -11,7 +11,7 @@ import org.oppia.android.app.databinding.databinding.CompletedStoryListFragmentB import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt index 92d37d3a315..13e94da3f9b 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt @@ -8,7 +8,7 @@ import org.oppia.android.app.databinding.databinding.DeveloperOptionsActivityBin import org.oppia.android.app.drawer.NavigationDrawerFragment import org.oppia.android.app.splash.SplashActivity import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/devoptions/featureflags/FeatureFlagsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/featureflags/FeatureFlagsActivityPresenter.kt index 95205ca201b..367bab55f94 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/featureflags/FeatureFlagsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/featureflags/FeatureFlagsActivityPresenter.kt @@ -3,16 +3,23 @@ package org.oppia.android.app.devoptions.featureflags import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [FeatureFlagsActivity]. */ @ActivityScope class FeatureFlagsActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { /** Called when [FeatureFlagsActivity] is created. Handles UI for the activity. */ fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) activity.setContentView(R.layout.feature_flags_activity) diff --git a/app/src/main/java/org/oppia/android/app/devoptions/featureflags/FeatureFlagsFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/featureflags/FeatureFlagsFragmentPresenter.kt index 000b1872bab..43501290ff0 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/featureflags/FeatureFlagsFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/featureflags/FeatureFlagsFragmentPresenter.kt @@ -19,11 +19,14 @@ import org.oppia.android.app.model.OverriddenFeatureFlag import org.oppia.android.app.model.SyncStatus import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.splash.SplashActivity +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.app.view.models.R import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.platformparameter.PlatformParameterControllerDebugImpl import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject import kotlin.system.exitProcess @@ -41,7 +44,8 @@ class FeatureFlagsFragmentPresenter @Inject constructor( private val oppiaLogger: OppiaLogger, private val featureFlagsViewModel: FeatureFlagsViewModel, private val platformParameterControllerDebugImpl: PlatformParameterControllerDebugImpl, - private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory + private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var binding: FeatureFlagsFragmentBinding private lateinit var linearLayoutManager: LinearLayoutManager @@ -95,6 +99,14 @@ class FeatureFlagsFragmentPresenter @Inject constructor( } } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.featureFlagsToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + return binding.root } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivityPresenter.kt index c1a55e36ed0..ed4d2a87a24 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivityPresenter.kt @@ -3,16 +3,23 @@ package org.oppia.android.app.devoptions.forcenetworktype import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [ForceNetworkTypeActivity]. */ @ActivityScope class ForceNetworkTypeActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { /** Called when [ForceNetworkTypeActivity] is created. Handles UI for the activity. */ fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) activity.setContentView(R.layout.force_network_type_activity) diff --git a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragmentPresenter.kt index 9e5013e3c24..0c0ddaf7f6b 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragmentPresenter.kt @@ -11,7 +11,11 @@ import org.oppia.android.app.databinding.databinding.ForceNetworkTypeFragmentBin import org.oppia.android.app.databinding.databinding.ForceNetworkTypeNetworkItemViewBinding import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.recyclerview.BindableAdapter +import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.networking.NetworkConnectionDebugUtil +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [ForceNetworkTypeFragment]. */ @@ -21,7 +25,8 @@ class ForceNetworkTypeFragmentPresenter @Inject constructor( private val fragment: Fragment, private val networkConnectionUtil: Optional, private val forceNetworkTypeViewModel: ForceNetworkTypeViewModel, - private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory + private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var binding: ForceNetworkTypeFragmentBinding @@ -56,6 +61,14 @@ class ForceNetworkTypeFragmentPresenter @Inject constructor( adapter = bindingAdapter } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.forceNetworkTypeToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + return binding.root } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivityPresenter.kt index 2d546656428..cd5cd7a8f88 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivityPresenter.kt @@ -3,7 +3,7 @@ package org.oppia.android.app.devoptions.markchapterscompleted import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt index 32e54fbe0c8..1796e6f7db5 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragmentPresenter.kt @@ -16,7 +16,7 @@ import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.devoptions.ModifyLessonProgressController import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedActivityPresenter.kt index 6a41f562cbd..c289d58cd92 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedActivityPresenter.kt @@ -3,7 +3,7 @@ package org.oppia.android.app.devoptions.markstoriescompleted import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedFragmentPresenter.kt index 0b1a955aa24..fcd5d6829c7 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedFragmentPresenter.kt @@ -12,7 +12,7 @@ import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.devoptions.ModifyLessonProgressController import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue diff --git a/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedActivityPresenter.kt index c314a3ce03b..9ff455aa79a 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedActivityPresenter.kt @@ -3,7 +3,7 @@ package org.oppia.android.app.devoptions.marktopicscompleted import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedFragmentPresenter.kt index fa637c14655..746888f7414 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedFragmentPresenter.kt @@ -12,7 +12,7 @@ import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.devoptions.ModifyLessonProgressController import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue diff --git a/app/src/main/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserActivityPresenter.kt index fc5a23f71b8..9b1d7aab670 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserActivityPresenter.kt @@ -3,16 +3,23 @@ package org.oppia.android.app.devoptions.mathexpressionparser import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [MathExpressionParserActivity]. */ @ActivityScope class MathExpressionParserActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { /** Called when [MathExpressionParserActivity] is created. Handles UI for the activity. */ fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) activity.setContentView(R.layout.math_expression_parser_activity) diff --git a/app/src/main/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserFragmentPresenter.kt index 8a309279e8e..4242cadb2fa 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserFragmentPresenter.kt @@ -6,13 +6,18 @@ import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import org.oppia.android.app.databinding.databinding.MathExpressionParserFragmentBinding +import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [MathExpressionParserFragment]. */ class MathExpressionParserFragmentPresenter @Inject constructor( private val activity: AppCompatActivity, private val fragment: Fragment, - private val viewModel: MathExpressionParserViewModel + private val viewModel: MathExpressionParserViewModel, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { /** Called when [MathExpressionParserFragment] is created. Handles UI for the fragment. */ fun handleCreateView( @@ -34,6 +39,13 @@ class MathExpressionParserFragmentPresenter @Inject constructor( viewModel = this@MathExpressionParserFragmentPresenter.viewModel } viewModel.initialize(binding.mathExpressionParseResultTextView) + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.mathExpressionParserToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } return binding.root } } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/platformparameters/PlatformParametersActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/platformparameters/PlatformParametersActivityPresenter.kt index fdda1abd1e5..5336e900942 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/platformparameters/PlatformParametersActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/platformparameters/PlatformParametersActivityPresenter.kt @@ -3,15 +3,22 @@ package org.oppia.android.app.devoptions.platformparameters import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [PlatformParametersActivity]. */ @ActivityScope class PlatformParametersActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { /** Called when [PlatformParametersActivity] is created. Handles UI for the activity. */ fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) activity.setContentView(R.layout.platform_parameters_activity) diff --git a/app/src/main/java/org/oppia/android/app/devoptions/platformparameters/PlatformParametersFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/platformparameters/PlatformParametersFragmentPresenter.kt index f27816a0d18..d3f95e83411 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/platformparameters/PlatformParametersFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/platformparameters/PlatformParametersFragmentPresenter.kt @@ -25,13 +25,16 @@ import org.oppia.android.app.model.SyncStatus import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.splash.SplashActivity import org.oppia.android.app.translation.AppLanguageResourceHandler +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.app.view.models.R import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.platformparameter.PlatformParameterControllerDebugImpl import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.platformparameter.EnableEdgeToEdge import javax.inject.Inject import kotlin.system.exitProcess +import org.oppia.android.util.platformparameter.PlatformParameterValue as PlatformParameterConfigValue /** Tag for displaying [AppRestartDialogFragment]. */ const val TAG_PLATFORM_PARAMETER_RESTART_DIALOG = "PLATFORM_PARAMETER_RESTART_DIALOG_TAG" @@ -49,7 +52,9 @@ class PlatformParametersFragmentPresenter @Inject constructor( resourceHandler: AppLanguageResourceHandler, private val oppiaLogger: OppiaLogger, private val platformParameterControllerDebugImpl: PlatformParameterControllerDebugImpl, - private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory + private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterConfigValue ) { private lateinit var binding: PlatformParametersFragmentBinding private lateinit var linearLayoutManager: LinearLayoutManager @@ -107,6 +112,14 @@ class PlatformParametersFragmentPresenter @Inject constructor( } } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.platformParametersToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + return binding.root } diff --git a/app/src/main/java/org/oppia/android/app/devoptions/vieweventlogs/ViewEventLogsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/vieweventlogs/ViewEventLogsActivityPresenter.kt index 202b36b5479..448c6b6d12f 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/vieweventlogs/ViewEventLogsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/vieweventlogs/ViewEventLogsActivityPresenter.kt @@ -3,15 +3,22 @@ package org.oppia.android.app.devoptions.vieweventlogs import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [ViewEventLogsActivity]. */ @ActivityScope class ViewEventLogsActivityPresenter @Inject constructor( - private val activity: AppCompatActivity + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.supportActionBar?.setDisplayHomeAsUpEnabled(true) activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_arrow_back_white_24dp) activity.setContentView(R.layout.view_event_logs_activity) diff --git a/app/src/main/java/org/oppia/android/app/devoptions/vieweventlogs/ViewEventLogsFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/devoptions/vieweventlogs/ViewEventLogsFragmentPresenter.kt index 76a5fe3928f..2931912de07 100644 --- a/app/src/main/java/org/oppia/android/app/devoptions/vieweventlogs/ViewEventLogsFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/devoptions/vieweventlogs/ViewEventLogsFragmentPresenter.kt @@ -10,6 +10,10 @@ import org.oppia.android.app.databinding.databinding.ViewEventLogsEventLogItemVi import org.oppia.android.app.databinding.databinding.ViewEventLogsFragmentBinding import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.recyclerview.BindableAdapter +import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [ViewEventLogsFragment]. */ @@ -18,7 +22,8 @@ class ViewEventLogsFragmentPresenter @Inject constructor( private val activity: AppCompatActivity, private val fragment: Fragment, private val viewEventLogsViewModel: ViewEventLogsViewModel, - private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory + private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private lateinit var binding: ViewEventLogsFragmentBinding @@ -52,6 +57,14 @@ class ViewEventLogsFragmentPresenter @Inject constructor( adapter = bindingAdapter } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.viewEventLogsToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } + return binding.root } diff --git a/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt index 364c2f1758c..7bcb0eff6c6 100644 --- a/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/drawer/NavigationDrawerFragmentPresenter.kt @@ -34,7 +34,7 @@ import org.oppia.android.app.options.OptionsActivity import org.oppia.android.app.profileprogress.ProfileProgressActivity import org.oppia.android.app.topic.TopicActivity import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.domain.topic.TopicController diff --git a/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt index 5484fe45b63..693648d9a15 100644 --- a/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/HelpActivityPresenter.kt @@ -23,7 +23,7 @@ import org.oppia.android.app.model.PolicyPage import org.oppia.android.app.policies.PoliciesFragment import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.extensions.putProto import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue diff --git a/app/src/main/java/org/oppia/android/app/help/faq/FAQListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/faq/FAQListActivityPresenter.kt index 34e6bf136f8..1abda3bc8e0 100644 --- a/app/src/main/java/org/oppia/android/app/help/faq/FAQListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/faq/FAQListActivityPresenter.kt @@ -7,7 +7,7 @@ import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.FaqListActivityBinding import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/help/faq/faqsingle/FAQSingleActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/faq/faqsingle/FAQSingleActivityPresenter.kt index 1b98bad88b3..e377f05ddbf 100644 --- a/app/src/main/java/org/oppia/android/app/help/faq/faqsingle/FAQSingleActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/faq/faqsingle/FAQSingleActivityPresenter.kt @@ -8,7 +8,7 @@ import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.FaqSingleActivityBinding import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.gcsresource.DefaultResourceBucketName import org.oppia.android.util.parser.html.HtmlParser import org.oppia.android.util.platformparameter.EnableEdgeToEdge diff --git a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListActivityPresenter.kt index c2df9af0f29..5756829f77d 100644 --- a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListActivityPresenter.kt @@ -6,7 +6,7 @@ import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.LicenseListActivityBinding import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerActivityPresenter.kt index 8e92b7f10ff..d56ba8c2bec 100644 --- a/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerActivityPresenter.kt @@ -6,7 +6,7 @@ import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.LicenseTextViewerActivityBinding import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.accessibility.AccessibilityService import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue diff --git a/app/src/main/java/org/oppia/android/app/help/thirdparty/ThirdPartyDependencyListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/help/thirdparty/ThirdPartyDependencyListActivityPresenter.kt index c6b0759565d..50853c49355 100644 --- a/app/src/main/java/org/oppia/android/app/help/thirdparty/ThirdPartyDependencyListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/help/thirdparty/ThirdPartyDependencyListActivityPresenter.kt @@ -6,7 +6,7 @@ import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.ThirdPartyDependencyListActivityBinding import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt index 37e0bbcd55e..4cbabb4dbed 100644 --- a/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/home/HomeActivityPresenter.kt @@ -9,7 +9,7 @@ import org.oppia.android.app.drawer.NavigationDrawerFragment import org.oppia.android.app.spotlight.SpotlightFragment import org.oppia.android.app.spotlight.SpotlightManager import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedActivityPresenter.kt index 9537f787646..13ee8cd48f8 100644 --- a/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedActivityPresenter.kt @@ -8,7 +8,7 @@ import org.oppia.android.app.model.RecentlyPlayedActivityParams import org.oppia.android.app.model.RecentlyPlayedActivityTitle import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsActivityPresenter.kt index c8657e88092..862cd8edf31 100644 --- a/app/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsActivityPresenter.kt @@ -3,12 +3,21 @@ package org.oppia.android.app.mydownloads import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [MyDownloadsActivity]. */ @ActivityScope -class MyDownloadsActivityPresenter @Inject constructor(private val activity: AppCompatActivity) { +class MyDownloadsActivityPresenter @Inject constructor( + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue +) { fun handleOnCreate() { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.my_downloads_activity) if (getMyDownloadsFragment() == null) { activity.supportFragmentManager.beginTransaction().add( diff --git a/app/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsFragmentPresenter.kt index b8e0fa47f31..37d2709d74b 100644 --- a/app/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsFragmentPresenter.kt @@ -3,6 +3,7 @@ package org.oppia.android.app.mydownloads import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.tabs.TabLayout @@ -11,13 +12,18 @@ import org.oppia.android.app.databinding.databinding.MyDownloadsFragmentBinding import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [MyDownloadsFragment]. */ @FragmentScope class MyDownloadsFragmentPresenter @Inject constructor( + private val activity: AppCompatActivity, private val fragment: Fragment, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? { val binding = MyDownloadsFragmentBinding.inflate( @@ -37,6 +43,13 @@ class MyDownloadsFragmentPresenter @Inject constructor( R.id.my_downloads_tabs_viewpager ) as ViewPager2 setUpViewPager(tabLayout, viewPager) + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + binding.myDownloadsToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } return binding.root } diff --git a/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivityPresenter.kt index 7b6cc205b61..395a0d9d9e0 100644 --- a/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivityPresenter.kt @@ -3,7 +3,7 @@ package org.oppia.android.app.ongoingtopiclist import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListFragmentPresenter.kt index 5abe9313f5a..250f972acf8 100644 --- a/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListFragmentPresenter.kt @@ -10,7 +10,7 @@ import org.oppia.android.app.databinding.databinding.OngoingTopicItemBinding import org.oppia.android.app.databinding.databinding.OngoingTopicListFragmentBinding import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/options/AppLanguageActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/options/AppLanguageActivityPresenter.kt index 192c6cbdd7e..c3669de78a8 100644 --- a/app/src/main/java/org/oppia/android/app/options/AppLanguageActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/AppLanguageActivityPresenter.kt @@ -6,7 +6,7 @@ import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.AppLanguageActivityBinding import org.oppia.android.app.model.OppiaLanguage import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/options/AudioLanguageActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/options/AudioLanguageActivityPresenter.kt index 810446a168e..ea17efc0b4d 100644 --- a/app/src/main/java/org/oppia/android/app/options/AudioLanguageActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/AudioLanguageActivityPresenter.kt @@ -11,7 +11,7 @@ import org.oppia.android.app.model.AudioLanguageActivityParams import org.oppia.android.app.model.AudioLanguageActivityResultBundle import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.extensions.putProtoExtra import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue diff --git a/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt index 8cc1b20ff98..7cca2947904 100644 --- a/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/OptionsActivityPresenter.kt @@ -14,7 +14,7 @@ import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.model.OppiaLanguage import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivityPresenter.kt index 1123185cb8e..b50b89a8d01 100644 --- a/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivityPresenter.kt @@ -5,7 +5,7 @@ import androidx.appcompat.widget.Toolbar import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt index 45a99a3dc49..8b63b2b3f10 100644 --- a/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt @@ -33,8 +33,8 @@ import org.oppia.android.app.survey.TAG_SURVEY_WELCOME_DIALOG import org.oppia.android.app.topic.TopicActivity import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.FontScaleConfigurationUtil +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.exploration.ExplorationDataController import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.oppialogger.analytics.LearnerAnalyticsLogger diff --git a/app/src/main/java/org/oppia/android/app/policies/PoliciesActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/policies/PoliciesActivityPresenter.kt index fedd2260c84..4261607178d 100644 --- a/app/src/main/java/org/oppia/android/app/policies/PoliciesActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/policies/PoliciesActivityPresenter.kt @@ -8,7 +8,7 @@ import org.oppia.android.app.model.PoliciesFragmentArguments import org.oppia.android.app.model.PolicyPage import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/profile/AddProfileActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/profile/AddProfileActivityPresenter.kt index 65ea4955df0..78dd938a2d6 100644 --- a/app/src/main/java/org/oppia/android/app/profile/AddProfileActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profile/AddProfileActivityPresenter.kt @@ -28,8 +28,8 @@ import org.oppia.android.app.model.AddProfileActivityParams import org.oppia.android.app.profile.AddProfileActivity.Companion.ADD_PROFILE_ACTIVITY_PARAMS_KEY import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.TextInputEditTextHelper.Companion.onTextChanged +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData diff --git a/app/src/main/java/org/oppia/android/app/profile/AdminAuthActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/profile/AdminAuthActivityPresenter.kt index 8fb4fd57023..7bd3ef15c40 100644 --- a/app/src/main/java/org/oppia/android/app/profile/AdminAuthActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profile/AdminAuthActivityPresenter.kt @@ -12,8 +12,8 @@ import org.oppia.android.app.model.AdminAuthActivityParams import org.oppia.android.app.profile.AdminAuthActivity.Companion.ADMIN_AUTH_ACTIVITY_PARAMS_KEY import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.TextInputEditTextHelper.Companion.onTextChanged +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData diff --git a/app/src/main/java/org/oppia/android/app/profile/AdminPinActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/profile/AdminPinActivityPresenter.kt index e08f7ffce46..96d7a97acc6 100644 --- a/app/src/main/java/org/oppia/android/app/profile/AdminPinActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profile/AdminPinActivityPresenter.kt @@ -14,8 +14,8 @@ import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.profile.AdminPinActivity.Companion.ADMIN_PIN_ACTIVITY_PARAMS_KEY import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.TextInputEditTextHelper.Companion.onTextChanged +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData diff --git a/app/src/main/java/org/oppia/android/app/profile/PinPasswordActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/profile/PinPasswordActivityPresenter.kt index 86596080548..8281157e013 100644 --- a/app/src/main/java/org/oppia/android/app/profile/PinPasswordActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profile/PinPasswordActivityPresenter.kt @@ -17,8 +17,8 @@ import org.oppia.android.app.onboarding.IntroActivity import org.oppia.android.app.profile.PinPasswordActivity.Companion.PIN_PASSWORD_ACTIVITY_PARAMS_KEY import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.TextInputEditTextHelper.Companion.onTextChanged +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.app.utility.lifecycle.LifecycleSafeTimerFactory import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.accessibility.AccessibilityService diff --git a/app/src/main/java/org/oppia/android/app/profileprogress/ProfilePictureActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/profileprogress/ProfilePictureActivityPresenter.kt index f1ae9257ebe..47f510abc0d 100644 --- a/app/src/main/java/org/oppia/android/app/profileprogress/ProfilePictureActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profileprogress/ProfilePictureActivityPresenter.kt @@ -13,7 +13,7 @@ import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.model.Profile import org.oppia.android.app.model.ProfileAvatar import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.data.AsyncResult @@ -66,7 +66,7 @@ class ProfilePictureActivityPresenter @Inject constructor( EdgeToEdgeHelper.applyToAppBarLayout( activity, toolbar, - R.color.component_color_shared_activity_status_bar_color + R.color.component_color_shared_profile_status_bar_color ) } } diff --git a/app/src/main/java/org/oppia/android/app/profileprogress/ProfileProgressActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/profileprogress/ProfileProgressActivityPresenter.kt index 6cf540916de..565b1b40c78 100644 --- a/app/src/main/java/org/oppia/android/app/profileprogress/ProfileProgressActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/profileprogress/ProfileProgressActivityPresenter.kt @@ -7,7 +7,7 @@ import androidx.appcompat.widget.Toolbar import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue diff --git a/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivityPresenter.kt index 3a96fe1498b..2cdae466dbf 100644 --- a/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivityPresenter.kt @@ -13,8 +13,8 @@ import org.oppia.android.app.model.Profile import org.oppia.android.app.model.ReadingTextSize import org.oppia.android.app.player.exploration.DefaultFontSizeStateListener import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.FontScaleConfigurationUtil +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.data.AsyncResult diff --git a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileEditActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileEditActivityPresenter.kt index 748e21324f5..ee14fc30254 100644 --- a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileEditActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileEditActivityPresenter.kt @@ -7,7 +7,7 @@ import androidx.appcompat.widget.Toolbar import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.model.ProfileEditActivityParams import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.extensions.getProtoExtra import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue diff --git a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListActivityPresenter.kt index c789889c517..2a023cedc0a 100644 --- a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListActivityPresenter.kt @@ -4,7 +4,7 @@ import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListFragmentPresenter.kt index 1dd000bd639..5b73ebee72b 100644 --- a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileListFragmentPresenter.kt @@ -12,7 +12,7 @@ import org.oppia.android.app.fragment.FragmentScope import org.oppia.android.app.model.Profile import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileRenameActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileRenameActivityPresenter.kt index df3d816ef45..17a0c30bf46 100644 --- a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileRenameActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileRenameActivityPresenter.kt @@ -5,7 +5,7 @@ import androidx.databinding.DataBindingUtil import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.databinding.databinding.ProfileRenameActivityBinding import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileResetPinActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileResetPinActivityPresenter.kt index b98b9a013d0..7f2f9978c09 100644 --- a/app/src/main/java/org/oppia/android/app/settings/profile/ProfileResetPinActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/settings/profile/ProfileResetPinActivityPresenter.kt @@ -7,7 +7,7 @@ import org.oppia.android.app.databinding.databinding.ProfileResetPinActivityBind import org.oppia.android.app.model.ProfileResetPinActivityParams import org.oppia.android.app.settings.profile.ProfileResetPinActivity.Companion.PROFILE_RESET_PIN_ACTIVITY_PARAMS_KEY import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.util.extensions.getProtoExtra import org.oppia.android.util.platformparameter.EnableEdgeToEdge diff --git a/app/src/main/java/org/oppia/android/app/story/StoryActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/story/StoryActivityPresenter.kt index af7e0056604..5c984926cca 100644 --- a/app/src/main/java/org/oppia/android/app/story/StoryActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/story/StoryActivityPresenter.kt @@ -3,7 +3,7 @@ package org.oppia.android.app.story import androidx.appcompat.app.AppCompatActivity import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject diff --git a/app/src/main/java/org/oppia/android/app/story/StoryFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/story/StoryFragmentPresenter.kt index 5b9f7c13ca1..fb64387c5d8 100644 --- a/app/src/main/java/org/oppia/android/app/story/StoryFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/story/StoryFragmentPresenter.kt @@ -32,7 +32,7 @@ import org.oppia.android.app.story.storyitemviewmodel.StoryItemViewModel import org.oppia.android.app.topic.RouteToResumeLessonListener import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.exploration.ExplorationDataController import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.oppialogger.analytics.AnalyticsController diff --git a/app/src/main/java/org/oppia/android/app/survey/SurveyActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/survey/SurveyActivityPresenter.kt index 6f1e50d2c1d..c1739c1521f 100644 --- a/app/src/main/java/org/oppia/android/app/survey/SurveyActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/survey/SurveyActivityPresenter.kt @@ -9,7 +9,10 @@ import org.oppia.android.app.model.LegacyProfileId import org.oppia.android.app.model.SurveyFragmentArguments import org.oppia.android.app.survey.SurveyFragment.Companion.SURVEY_FRAGMENT_ARGUMENTS_KEY import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.extensions.putProto +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.profile.CurrentUserProfileIdIntentDecorator.decorateWithUserProfileId import javax.inject.Inject @@ -17,7 +20,10 @@ private const val TAG_SURVEY_FRAGMENT = "TAG_SURVEY_FRAGMENT" /** The Presenter for [SurveyActivity]. */ @ActivityScope -class SurveyActivityPresenter @Inject constructor(private val activity: AppCompatActivity) { +class SurveyActivityPresenter @Inject constructor( + private val activity: AppCompatActivity, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue +) { private lateinit var binding: SurveyActivityBinding fun handleOnCreate( @@ -25,6 +31,9 @@ class SurveyActivityPresenter @Inject constructor(private val activity: AppCompa topicId: String, explorationId: String ) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } binding = DataBindingUtil.setContentView(activity, R.layout.survey_activity) binding.apply { lifecycleOwner = activity diff --git a/app/src/main/java/org/oppia/android/app/survey/SurveyFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/survey/SurveyFragmentPresenter.kt index bd3844c21bf..c8736c0b22a 100644 --- a/app/src/main/java/org/oppia/android/app/survey/SurveyFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/survey/SurveyFragmentPresenter.kt @@ -28,11 +28,15 @@ import org.oppia.android.app.survey.surveyitemviewmodel.NpsItemsViewModel import org.oppia.android.app.survey.surveyitemviewmodel.SurveyAnswerItemViewModel import org.oppia.android.app.survey.surveyitemviewmodel.UserTypeItemsViewModel import org.oppia.android.app.translation.AppLanguageResourceHandler +import org.oppia.android.app.ui.R +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.oppialogger.analytics.AnalyticsController import org.oppia.android.domain.survey.SurveyProgressController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import javax.inject.Inject /** The presenter for [SurveyFragment]. */ @@ -44,7 +48,8 @@ class SurveyFragmentPresenter @Inject constructor( private val surveyViewModel: SurveyViewModel, private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory, private val resourceHandler: AppLanguageResourceHandler, - private val analyticsController: AnalyticsController + private val analyticsController: AnalyticsController, + @EnableEdgeToEdge private val enableEdgeToEdge: PlatformParameterValue ) { private val ephemeralQuestionLiveData: LiveData> by lazy { surveyProgressController.getCurrentQuestion().toLiveData() @@ -87,6 +92,13 @@ class SurveyFragmentPresenter @Inject constructor( val dialogFragment = ExitSurveyConfirmationDialogFragment.newInstance(profileId) dialogFragment.showNow(fragment.childFragmentManager, TAG_EXIT_SURVEY_CONFIRMATION_DIALOG) } + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + surveyToolbar, + R.color.component_color_shared_activity_status_bar_color + ) + } binding.surveyAnswersRecyclerView.apply { adapter = createRecyclerViewAdapter() diff --git a/app/src/main/java/org/oppia/android/app/topic/TopicActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/TopicActivityPresenter.kt index e680305694c..58123b10db7 100755 --- a/app/src/main/java/org/oppia/android/app/topic/TopicActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/TopicActivityPresenter.kt @@ -8,7 +8,7 @@ import org.oppia.android.app.model.TopicFragmentArguments import org.oppia.android.app.spotlight.SpotlightFragment import org.oppia.android.app.spotlight.SpotlightManager import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.util.extensions.putProto import org.oppia.android.util.platformparameter.EnableEdgeToEdge import org.oppia.android.util.platformparameter.PlatformParameterValue diff --git a/app/src/main/java/org/oppia/android/app/topic/TopicFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/TopicFragmentPresenter.kt index 8f2625146df..7369dfcd311 100644 --- a/app/src/main/java/org/oppia/android/app/topic/TopicFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/TopicFragmentPresenter.kt @@ -18,7 +18,7 @@ import org.oppia.android.app.spotlight.SpotlightShape import org.oppia.android.app.spotlight.SpotlightTarget import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.oppialogger.analytics.AnalyticsController import org.oppia.android.util.accessibility.AccessibilityService diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt index dfa2181965d..fc76d424c45 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt @@ -20,8 +20,8 @@ import org.oppia.android.app.player.exploration.DefaultFontSizeStateListener import org.oppia.android.app.player.exploration.TAG_HINTS_AND_SOLUTION_DIALOG import org.oppia.android.app.topic.questionplayer.QuestionPlayerActivity.Companion.QUESTION_PLAYER_ACTIVITY_PARAMS_KEY import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.FontScaleConfigurationUtil +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.profile.ProfileManagementController import org.oppia.android.domain.question.QuestionTrainingController diff --git a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt index 6924bfc0075..b70a7158bd7 100644 --- a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt @@ -18,8 +18,8 @@ import org.oppia.android.app.options.OptionsActivity import org.oppia.android.app.player.exploration.BottomSheetOptionsMenu import org.oppia.android.app.player.exploration.DefaultFontSizeStateListener import org.oppia.android.app.ui.R -import org.oppia.android.app.utility.EdgeToEdgeHelper import org.oppia.android.app.utility.FontScaleConfigurationUtil +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.oppialogger.analytics.AnalyticsController import org.oppia.android.domain.profile.ProfileManagementController @@ -91,7 +91,7 @@ class RevisionCardActivityPresenter @Inject constructor( EdgeToEdgeHelper.applyToAppBarLayout( activity, revisionCardToolbar, - R.color.component_color_shared_activity_status_bar_color + R.color.component_color_revision_card_activity_status_bar_color ) } activity.supportActionBar?.setDisplayShowTitleEnabled(false) diff --git a/app/src/main/java/org/oppia/android/app/topic/studyguide/StudyGuideActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/studyguide/StudyGuideActivityPresenter.kt index 27e8c03b456..12ffb95a589 100644 --- a/app/src/main/java/org/oppia/android/app/topic/studyguide/StudyGuideActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/studyguide/StudyGuideActivityPresenter.kt @@ -19,6 +19,7 @@ import org.oppia.android.app.player.exploration.DefaultFontSizeStateListener import org.oppia.android.app.topic.revisioncard.ReturnToTopicClickListener import org.oppia.android.app.ui.R import org.oppia.android.app.utility.FontScaleConfigurationUtil +import org.oppia.android.app.utility.edgetoedge.EdgeToEdgeHelper import org.oppia.android.domain.oppialogger.OppiaLogger import org.oppia.android.domain.oppialogger.analytics.AnalyticsController import org.oppia.android.domain.profile.ProfileManagementController @@ -27,6 +28,8 @@ import org.oppia.android.domain.translation.TranslationController import org.oppia.android.util.accessibility.AccessibilityService import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import org.oppia.android.util.platformparameter.EnableEdgeToEdge +import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.profile.toProfileIdPreservingZero import javax.inject.Inject @@ -40,6 +43,8 @@ class StudyGuideActivityPresenter @Inject constructor( private val translationController: TranslationController, private val profileManagementController: ProfileManagementController, private val fontScaleConfigurationUtil: FontScaleConfigurationUtil, + @EnableEdgeToEdge + private val enableEdgeToEdge: PlatformParameterValue ) { @Inject lateinit var accessibilityService: AccessibilityService @@ -58,6 +63,9 @@ class StudyGuideActivityPresenter @Inject constructor( subtopicIndex: Int, subtopicListSize: Int ) { + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.enableEdgeToEdgeDispatch(activity) + } activity.setContentView(R.layout.study_guide_activity) this.profileId = profileId this.topicId = topicId @@ -73,6 +81,13 @@ class StudyGuideActivityPresenter @Inject constructor( studyGuideToolbar = activity.findViewById(R.id.study_guide_toolbar) studyGuideToolbarTitle = activity.findViewById(R.id.study_guide_toolbar_title) activity.setSupportActionBar(studyGuideToolbar) + if (enableEdgeToEdge.value) { + EdgeToEdgeHelper.applyToAppBarLayout( + activity, + studyGuideToolbar, + R.color.component_color_revision_card_activity_status_bar_color + ) + } activity.supportActionBar?.setDisplayShowTitleEnabled(false) studyGuideToolbar.setNavigationOnClickListener { diff --git a/app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt b/app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt deleted file mode 100644 index 0526a17da4d..00000000000 --- a/app/src/main/java/org/oppia/android/app/utility/EdgeToEdgeHelper.kt +++ /dev/null @@ -1,220 +0,0 @@ -package org.oppia.android.app.utility - -import android.os.Build -import android.view.View -import android.widget.LinearLayout -import androidx.annotation.ColorRes -import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.widget.Toolbar -import androidx.core.content.ContextCompat -import androidx.core.graphics.Insets -import androidx.core.view.ViewCompat -import androidx.core.view.WindowCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updatePadding - -/** - * Utility that dispatches edge-to-edge window insets to activity toolbars and the navigation - * drawer while the `EnableEdgeToEdge` platform parameter gates the Android 15 rollout. - */ -object EdgeToEdgeHelper { - /** - * Enables manual edge-to-edge window dispatch for [activity]. Must be called before - * [AppCompatActivity.setContentView]. - */ - fun enableEdgeToEdgeDispatch(activity: AppCompatActivity) { - WindowCompat.setDecorFitsSystemWindows(activity.window, false) - } - - /** - * Applies edge-to-edge insets for activities whose root vertical `LinearLayout` directly - * contains the [toolbar] (e.g. Home, Options). Inserts a status-bar spacer colored with - * [statusBarColorRes] at the top of that `LinearLayout`. If the parent is not a `LinearLayout` - * (e.g. `ConstraintLayout` in AdminPin / sw600dp AdminControls), pads the toolbar directly. - */ - fun applyToToolbarContainer( - activity: AppCompatActivity, - toolbar: Toolbar, - @ColorRes statusBarColorRes: Int - ) { - val parent = toolbar.parent as View - if (parent !is LinearLayout) { - applyAsTopBar(activity, toolbar, statusBarColorRes) - return - } - val contentLayout: LinearLayout = parent - val spacer = createStatusBarSpacer(activity, statusBarColorRes) - contentLayout.addView(spacer, 0) - - ViewCompat.setOnApplyWindowInsetsListener(spacer) { view, insets -> - val bars = insets.systemBarsWithCutout() - view.layoutParams.height = bars.top - view.requestLayout() - insets - } - ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> - val bars = insets.systemBarsWithCutout() - view.updatePadding(left = bars.left, right = bars.right, bottom = bars.bottom) - insets - } - - disableDecorAncestorFits(activity) - applyRootInsetsIfAvailable(contentLayout) { bars -> - spacer.layoutParams.height = bars.top - spacer.requestLayout() - contentLayout.updatePadding(left = bars.left, right = bars.right, bottom = bars.bottom) - } - disableNavBarContrast(activity) - } - - /** - * Applies edge-to-edge insets for activities where the [toolbar] is wrapped in an app-bar - * `LinearLayout` or `AppBarLayout` (e.g. AppLanguage, FAQ). Paints the wrapper with - * [statusBarColorRes] and adds top padding equal to the status-bar inset; cutout-horizontal - * and navigation-bar-bottom padding is applied to `android.R.id.content`. Same fallback as - * [applyToToolbarContainer] when the parent is not a `LinearLayout`. - */ - fun applyToAppBarLayout( - activity: AppCompatActivity, - toolbar: Toolbar, - @ColorRes statusBarColorRes: Int - ) { - val parent = toolbar.parent as View - if (parent !is LinearLayout) { - applyAsTopBar(activity, toolbar, statusBarColorRes) - return - } - val appBarLayout: LinearLayout = parent - appBarLayout.setBackgroundColor(ContextCompat.getColor(activity, statusBarColorRes)) - val contentRoot = activity.findViewById(android.R.id.content) - - ViewCompat.setOnApplyWindowInsetsListener(appBarLayout) { view, insets -> - val bars = insets.systemBarsWithCutout() - view.updatePadding(top = bars.top) - insets - } - ViewCompat.setOnApplyWindowInsetsListener(contentRoot) { view, insets -> - val bars = insets.systemBarsWithCutout() - view.setPadding(bars.left, 0, bars.right, bars.bottom) - insets - } - - disableDecorAncestorFits(activity) - applyRootInsetsIfAvailable(appBarLayout) { bars -> - appBarLayout.updatePadding(top = bars.top) - contentRoot.setPadding(bars.left, 0, bars.right, bars.bottom) - } - disableNavBarContrast(activity) - } - - /** - * Applies edge-to-edge insets for the side navigation drawer. Inserts a status-bar spacer - * colored with [statusBarColorRes] at the top of [drawerContentLayout], clears - * [navigationView]'s `fitsSystemWindows` and replaces its inset listener with a no-op so its - * default scrim does not paint over the drawer's system-bar area, and applies RTL-aware side - * and bottom padding to [drawerRoot]. - */ - fun applyToNavigationDrawer( - activity: AppCompatActivity, - drawerRoot: View, - drawerContentLayout: LinearLayout, - navigationView: View, - @ColorRes statusBarColorRes: Int - ) { - navigationView.fitsSystemWindows = false - - val spacer = createStatusBarSpacer(activity, statusBarColorRes) - drawerContentLayout.addView(spacer, 0) - ViewCompat.setOnApplyWindowInsetsListener(spacer) { view, insets -> - val bars = insets.systemBarsWithCutout() - view.layoutParams.height = bars.top - view.requestLayout() - insets - } - - ViewCompat.setOnApplyWindowInsetsListener(navigationView) { _, insets -> insets } - - ViewCompat.setOnApplyWindowInsetsListener(drawerRoot) { view, insets -> - val bars = insets.systemBarsWithCutout() - val isRtl = ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL - view.updatePadding( - left = if (isRtl) 0 else bars.left, - right = if (isRtl) bars.right else 0, - bottom = bars.bottom - ) - insets - } - } - - // Used when the toolbar's parent is not a LinearLayout (e.g. ConstraintLayout). A spacer - // at index 0 doesn't work without constraints, so paint the toolbar itself and pad its top - // by the status-bar inset. Cutout / nav-bar padding goes on android.R.id.content. - private fun applyAsTopBar( - activity: AppCompatActivity, - topBar: View, - @ColorRes statusBarColorRes: Int - ) { - topBar.setBackgroundColor(ContextCompat.getColor(activity, statusBarColorRes)) - val contentRoot = activity.findViewById(android.R.id.content) - - ViewCompat.setOnApplyWindowInsetsListener(topBar) { view, insets -> - val bars = insets.systemBarsWithCutout() - view.updatePadding(top = bars.top) - insets - } - ViewCompat.setOnApplyWindowInsetsListener(contentRoot) { view, insets -> - val bars = insets.systemBarsWithCutout() - view.setPadding(bars.left, 0, bars.right, bars.bottom) - insets - } - - disableDecorAncestorFits(activity) - applyRootInsetsIfAvailable(topBar) { bars -> - topBar.updatePadding(top = bars.top) - contentRoot.setPadding(bars.left, 0, bars.right, bars.bottom) - } - disableNavBarContrast(activity) - } - - private fun createStatusBarSpacer( - activity: AppCompatActivity, - @ColorRes statusBarColorRes: Int - ): View = View(activity).apply { - setBackgroundColor(ContextCompat.getColor(activity, statusBarColorRes)) - // Explicit 0-height; default is MATCH_PARENT which paints the whole screen until the inset - // listener resizes the spacer. - layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0) - } - - private fun WindowInsetsCompat.systemBarsWithCutout(): Insets = getInsets( - WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() - ) - - // AppCompat Bridge themes re-apply status-bar top padding via legacy fitsSystemWindows - // dispatch on ancestors like FitWindowsLinearLayout; clearing those flags avoids double-counting - // the top inset. - private fun disableDecorAncestorFits(activity: AppCompatActivity) { - val contentRoot = activity.findViewById(android.R.id.content) - var ancestor: View? = contentRoot.parent as? View - while (ancestor != null) { - if (ancestor.fitsSystemWindows) { - ancestor.fitsSystemWindows = false - ancestor.setPadding(0, 0, 0, 0) - } - ancestor = ancestor.parent as? View - } - } - - // On activity-recreate the dispatch listener may not fire on the first frame; seed the spacer - // and padding synchronously from getRootWindowInsets to avoid a momentary full-screen flash. - private fun applyRootInsetsIfAvailable(view: View, apply: (Insets) -> Unit) { - val rootInsets = ViewCompat.getRootWindowInsets(view) ?: return - apply(rootInsets.systemBarsWithCutout()) - } - - private fun disableNavBarContrast(activity: AppCompatActivity) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - activity.window.isNavigationBarContrastEnforced = false - } - } -} diff --git a/app/src/main/java/org/oppia/android/app/utility/edgetoedge/BUILD.bazel b/app/src/main/java/org/oppia/android/app/utility/edgetoedge/BUILD.bazel new file mode 100644 index 00000000000..485a6ec8a03 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/utility/edgetoedge/BUILD.bazel @@ -0,0 +1,17 @@ +""" +Edge-to-edge window inset handling shared by app UI flows. +""" + +load("@io_bazel_rules_kotlin//kotlin:android.bzl", "kt_android_library") + +kt_android_library( + name = "edge_to_edge_helper", + srcs = ["EdgeToEdgeHelper.kt"], + visibility = ["//app:app_visibility"], + deps = [ + "//third_party:androidx_annotation_annotation", + "//third_party:androidx_appcompat_appcompat", + "//third_party:androidx_constraintlayout_constraintlayout", + "//third_party:androidx_core_core-ktx", + ], +) diff --git a/app/src/main/java/org/oppia/android/app/utility/edgetoedge/EdgeToEdgeHelper.kt b/app/src/main/java/org/oppia/android/app/utility/edgetoedge/EdgeToEdgeHelper.kt new file mode 100644 index 00000000000..08bd1c15b01 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/utility/edgetoedge/EdgeToEdgeHelper.kt @@ -0,0 +1,358 @@ +package org.oppia.android.app.utility.edgetoedge + +import android.os.Build +import android.view.Gravity +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.LinearLayout +import androidx.annotation.ColorRes +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.Toolbar +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet +import androidx.core.content.ContextCompat +import androidx.core.graphics.Insets +import androidx.core.view.ViewCompat +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import java.util.WeakHashMap + +/** + * Utility that dispatches edge-to-edge window insets to activity toolbars and the navigation + * drawer while the `EnableEdgeToEdge` platform parameter gates the Android 15 rollout. + */ +object EdgeToEdgeHelper { + private const val STATUS_BAR_SPACER_TAG = "oppia_edge_to_edge_status_bar_spacer" + + private val initialPaddings = WeakHashMap() + private val initialMargins = WeakHashMap() + + /** + * Enables manual edge-to-edge window dispatch for [activity]. Must be called before + * [AppCompatActivity.setContentView]. + */ + fun enableEdgeToEdgeDispatch(activity: AppCompatActivity) { + WindowCompat.setDecorFitsSystemWindows(activity.window, false) + } + + /** + * Applies edge-to-edge insets for activities whose root vertical `LinearLayout` directly + * contains the [toolbar] (e.g. Home, Options). Inserts a status-bar spacer colored with + * [statusBarColorRes] at the top of that `LinearLayout`. If the parent is not a `LinearLayout` + * (e.g. `ConstraintLayout` in AdminPin / sw600dp AdminControls), adds a separately colored + * status-bar spacer without changing the toolbar's background. + */ + fun applyToToolbarContainer( + activity: AppCompatActivity, + toolbar: Toolbar, + @ColorRes statusBarColorRes: Int + ) { + val parent = toolbar.parent + if (parent !is LinearLayout) { + applyAsTopBar(activity, toolbar, statusBarColorRes) + return + } + val contentLayout = parent + val spacer = getOrCreateLinearStatusBarSpacer( + activity, contentLayout, statusBarColorRes + ) + + ViewCompat.setOnApplyWindowInsetsListener(spacer) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.layoutParams.height = bars.top + view.requestLayout() + insets + } + ViewCompat.setOnApplyWindowInsetsListener(contentLayout) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.applyInsetPadding(bars, applyLeft = true, applyRight = true, applyBottom = true) + insets + } + + disableDecorAncestorFits(activity) + applyRootInsetsIfAvailable(contentLayout) { bars -> + spacer.layoutParams.height = bars.top + spacer.requestLayout() + contentLayout.applyInsetPadding( + bars, applyLeft = true, applyRight = true, applyBottom = true + ) + } + disableNavBarContrast(activity) + } + + /** + * Applies edge-to-edge insets for activities where the [toolbar] is wrapped in an app-bar + * `LinearLayout` or `AppBarLayout` (e.g. AppLanguage, FAQ). Paints the wrapper with + * [statusBarColorRes] and adds top padding equal to the status-bar inset; cutout-horizontal + * and navigation-bar-bottom padding is applied to `android.R.id.content`. Same fallback as + * [applyToToolbarContainer] when the parent is not a `LinearLayout`. + */ + fun applyToAppBarLayout( + activity: AppCompatActivity, + toolbar: Toolbar, + @ColorRes statusBarColorRes: Int + ) { + val parent = toolbar.parent + if (parent !is LinearLayout) { + applyAsTopBar(activity, toolbar, statusBarColorRes) + return + } + val appBarLayout: LinearLayout = parent + appBarLayout.setBackgroundColor(ContextCompat.getColor(activity, statusBarColorRes)) + val contentRoot = activity.findViewById(android.R.id.content) + + ViewCompat.setOnApplyWindowInsetsListener(appBarLayout) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.applyInsetPadding(bars, applyTop = true) + insets + } + ViewCompat.setOnApplyWindowInsetsListener(contentRoot) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.applyInsetPadding(bars, applyLeft = true, applyRight = true, applyBottom = true) + insets + } + + disableDecorAncestorFits(activity) + applyRootInsetsIfAvailable(appBarLayout) { bars -> + appBarLayout.applyInsetPadding(bars, applyTop = true) + contentRoot.applyInsetPadding( + bars, applyLeft = true, applyRight = true, applyBottom = true + ) + } + disableNavBarContrast(activity) + } + + /** + * Applies edge-to-edge insets for the side navigation drawer. Inserts a status-bar spacer + * colored with [statusBarColorRes] at the top of [drawerContentLayout], clears + * [navigationView]'s `fitsSystemWindows` and replaces its inset listener with a no-op so its + * default scrim does not paint over the drawer's system-bar area, and applies RTL-aware side + * and bottom padding to [drawerRoot]. + */ + fun applyToNavigationDrawer( + activity: AppCompatActivity, + drawerRoot: View, + drawerContentLayout: LinearLayout, + navigationView: View, + @ColorRes statusBarColorRes: Int + ) { + navigationView.fitsSystemWindows = false + + val spacer = getOrCreateLinearStatusBarSpacer( + activity, drawerContentLayout, statusBarColorRes + ) + ViewCompat.setOnApplyWindowInsetsListener(spacer) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.layoutParams.height = bars.top + view.requestLayout() + insets + } + + ViewCompat.setOnApplyWindowInsetsListener(navigationView) { _, insets -> insets } + + ViewCompat.setOnApplyWindowInsetsListener(drawerRoot) { view, insets -> + val bars = insets.systemBarsWithCutout() + val isRtl = ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL + view.applyInsetPadding( + bars, + applyLeft = !isRtl, + applyRight = isRtl, + applyBottom = true + ) + insets + } + } + + // Used when the toolbar's parent is not a LinearLayout. A separate spacer preserves the + // toolbar's original color instead of turning the whole toolbar into the status-bar color. + private fun applyAsTopBar( + activity: AppCompatActivity, + topBar: View, + @ColorRes statusBarColorRes: Int + ) { + val contentRoot = activity.findViewById(android.R.id.content) + val topBarParent = topBar.parent + val spacer = if (topBarParent is ConstraintLayout && topBar.id != View.NO_ID) { + getOrCreateConstrainedStatusBarSpacer( + activity, topBarParent, topBar, statusBarColorRes + ) + } else { + getOrCreateOverlayStatusBarSpacer(activity, contentRoot, statusBarColorRes).also { + initialMargins.getOrPut(topBar) { topBar.captureMargins() } + } + } + + ViewCompat.setOnApplyWindowInsetsListener(spacer) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.layoutParams.height = bars.top + view.requestLayout() + if (topBarParent !is ConstraintLayout) { + topBar.applyTopInsetMargin(bars.top) + } + insets + } + ViewCompat.setOnApplyWindowInsetsListener(contentRoot) { view, insets -> + val bars = insets.systemBarsWithCutout() + view.applyInsetPadding(bars, applyLeft = true, applyRight = true, applyBottom = true) + insets + } + + disableDecorAncestorFits(activity) + applyRootInsetsIfAvailable(spacer) { bars -> + spacer.layoutParams.height = bars.top + spacer.requestLayout() + if (topBarParent !is ConstraintLayout) { + topBar.applyTopInsetMargin(bars.top) + } + contentRoot.applyInsetPadding( + bars, applyLeft = true, applyRight = true, applyBottom = true + ) + } + disableNavBarContrast(activity) + } + + private fun getOrCreateLinearStatusBarSpacer( + activity: AppCompatActivity, + parent: LinearLayout, + @ColorRes statusBarColorRes: Int + ): View { + val spacer = parent.findDirectStatusBarSpacer() ?: View(activity).also { + it.tag = STATUS_BAR_SPACER_TAG + it.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0) + parent.addView(it, 0) + } + spacer.setBackgroundColor(ContextCompat.getColor(activity, statusBarColorRes)) + return spacer + } + + private fun getOrCreateConstrainedStatusBarSpacer( + activity: AppCompatActivity, + parent: ConstraintLayout, + topBar: View, + @ColorRes statusBarColorRes: Int + ): View { + val spacer = parent.findDirectStatusBarSpacer() ?: View(activity).also { + it.id = View.generateViewId() + it.tag = STATUS_BAR_SPACER_TAG + it.layoutParams = ConstraintLayout.LayoutParams(0, 0) + parent.addView(it) + } + spacer.setBackgroundColor(ContextCompat.getColor(activity, statusBarColorRes)) + + val originalMargins = initialMargins.getOrPut(topBar) { topBar.captureMargins() } + ConstraintSet().apply { + clone(parent) + connect(spacer.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP) + connect(spacer.id, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START) + connect(spacer.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END) + clear(topBar.id, ConstraintSet.TOP) + connect( + topBar.id, + ConstraintSet.TOP, + spacer.id, + ConstraintSet.BOTTOM, + originalMargins.top + ) + applyTo(parent) + } + return spacer + } + + private fun getOrCreateOverlayStatusBarSpacer( + activity: AppCompatActivity, + contentRoot: View, + @ColorRes statusBarColorRes: Int + ): View { + val root = contentRoot as ViewGroup + val spacer = root.findDirectStatusBarSpacer() ?: View(activity).also { + it.tag = STATUS_BAR_SPACER_TAG + it.layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + 0, + Gravity.TOP + ) + root.addView(it) + } + spacer.setBackgroundColor(ContextCompat.getColor(activity, statusBarColorRes)) + return spacer + } + + private fun ViewGroup.findDirectStatusBarSpacer(): View? = + (0 until childCount) + .map(::getChildAt) + .firstOrNull { it.tag == STATUS_BAR_SPACER_TAG } + + private fun WindowInsetsCompat.systemBarsWithCutout(): Insets = getInsets( + WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() + ) + + // AppCompat Bridge themes re-apply status-bar top padding via legacy fitsSystemWindows + // dispatch on ancestors like FitWindowsLinearLayout; clearing those flags avoids double-counting + // the top inset. + private fun disableDecorAncestorFits(activity: AppCompatActivity) { + val contentRoot = activity.findViewById(android.R.id.content) + var ancestor: View? = contentRoot.parent as? View + while (ancestor != null) { + if (ancestor.fitsSystemWindows) { + ancestor.fitsSystemWindows = false + ancestor.setPadding(0, 0, 0, 0) + } + ancestor = ancestor.parent as? View + } + } + + // On activity-recreate the dispatch listener may not fire on the first frame; seed the spacer + // and padding synchronously from getRootWindowInsets to avoid a momentary full-screen flash. + private fun applyRootInsetsIfAvailable(view: View, apply: (Insets) -> Unit) { + val rootInsets = ViewCompat.getRootWindowInsets(view) ?: return + apply(rootInsets.systemBarsWithCutout()) + } + + private fun disableNavBarContrast(activity: AppCompatActivity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + activity.window.isNavigationBarContrastEnforced = false + } + } + + private fun View.applyInsetPadding( + insets: Insets, + applyLeft: Boolean = false, + applyTop: Boolean = false, + applyRight: Boolean = false, + applyBottom: Boolean = false + ) { + val initial = initialPaddings.getOrPut(this) { capturePadding() } + setPadding( + initial.left + if (applyLeft) insets.left else 0, + initial.top + if (applyTop) insets.top else 0, + initial.right + if (applyRight) insets.right else 0, + initial.bottom + if (applyBottom) insets.bottom else 0 + ) + } + + private fun View.applyTopInsetMargin(topInset: Int) { + val marginParams = layoutParams as? ViewGroup.MarginLayoutParams ?: return + val initial = initialMargins.getOrPut(this) { captureMargins() } + if (marginParams.topMargin != initial.top + topInset) { + marginParams.topMargin = initial.top + topInset + layoutParams = marginParams + } + } + + private fun View.capturePadding() = Padding(paddingLeft, paddingTop, paddingRight, paddingBottom) + + private fun View.captureMargins(): Margins { + val marginParams = layoutParams as? ViewGroup.MarginLayoutParams + return Margins( + marginParams?.leftMargin ?: 0, + marginParams?.topMargin ?: 0, + marginParams?.rightMargin ?: 0, + marginParams?.bottomMargin ?: 0 + ) + } + + private data class Padding(val left: Int, val top: Int, val right: Int, val bottom: Int) + + private data class Margins(val left: Int, val top: Int, val right: Int, val bottom: Int) +} diff --git a/app/src/test/java/org/oppia/android/app/utility/edgetoedge/EdgeToEdgeHelperTest.kt b/app/src/test/java/org/oppia/android/app/utility/edgetoedge/EdgeToEdgeHelperTest.kt new file mode 100644 index 00000000000..c4fcd64c21c --- /dev/null +++ b/app/src/test/java/org/oppia/android/app/utility/edgetoedge/EdgeToEdgeHelperTest.kt @@ -0,0 +1,254 @@ +package org.oppia.android.app.utility.edgetoedge + +import android.app.Application +import android.content.Context +import android.content.pm.ActivityInfo +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.LinearLayout +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.Toolbar +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet +import androidx.core.content.ContextCompat +import androidx.core.graphics.Insets +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.test.core.app.ActivityScenario +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.oppia.android.app.test.R +import org.oppia.android.app.testing.activity.TestActivity +import org.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode + +/** Tests for [EdgeToEdgeHelper]'s window-inset behavior. */ +// FunctionName: test names are conventionally named with underscores. +@Suppress("FunctionName") +@RunWith(AndroidJUnit4::class) +@Config(application = Application::class) +@LooperMode(LooperMode.Mode.PAUSED) +class EdgeToEdgeHelperTest { + private lateinit var context: Context + + @Before + fun setUp() { + context = ApplicationProvider.getApplicationContext() + TestActivity.registerActivityInfo( + context, + ActivityInfo().apply { + name = AppCompatActivity::class.java.name + packageName = context.packageName + theme = R.style.OppiaThemeWithoutActionBar + } + ) + } + + @Test + fun testApplyToToolbarContainer_twice_createsOneSpacerAndPreservesPadding() { + launchTestActivity { activity -> + val contentLayout = LinearLayout(activity).apply { + orientation = LinearLayout.VERTICAL + setPadding(1, 2, 3, 4) + } + val toolbar = Toolbar(activity).apply { setBackgroundColor(Color.RED) } + contentLayout.addView(toolbar) + activity.setContentView(contentLayout) + + EdgeToEdgeHelper.applyToToolbarContainer(activity, toolbar, STATUS_BAR_COLOR) + EdgeToEdgeHelper.applyToToolbarContainer(activity, toolbar, STATUS_BAR_COLOR) + dispatchInsets(contentLayout) + + assertThat(contentLayout.childCount).isEqualTo(2) + assertThat(contentLayout.getChildAt(1)).isSameInstanceAs(toolbar) + assertThat(contentLayout.paddingLeft).isEqualTo(16) + assertThat(contentLayout.paddingTop).isEqualTo(2) + assertThat(contentLayout.paddingRight).isEqualTo(38) + assertThat(contentLayout.paddingBottom).isEqualTo(44) + assertThat(contentLayout.getChildAt(0).layoutParams.height).isEqualTo(25) + assertThat(toolbar.backgroundColor).isEqualTo(Color.RED) + + dispatchInsets(contentLayout) + + assertThat(contentLayout.paddingLeft).isEqualTo(16) + assertThat(contentLayout.paddingRight).isEqualTo(38) + assertThat(contentLayout.paddingBottom).isEqualTo(44) + } + } + + @Test + fun testApplyToAppBarLayout_insetsPreserveExistingPaddingAndUseStatusBarColor() { + launchTestActivity { activity -> + val root = LinearLayout(activity).apply { orientation = LinearLayout.VERTICAL } + val appBarLayout = LinearLayout(activity).apply { + orientation = LinearLayout.VERTICAL + setPadding(1, 2, 3, 4) + } + val toolbar = Toolbar(activity).apply { setBackgroundColor(Color.RED) } + appBarLayout.addView(toolbar) + root.addView(appBarLayout) + activity.setContentView(root) + val contentRoot = activity.findViewById(android.R.id.content).apply { + setPadding(5, 6, 7, 8) + } + + EdgeToEdgeHelper.applyToAppBarLayout(activity, toolbar, STATUS_BAR_COLOR) + dispatchInsets(contentRoot) + dispatchInsets(appBarLayout) + + assertThat(appBarLayout.paddingLeft).isEqualTo(1) + assertThat(appBarLayout.paddingTop).isEqualTo(27) + assertThat(appBarLayout.paddingRight).isEqualTo(3) + assertThat(appBarLayout.paddingBottom).isEqualTo(4) + assertThat(contentRoot.paddingLeft).isEqualTo(20) + assertThat(contentRoot.paddingTop).isEqualTo(6) + assertThat(contentRoot.paddingRight).isEqualTo(42) + assertThat(contentRoot.paddingBottom).isEqualTo(48) + assertThat(appBarLayout.backgroundColor).isEqualTo(expectedStatusBarColor(activity)) + assertThat(toolbar.backgroundColor).isEqualTo(Color.RED) + + dispatchInsets(contentRoot) + dispatchInsets(appBarLayout) + + assertThat(appBarLayout.paddingTop).isEqualTo(27) + assertThat(contentRoot.paddingBottom).isEqualTo(48) + } + } + + @Test + fun testApplyToToolbarContainer_constraintParent_preservesToolbarColorAndConstraintsSpacer() { + launchTestActivity { activity -> + val root = ConstraintLayout(activity) + val toolbar = Toolbar(activity).apply { + id = View.generateViewId() + setBackgroundColor(Color.RED) + } + root.addView(toolbar, ConstraintLayout.LayoutParams(0, 56)) + ConstraintSet().apply { + clone(root) + connect(toolbar.id, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP) + connect(toolbar.id, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START) + connect(toolbar.id, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END) + applyTo(root) + } + activity.setContentView(root) + + EdgeToEdgeHelper.applyToToolbarContainer(activity, toolbar, STATUS_BAR_COLOR) + EdgeToEdgeHelper.applyToToolbarContainer(activity, toolbar, STATUS_BAR_COLOR) + val spacer = root.getChildAt(1) + dispatchInsets(spacer) + + assertThat(root.childCount).isEqualTo(2) + assertThat(toolbar.backgroundColor).isEqualTo(Color.RED) + assertThat(spacer.backgroundColor).isEqualTo(expectedStatusBarColor(activity)) + assertThat(spacer.layoutParams.height).isEqualTo(25) + assertThat((toolbar.layoutParams as ConstraintLayout.LayoutParams).topToBottom) + .isEqualTo(spacer.id) + } + } + + @Test + fun testApplyToToolbarContainer_genericParent_usesOverlayAndPreservesToolbarColorAndMargin() { + launchTestActivity { activity -> + val root = FrameLayout(activity) + val toolbar = Toolbar(activity).apply { setBackgroundColor(Color.RED) } + root.addView( + toolbar, + FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + 56 + ).apply { topMargin = 7 } + ) + activity.setContentView(root) + val contentRoot = activity.findViewById(android.R.id.content) + + EdgeToEdgeHelper.applyToToolbarContainer(activity, toolbar, STATUS_BAR_COLOR) + EdgeToEdgeHelper.applyToToolbarContainer(activity, toolbar, STATUS_BAR_COLOR) + val spacer = contentRoot.getChildAt(1) + dispatchInsets(spacer) + + assertThat(contentRoot.childCount).isEqualTo(2) + assertThat(toolbar.backgroundColor).isEqualTo(Color.RED) + assertThat(spacer.backgroundColor).isEqualTo(expectedStatusBarColor(activity)) + assertThat(spacer.layoutParams.height).isEqualTo(25) + assertThat((toolbar.layoutParams as FrameLayout.LayoutParams).topMargin).isEqualTo(32) + + dispatchInsets(spacer) + + assertThat((toolbar.layoutParams as FrameLayout.LayoutParams).topMargin).isEqualTo(32) + } + } + + @Test + fun testApplyToNavigationDrawer_ltrAndRtl_preservesPaddingAndCreatesOneSpacer() { + launchTestActivity { activity -> + val drawerRoot = FrameLayout(activity).apply { setPadding(1, 2, 3, 4) } + val drawerContent = LinearLayout(activity).apply { + addView(View(activity)) + } + val navigationView = View(activity).apply { fitsSystemWindows = true } + + EdgeToEdgeHelper.applyToNavigationDrawer( + activity, drawerRoot, drawerContent, navigationView, STATUS_BAR_COLOR + ) + EdgeToEdgeHelper.applyToNavigationDrawer( + activity, drawerRoot, drawerContent, navigationView, STATUS_BAR_COLOR + ) + ViewCompat.setLayoutDirection(drawerRoot, ViewCompat.LAYOUT_DIRECTION_LTR) + dispatchInsets(drawerRoot) + dispatchInsets(drawerContent) + + assertThat(drawerContent.childCount).isEqualTo(2) + assertThat(drawerContent.getChildAt(0).layoutParams.height).isEqualTo(25) + assertThat(drawerRoot.paddingLeft).isEqualTo(16) + assertThat(drawerRoot.paddingTop).isEqualTo(2) + assertThat(drawerRoot.paddingRight).isEqualTo(3) + assertThat(drawerRoot.paddingBottom).isEqualTo(44) + assertThat(navigationView.fitsSystemWindows).isFalse() + + ViewCompat.setLayoutDirection(drawerRoot, ViewCompat.LAYOUT_DIRECTION_RTL) + dispatchInsets(drawerRoot) + + assertThat(drawerRoot.paddingLeft).isEqualTo(1) + assertThat(drawerRoot.paddingRight).isEqualTo(38) + assertThat(drawerRoot.paddingBottom).isEqualTo(44) + } + } + + @Test + fun testEnableEdgeToEdgeDispatch_doesNotCrash() { + launchTestActivity(EdgeToEdgeHelper::enableEdgeToEdgeDispatch) + } + + private fun launchTestActivity(testBlock: (AppCompatActivity) -> Unit) { + ActivityScenario.launch(AppCompatActivity::class.java).use { scenario -> + scenario.onActivity(testBlock) + } + } + + private fun dispatchInsets(view: View) { + ViewCompat.dispatchApplyWindowInsets(view, TEST_INSETS) + } + + private fun expectedStatusBarColor(activity: AppCompatActivity): Int = + ContextCompat.getColor(activity, STATUS_BAR_COLOR) + + private val View.backgroundColor: Int + get() = (background as ColorDrawable).color + + private companion object { + val STATUS_BAR_COLOR = R.color.component_color_shared_activity_status_bar_color + + val TEST_INSETS: WindowInsetsCompat = WindowInsetsCompat.Builder() + .setInsets(WindowInsetsCompat.Type.systemBars(), Insets.of(10, 20, 30, 40)) + .setInsets(WindowInsetsCompat.Type.displayCutout(), Insets.of(15, 25, 35, 0)) + .build() + } +}