Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/oppia/oppia-android into #…
Browse files Browse the repository at this point in the history
  • Loading branch information
XichengSpencer committed Nov 15, 2023
2 parents 6fe9746 + 87c6fb2 commit 1cbe96d
Show file tree
Hide file tree
Showing 148 changed files with 2,630 additions and 748 deletions.
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ If your PR includes UI-related changes, then:
- Add screenshots for portrait/landscape for both a tablet & phone of the before & after UI changes
- For the screenshots above, include both English and pseudo-localized (RTL) screenshots (see [RTL guide](https://github.com/oppia/oppia-android/wiki/RTL-Guidelines))
- Add a video showing the full UX flow with a screen reader enabled (see [accessibility guide](https://github.com/oppia/oppia-android/wiki/Accessibility-A11y-Guide))
- For PRs introducing new UI elements or color changes, both light and dark mode screenshots must be included
- Add a screenshot demonstrating that you ran affected Espresso tests locally & that they're passing
4 changes: 2 additions & 2 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ git_repository(
# to correctly size in-line SVGs (such as those needed for LaTeX-based math expressions).
git_repository(
name = "androidsvg",
commit = "1265eb1087056cf3fc2e10442e5545bc65c109ce",
commit = "5bc9c7553e94c3476e8ea32baea3c77567228fcd",
remote = "https://github.com/oppia/androidsvg",
shallow_since = "1686302944 -0700",
shallow_since = "1686304726 -0700",
)

git_repository(
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@
android:name=".app.notice.testing.GeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity"
android:label="@string/test_activity_label"
android:theme="@style/OppiaThemeWithoutActionBar" />
<activity
android:name=".app.notice.testing.ForcedAppDeprecationNoticeDialogFragmentTestActivity"
android:label="@string/test_activity_label"
android:theme="@style/OppiaThemeWithoutActionBar" />
<activity
android:name=".app.notice.testing.OptionalAppDeprecationNoticeDialogFragmentTestActivity"
android:label="@string/test_activity_label"
android:theme="@style/OppiaThemeWithoutActionBar" />
<activity
android:name=".app.notice.testing.OsDeprecationNoticeDialogFragmentTestActivity"
android:label="@string/test_activity_label"
android:theme="@style/OppiaThemeWithoutActionBar" />
<activity
android:name=".app.onboarding.OnboardingActivity"
android:label="@string/onboarding_activity_title"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.oppia.android.app.administratorcontrols.learneranalytics
import android.annotation.SuppressLint
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
Expand Down Expand Up @@ -228,7 +229,11 @@ class ControlButtonsViewModel private constructor(
val compressedMessage = ByteArrayOutputStream().also { byteOutputStream ->
GZIPOutputStream(byteOutputStream).use(::writeTo)
}.toByteArray()
return Base64.getEncoder().encodeToString(compressedMessage)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Base64.getEncoder().encodeToString(compressedMessage)
} else {
android.util.Base64.encodeToString(compressedMessage, 0)
}
}

private fun String.computeSha1Hash(machineLocale: OppiaLocale.MachineLocale): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatCheckBox;
import androidx.core.widget.CompoundButtonCompat;
import androidx.databinding.BindingAdapter;

/**
Expand All @@ -13,6 +14,6 @@ public final class AppCompatCheckBoxBindingAdapters {
/** Sets the button tint for the specified checkbox, via data-binding. */
@BindingAdapter("app:buttonTint")
public static void setButtonTint(@NonNull AppCompatCheckBox checkBox, @ColorInt int colorRgb) {
checkBox.setSupportButtonTintList(ColorStateList.valueOf(colorRgb));
CompoundButtonCompat.setButtonTintList(checkBox, ColorStateList.valueOf(colorRgb));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,21 +83,21 @@ private static String getTimeAgo(View view, long lastVisitedTimestamp) {
} else if (timeDifferenceMillis < TimeUnit.MINUTES.toMillis(50)) {
return getPluralString(
resourceHandler,
R.plurals.minutes,
R.plurals.minutes_ago,
(int) TimeUnit.MILLISECONDS.toMinutes(timeDifferenceMillis)
);
} else if (timeDifferenceMillis < TimeUnit.DAYS.toMillis(1)) {
return getPluralString(
resourceHandler,
R.plurals.hours,
R.plurals.hours_ago,
(int) TimeUnit.MILLISECONDS.toHours(timeDifferenceMillis)
);
} else if (timeDifferenceMillis < TimeUnit.DAYS.toMillis(2)) {
return resourceHandler.getStringInLocale(R.string.yesterday);
}
return getPluralString(
resourceHandler,
R.plurals.days,
R.plurals.days_ago,
(int) TimeUnit.MILLISECONDS.toDays(timeDifferenceMillis)
);
}
Expand All @@ -107,13 +107,9 @@ private static String getPluralString(
@PluralsRes int pluralsResId,
int count
) {
// TODO(#3841): Combine these strings together.
return resourceHandler.getStringInLocaleWithWrapping(
R.string.time_ago,
resourceHandler.getQuantityStringInLocaleWithWrapping(
return resourceHandler.getQuantityStringInLocaleWithWrapping(
pluralsResId, count, String.valueOf(count)
)
);
);
}

private static long ensureTimestampIsInMilliseconds(long lastVisitedTimestamp) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ import org.oppia.android.app.mydownloads.MyDownloadsFragment
import org.oppia.android.app.mydownloads.UpdatesTabFragment
import org.oppia.android.app.notice.AutomaticAppDeprecationNoticeDialogFragment
import org.oppia.android.app.notice.BetaNoticeDialogFragment
import org.oppia.android.app.notice.ForcedAppDeprecationNoticeDialogFragment
import org.oppia.android.app.notice.GeneralAvailabilityUpgradeNoticeDialogFragment
import org.oppia.android.app.notice.OptionalAppDeprecationNoticeDialogFragment
import org.oppia.android.app.notice.OsDeprecationNoticeDialogFragment
import org.oppia.android.app.onboarding.OnboardingFragment
import org.oppia.android.app.ongoingtopiclist.OngoingTopicListFragment
import org.oppia.android.app.options.AppLanguageFragment
Expand Down Expand Up @@ -113,7 +116,10 @@ interface FragmentComponentImpl : FragmentComponent, ViewComponentBuilderInjecto
fun inject(appVersionFragment: AppVersionFragment)
fun inject(audioFragment: AudioFragment)
fun inject(audioLanguageFragment: AudioLanguageFragment)
fun inject(autoAppDeprecationNoticeDialogFragment: AutomaticAppDeprecationNoticeDialogFragment)
fun inject(
automaticAppDeprecationNoticeDialogFragment:
AutomaticAppDeprecationNoticeDialogFragment
)
fun inject(betaNoticeDialogFragment: BetaNoticeDialogFragment)
fun inject(cellularAudioDialogFragment: CellularAudioDialogFragment)
fun inject(completedStoryListFragment: CompletedStoryListFragment)
Expand All @@ -127,6 +133,7 @@ interface FragmentComponentImpl : FragmentComponent, ViewComponentBuilderInjecto
fun inject(explorationTestActivityTestFragment: ExplorationTestActivityPresenter.TestFragment)
fun inject(faqListFragment: FAQListFragment)
fun inject(forceNetworkTypeFragment: ForceNetworkTypeFragment)
fun inject(forcedAppDeprecationNoticeDialogFragment: ForcedAppDeprecationNoticeDialogFragment)
fun inject(fragment: GeneralAvailabilityUpgradeNoticeDialogFragment)
fun inject(helpFragment: HelpFragment)
fun inject(hintsAndSolutionDialogFragment: HintsAndSolutionDialogFragment)
Expand All @@ -146,7 +153,9 @@ interface FragmentComponentImpl : FragmentComponent, ViewComponentBuilderInjecto
fun inject(navigationDrawerFragment: NavigationDrawerFragment)
fun inject(onboardingFragment: OnboardingFragment)
fun inject(ongoingTopicListFragment: OngoingTopicListFragment)
fun inject(optionalAppDeprecationNoticeDialogFragment: OptionalAppDeprecationNoticeDialogFragment)
fun inject(optionFragment: OptionsFragment)
fun inject(osDeprecationNoticeDialogFragment: OsDeprecationNoticeDialogFragment)
fun inject(policiesFragment: PoliciesFragment)
fun inject(profileAndDeviceIdFragment: ProfileAndDeviceIdFragment)
fun inject(profileChooserFragment: ProfileChooserFragment)
Expand Down
15 changes: 14 additions & 1 deletion app/src/main/java/org/oppia/android/app/home/HomeViewModel.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.oppia.android.app.home

import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.ObservableField
import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
Expand Down Expand Up @@ -58,6 +59,15 @@ class HomeViewModel(
R.integer.promoted_story_list_limit
)

/**
* A Boolean property indicating the visibility state of a progress bar.
* This property is used to control the visibility of a progress bar in a user interface.
* When set to true, the progress bar is made visible, indicating that an ongoing task
* or operation is in progress or pending or failed. When set to false, the progress bar is hidden, indicating
* that the operation has completed.
*/
val isProgressBarVisible = ObservableField(true)

private val profileDataProvider: DataProvider<Profile> by lazy {
profileManagementController.getProfile(profileId)
}
Expand Down Expand Up @@ -111,7 +121,10 @@ class HomeViewModel(
listOf()
}
is AsyncResult.Pending -> listOf()
is AsyncResult.Success -> itemListResult.value
is AsyncResult.Success -> {
isProgressBarVisible.set(false)
itemListResult.value
}
}
}
}
Expand Down
13 changes: 8 additions & 5 deletions app/src/main/java/org/oppia/android/app/home/WelcomeViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,18 @@ class WelcomeViewModel(
dateTimeUtil: DateTimeUtil
) : HomeItemViewModel() {

/** Text [String] to greet the learner and display on-screen when launching the home activity. */
/** Text [String] to greet the learner. */
val greeting: String = dateTimeUtil.getGreetingMessage()

/**
* Returns the user-readable portion of the welcome screen greeting that contains the user's name.
* Returns the string which contains greeting message with user's name and
* display on-screen when launching the home activity.
*/
fun computeProfileNameText(): String {
return resourceHandler.getStringInLocaleWithWrapping(R.string.welcome_profile_name, profileName)
}
fun computeWelcomeText(): String = resourceHandler.getStringInLocaleWithWrapping(
R.string.welcome_profile_name,
greeting,
profileName
)

// Overriding equals is needed so that DataProvider combine functions used in the HomeViewModel
// will only rebind when the actual data in the data list changes, rather than when the ViewModel
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.oppia.android.app.notice

import org.oppia.android.app.splash.DeprecationNoticeActionType

/** Listener for when an option on any deprecation dialog is clicked. */
interface DeprecationNoticeActionListener {
/** Called when a dialog button is clicked. */
fun onActionButtonClicked(noticeType: DeprecationNoticeActionType)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.oppia.android.app.notice

import android.app.Dialog
import android.content.Context
import android.os.Bundle
import org.oppia.android.app.fragment.FragmentComponentImpl
import org.oppia.android.app.fragment.InjectableDialogFragment
import javax.inject.Inject

/** Dialog fragment that informs the user of an app deprecation. */
class ForcedAppDeprecationNoticeDialogFragment : InjectableDialogFragment() {
companion object {
/** Returns a new instance of [ForcedAppDeprecationNoticeDialogFragment]. */
fun newInstance(): ForcedAppDeprecationNoticeDialogFragment {
return ForcedAppDeprecationNoticeDialogFragment()
}
}

@Inject lateinit var presenter: ForcedAppDeprecationNoticeDialogFragmentPresenter

override fun onAttach(context: Context) {
super.onAttach(context)
(fragmentComponent as FragmentComponentImpl).inject(this)
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return presenter.handleOnCreateDialog()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.oppia.android.app.notice

import android.app.Dialog
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import org.oppia.android.R
import org.oppia.android.app.splash.DeprecationNoticeActionType
import org.oppia.android.app.translation.AppLanguageResourceHandler
import javax.inject.Inject

/** Presenter class responsible for showing an app deprecation dialog to the user. */
class ForcedAppDeprecationNoticeDialogFragmentPresenter @Inject constructor(
private val activity: AppCompatActivity,
private val resourceHandler: AppLanguageResourceHandler
) {
private val deprecationNoticeActionListener by lazy {
activity as DeprecationNoticeActionListener
}

/** Handles dialog creation for the forced app deprecation notice. */
fun handleOnCreateDialog(): Dialog {
val appName = resourceHandler.getStringInLocale(R.string.app_name)

val dialog = AlertDialog.Builder(activity, R.style.DeprecationAlertDialogTheme)
.setTitle(R.string.forced_app_update_dialog_title)
.setMessage(
resourceHandler.getStringInLocaleWithWrapping(
R.string.forced_app_update_dialog_message,
appName
)
)
.setPositiveButton(R.string.forced_app_update_dialog_update_button_text) { _, _ ->
deprecationNoticeActionListener.onActionButtonClicked(
DeprecationNoticeActionType.UPDATE
)
}
.setNegativeButton(R.string.forced_app_update_dialog_close_button_text) { _, _ ->
deprecationNoticeActionListener.onActionButtonClicked(
DeprecationNoticeActionType.CLOSE
)
}
.setCancelable(false)
.create()
dialog.setCanceledOnTouchOutside(false)
return dialog
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.oppia.android.app.notice

import android.app.Dialog
import android.content.Context
import android.os.Bundle
import org.oppia.android.app.fragment.FragmentComponentImpl
import org.oppia.android.app.fragment.InjectableDialogFragment
import javax.inject.Inject

/** Dialog fragment that informs the user that a new app version is available for download. */
class OptionalAppDeprecationNoticeDialogFragment : InjectableDialogFragment() {
companion object {
/** Returns a new instance of [OptionalAppDeprecationNoticeDialogFragment]. */
fun newInstance(): OptionalAppDeprecationNoticeDialogFragment {
return OptionalAppDeprecationNoticeDialogFragment()
}
}

@Inject lateinit var optionalAppDeprecationNoticeDialogFragmentPresenter:
OptionalAppDeprecationNoticeDialogFragmentPresenter

override fun onAttach(context: Context) {
super.onAttach(context)
(fragmentComponent as FragmentComponentImpl).inject(this)
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return optionalAppDeprecationNoticeDialogFragmentPresenter.handleOnCreateDialog()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.oppia.android.app.notice

import android.app.Dialog
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import org.oppia.android.R
import org.oppia.android.app.splash.DeprecationNoticeActionType
import org.oppia.android.app.translation.AppLanguageResourceHandler
import javax.inject.Inject

/** Presenter class responsible for showing an optional update dialog to the user. */
class OptionalAppDeprecationNoticeDialogFragmentPresenter @Inject constructor(
private val activity: AppCompatActivity,
private val resourceHandler: AppLanguageResourceHandler
) {
private val deprecationNoticeActionListener by lazy {
activity as DeprecationNoticeActionListener
}

/** Handles dialog creation for the optional app deprecation notice. */
fun handleOnCreateDialog(): Dialog {
val appName = resourceHandler.getStringInLocale(R.string.app_name)

val dialog = AlertDialog.Builder(activity, R.style.DeprecationAlertDialogTheme)
.setTitle(R.string.optional_app_update_dialog_title)
.setMessage(
resourceHandler.getStringInLocaleWithWrapping(
R.string.optional_app_update_dialog_message,
appName
)
)
.setPositiveButton(R.string.optional_app_update_dialog_update_button_text) { _, _ ->
deprecationNoticeActionListener.onActionButtonClicked(
DeprecationNoticeActionType.UPDATE
)
}
.setNegativeButton(R.string.optional_app_update_dialog_dismiss_button_text) { _, _ ->
deprecationNoticeActionListener.onActionButtonClicked(
DeprecationNoticeActionType.DISMISS
)
}
.setCancelable(false)
.create()
dialog.setCanceledOnTouchOutside(false)
return dialog
}
}
Loading

0 comments on commit 1cbe96d

Please sign in to comment.