Skip to content

Commit 629c0db

Browse files
theayushyadav11theayushyadav11manas-yuadhiamboperesRd4dev
authored
Fixes part of #5345: Implementing Force downloading . (#5949)
Fixes part of #5345 : This pull request adds a new "Force Download" feature to the developer options, allowing developers to manually trigger a download of remote platform parameters and feature flags, and restart the app to apply changes. The implementation includes new UI elements, dialog fragments, presenter logic, and integration with existing controllers and activity lifecycle. The actual downloading implementation of platform parameter is not yet done, and will be done in future. This PR just wires the UI with the controller which can be verified by logs. **Feature Addition: Force Download Functionality** * Added a new `ForceDownloadsButtonClickListener` interface and integrated it into `DeveloperOptionsActivity`, enabling handling of force download button clicks. * Implemented `ForceDownloadDialogFragment` and its presenter to show a dialog for force downloading parameters, with options to restart the app or cancel. * Updated `DeveloperOptionsActivityPresenter` to handle force download requests and perform a full app restart if needed. **UI Enhancements** * Updated `developer_options_override_app_behaviors_view.xml` to add a force download button, divider, and related layout changes for improved organization and visibility. * Bound the button's enabled state to a new LiveData property in the view model, reflecting whether force download is currently available **Integration with ViewModels and Controllers** * Extended `DeveloperOptionsViewModel` and `DeveloperOptionsOverrideAppBehaviorsViewModel` to support force download logic and button state, integrating with `ForceDownloadParametersController`. * Registered new listener and fragment for dependency injection and activity lifecycle management. **Verification via logs** The downloading behaviour can be verified using the logs: - When download starts it logs: "Calling Force Download of remote parameters". - When download completes successfully: "Download finished successfully." - When we cancel download before being finished: "Cancelled ongoing remote parameter download" - All the logs can be verified using the TAG : ``` PlatformParameterController ``` ### Screenshot <img width="400" height="1000" alt="image" src="https://github.com/user-attachments/assets/3a74a3a5-d414-4efd-8a2b-3723c8041d96" /> ## Essential Checklist <!-- Please tick the relevant boxes by putting an "x" in them. --> - [x] The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".) - [x] Any changes to [scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets) files have their rationale included in the PR explanation. - [x] The PR follows the [style guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide). - [x] The PR does not contain any unnecessary code changes from Android Studio ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)). - [x] The PR is made from a branch that's **not** called "develop" and is up-to-date with "develop". - [x] The PR is **assigned** to the appropriate reviewers ([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)). --------- Co-authored-by: theayushyadav11 <[email protected]> Co-authored-by: Manas <[email protected]> Co-authored-by: Adhiambo Peres <[email protected]> Co-authored-by: RD Rama Devi <[email protected]>
1 parent d3d6f82 commit 629c0db

22 files changed

+540
-4
lines changed

app/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ LISTENERS = [
8181
"src/main/java/org/oppia/android/app/administratorcontrols/RouteToLearnerAnalyticsListener.kt",
8282
"src/main/java/org/oppia/android/app/administratorcontrols/RouteToProfileListListener.kt",
8383
"src/main/java/org/oppia/android/app/administratorcontrols/ShowLogoutDialogListener.kt",
84+
"src/main/java/org/oppia/android/app/devoptions/AppRestartListener.kt",
8485
"src/main/java/org/oppia/android/app/devoptions/ForceCrashButtonClickListener.kt",
86+
"src/main/java/org/oppia/android/app/devoptions/ForceDownloadRemoteParametersButtonClickListener.kt",
8587
"src/main/java/org/oppia/android/app/devoptions/RouteToFeatureFlagsListener.kt",
8688
"src/main/java/org/oppia/android/app/devoptions/RouteToForceNetworkTypeListener.kt",
8789
"src/main/java/org/oppia/android/app/devoptions/RouteToMarkChaptersCompletedListener.kt",
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.oppia.android.app.devoptions
2+
3+
/** Listener interface for handling application restart requests. */
4+
interface AppRestartListener {
5+
/** Triggers a restart of the application. */
6+
fun restartApp()
7+
}

app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivity.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ import javax.inject.Inject
2626
class DeveloperOptionsActivity :
2727
InjectableAutoLocalizedAppCompatActivity(),
2828
ForceCrashButtonClickListener,
29+
ForceDownloadRemoteParametersButtonClickListener,
2930
RouteToMarkChaptersCompletedListener,
3031
RouteToMarkStoriesCompletedListener,
3132
RouteToMarkTopicsCompletedListener,
3233
RouteToViewEventLogsListener,
3334
RouteToForceNetworkTypeListener,
3435
RouteToMathExpressionParserTestListener,
3536
RouteToFeatureFlagsListener,
36-
RouteToPlatformParametersListener {
37+
RouteToPlatformParametersListener,
38+
AppRestartListener {
3739

3840
@Inject
3941
lateinit var developerOptionsActivityPresenter: DeveloperOptionsActivityPresenter
@@ -112,4 +114,12 @@ class DeveloperOptionsActivity :
112114
override fun forceCrash() {
113115
developerOptionsActivityPresenter.forceCrash()
114116
}
117+
118+
override fun forceDownloadRemoteParameters() {
119+
developerOptionsActivityPresenter.forceDownloadRemoteParameters()
120+
}
121+
122+
override fun restartApp() {
123+
developerOptionsActivityPresenter.restartApp()
124+
}
115125
}

app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
package org.oppia.android.app.devoptions
22

3+
import android.content.Intent
34
import androidx.appcompat.app.AppCompatActivity
45
import androidx.databinding.DataBindingUtil
56
import org.oppia.android.app.activity.ActivityScope
67
import org.oppia.android.app.databinding.databinding.DeveloperOptionsActivityBinding
78
import org.oppia.android.app.drawer.NavigationDrawerFragment
9+
import org.oppia.android.app.splash.SplashActivity
810
import org.oppia.android.app.ui.R
911
import javax.inject.Inject
12+
import kotlin.system.exitProcess
13+
14+
/** Tag for displaying [ForceDownloadRemoteParametersDialogFragment]. */
15+
const val TAG_FORCE_DOWNLOAD_DIALOG = "FORCE_DOWNLOAD_DIALOG_TAG"
1016

1117
/** The presenter for [DeveloperOptionsActivity]. */
1218
@ActivityScope
@@ -58,4 +64,23 @@ class DeveloperOptionsActivityPresenter @Inject constructor(
5864
fun forceCrash(): Nothing {
5965
throw RuntimeException("Force crash occurred")
6066
}
67+
68+
/** Opens a dialog to force download remote parameters. */
69+
fun forceDownloadRemoteParameters() {
70+
val dialog = ForceDownloadRemoteParametersDialogFragment.newInstance()
71+
dialog.showNow(activity.supportFragmentManager, TAG_FORCE_DOWNLOAD_DIALOG)
72+
}
73+
74+
/** Called when restart is triggered by [ForceDownloadRemoteParametersDialogFragment]. */
75+
fun restartApp() {
76+
val intent = Intent(activity, SplashActivity::class.java).also {
77+
it.action = Intent.ACTION_MAIN
78+
it.addCategory(Intent.CATEGORY_LAUNCHER)
79+
}
80+
activity.finishAffinity()
81+
activity.startActivity(intent)
82+
// App is terminated to ensure a fresh restart and kill all the current process
83+
// so that ProcessState can be reinitialised on the fresh restart.
84+
exitProcess(0)
85+
}
6186
}

app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsViewModel.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class DeveloperOptionsViewModel @Inject constructor(
2121
private val showAllHintsAndSolutionController: ShowAllHintsAndSolutionController
2222
) {
2323
private val forceCrashButtonClickListener = activity as ForceCrashButtonClickListener
24+
private val forceDownloadRemoteParametersButtonClickListener =
25+
activity as ForceDownloadRemoteParametersButtonClickListener
2426
private val routeToMarkChaptersCompletedListener =
2527
activity as RouteToMarkChaptersCompletedListener
2628
private val routeToMarkStoriesCompletedListener =
@@ -52,6 +54,7 @@ class DeveloperOptionsViewModel @Inject constructor(
5254
DeveloperOptionsViewLogsViewModel(routeToViewEventLogsListener),
5355
DeveloperOptionsOverrideAppBehaviorsViewModel(
5456
forceCrashButtonClickListener,
57+
forceDownloadRemoteParametersButtonClickListener,
5558
routeToForceNetworkTypeListener,
5659
showAllHintsAndSolutionController,
5760
routeToFeatureFlagsListener,
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.oppia.android.app.devoptions
2+
3+
/** Listener for handling clicks on the force download button. */
4+
interface ForceDownloadRemoteParametersButtonClickListener {
5+
6+
/** Listener for handling clicks on the force download remote parameters button. */
7+
fun forceDownloadRemoteParameters()
8+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.oppia.android.app.devoptions
2+
3+
import android.app.Dialog
4+
import android.content.Context
5+
import android.os.Bundle
6+
import org.oppia.android.app.fragment.FragmentComponentImpl
7+
import org.oppia.android.app.fragment.InjectableDialogFragment
8+
import javax.inject.Inject
9+
10+
/** Dialog fragment shown for force downloading remote parameters. */
11+
class ForceDownloadRemoteParametersDialogFragment : InjectableDialogFragment() {
12+
@Inject
13+
lateinit var forceDownloadRemoteParametersDialogFragmentPresenter:
14+
ForceDownloadRemoteParametersDialogFragmentPresenter
15+
16+
companion object {
17+
/** Returns a new instance of [ForceDownloadRemoteParametersDialogFragment]. */
18+
fun newInstance(): ForceDownloadRemoteParametersDialogFragment =
19+
ForceDownloadRemoteParametersDialogFragment()
20+
}
21+
22+
override fun onAttach(context: Context) {
23+
super.onAttach(context)
24+
(fragmentComponent as FragmentComponentImpl).inject(this)
25+
}
26+
27+
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
28+
return forceDownloadRemoteParametersDialogFragmentPresenter.handleOnCreateDialog()
29+
}
30+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package org.oppia.android.app.devoptions
2+
3+
import android.app.AlertDialog
4+
import android.app.Dialog
5+
import android.view.View
6+
import androidx.appcompat.app.AppCompatActivity
7+
import androidx.core.content.ContextCompat
8+
import androidx.fragment.app.Fragment
9+
import org.oppia.android.app.databinding.databinding.ForceDownloadRemoteParametersDialogFragmentBinding
10+
import org.oppia.android.app.translation.AppLanguageResourceHandler
11+
import org.oppia.android.app.ui.R
12+
import org.oppia.android.domain.devoptions.ForceDownloadRemoteParametersController
13+
import org.oppia.android.domain.oppialogger.OppiaLogger
14+
import org.oppia.android.util.data.AsyncResult
15+
import org.oppia.android.util.data.DataProviders.Companion.toLiveData
16+
import javax.inject.Inject
17+
18+
/** Presenter for the [ForceDownloadRemoteParametersDialogFragment]. */
19+
class ForceDownloadRemoteParametersDialogFragmentPresenter @Inject constructor(
20+
private val fragment: Fragment,
21+
private val activity: AppCompatActivity,
22+
private val oppiaLogger: OppiaLogger,
23+
private val resourceHandler: AppLanguageResourceHandler,
24+
private val forceDownloadRemoteParametersParametersController:
25+
ForceDownloadRemoteParametersController
26+
) {
27+
/**
28+
* Creates a dialog to display the status of the ongoing remote parameters download and then
29+
* restart.
30+
*/
31+
fun handleOnCreateDialog(): Dialog {
32+
val binding = ForceDownloadRemoteParametersDialogFragmentBinding.inflate(
33+
activity.layoutInflater,
34+
/* parent= */ null,
35+
/* attachToRoot= */ false
36+
)
37+
binding.lifecycleOwner = fragment
38+
val appRestartInterface = activity as AppRestartListener
39+
40+
forceDownloadRemoteParametersParametersController
41+
.downloadRemoteParameters().toLiveData().observe(fragment) {
42+
if (it is AsyncResult.Success) {
43+
oppiaLogger.d(
44+
"ForceDownloadRemoteParametersDialog",
45+
"Remote parameters downloaded successfully."
46+
)
47+
handleDownloadComplete(binding)
48+
}
49+
}
50+
51+
val dialog = AlertDialog.Builder(activity, R.style.OppiaAlertDialogTheme)
52+
.setView(binding.root)
53+
.create()
54+
dialog.setCanceledOnTouchOutside(false)
55+
56+
binding.restartButton.setOnClickListener {
57+
appRestartInterface.restartApp()
58+
dialog.dismiss()
59+
}
60+
61+
binding.cancelButton.setOnClickListener {
62+
forceDownloadRemoteParametersParametersController.cancelRemoteParameterDownload()
63+
dialog.dismiss()
64+
}
65+
return dialog
66+
}
67+
68+
private fun handleDownloadComplete(binding: ForceDownloadRemoteParametersDialogFragmentBinding) {
69+
binding.cancelButton.visibility = View.GONE
70+
binding.forceDownloadMessage.text = resourceHandler
71+
.getStringInLocale(R.string.force_download_dialog_successfully_downloaded_message_text)
72+
binding.forceDownloadRestartMessage.visibility = View.VISIBLE
73+
binding.restartButton.apply {
74+
isEnabled = true
75+
setTextColor(
76+
ContextCompat.getColor(
77+
fragment.requireContext(),
78+
R.color.component_color_shared_secondary_4_text_color
79+
)
80+
)
81+
setBackgroundResource(R.drawable.state_button_primary_background)
82+
}
83+
}
84+
}

app/src/main/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/DeveloperOptionsOverrideAppBehaviorsViewModel.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.oppia.android.app.devoptions.devoptionsitemviewmodel
22

33
import androidx.databinding.ObservableField
44
import org.oppia.android.app.devoptions.ForceCrashButtonClickListener
5+
import org.oppia.android.app.devoptions.ForceDownloadRemoteParametersButtonClickListener
56
import org.oppia.android.app.devoptions.RouteToFeatureFlagsListener
67
import org.oppia.android.app.devoptions.RouteToForceNetworkTypeListener
78
import org.oppia.android.app.devoptions.RouteToPlatformParametersListener
@@ -13,6 +14,8 @@ import org.oppia.android.domain.devoptions.ShowAllHintsAndSolutionController
1314
*/
1415
class DeveloperOptionsOverrideAppBehaviorsViewModel(
1516
private val forceCrashButtonClickListener: ForceCrashButtonClickListener,
17+
private val forceDownloadRemoteParametersButtonClickListener:
18+
ForceDownloadRemoteParametersButtonClickListener,
1619
private val forceNetworkTypeListener: RouteToForceNetworkTypeListener,
1720
private val showAllHintsAndSolutionController: ShowAllHintsAndSolutionController,
1821
private val featureFlagsListener: RouteToFeatureFlagsListener,
@@ -43,6 +46,14 @@ class DeveloperOptionsOverrideAppBehaviorsViewModel(
4346
platformParametersListener.routeToPlatformParameters()
4447
}
4548

49+
/**
50+
* Initiates a request to download the remote parameters from the web server when the download
51+
* button is clicked.
52+
*/
53+
fun onForceDownloadRemoteParametersButtonClicked() {
54+
forceDownloadRemoteParametersButtonClickListener.forceDownloadRemoteParameters()
55+
}
56+
4657
/**
4758
* Called when the state of 'show all hints/solution' switch is changed by the user.
4859
* Enables or disables the feature to show all hints and solution.

app/src/main/java/org/oppia/android/app/devoptions/featureflags/FeatureFlagItemViewModel.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class FeatureFlagItemViewModel(
5858
SyncStatus.LOCAL_OVERRIDE ->
5959
resourceHandler.getStringInLocale(R.string.platform_parameter_currently_overridden_message)
6060
SyncStatus.SYNCED_FROM_SERVER ->
61+
// TODO(#5951): Replace this placeholder message with the actual server last-synced timestamps.
6162
resourceHandler.getStringInLocale(R.string.platform_parameter_synced_from_server_message)
6263
else ->
6364
resourceHandler.getStringInLocale(R.string.platform_parameter_never_synced_message)

0 commit comments

Comments
 (0)