Skip to content

Commit 32c7c42

Browse files
committed
Bug 1955886 - Add app icon repository r=android-reviewers,sfamisa,marcin
Differential Revision: https://phabricator.services.mozilla.com/D256749
1 parent 2856ad6 commit 32c7c42

File tree

9 files changed

+201
-7
lines changed

9 files changed

+201
-7
lines changed

mobile/android/fenix/app/src/main/java/org/mozilla/fenix/iconpicker/ActivityAlias.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ enum class AliasSuffix(val suffix: String) {
182182
AppPride("AppPride"),
183183
AppCute("AppCute"),
184184
AppMomo("AppMomo"),
185+
186+
// This one is for testing purposes only; not intended to be used.
187+
Unknown("Unknown"),
185188
;
186189

187190
/**

mobile/android/fenix/app/src/main/java/org/mozilla/fenix/iconpicker/SettingsAppIcon.kt

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org.mozilla.fenix.iconpicker
66

77
import androidx.annotation.StringRes
8+
import androidx.annotation.VisibleForTesting
89
import org.mozilla.fenix.R
910

1011
/**
@@ -67,7 +68,9 @@ data class SettingsAppIcon(
6768
activityAlias = ActivityAlias.AppGradientGoldenHour,
6869
titleId = R.string.alternative_app_icon_option_gradient_golden_hour,
6970
)
70-
private val appGradientSunset = SettingsAppIcon(
71+
72+
@VisibleForTesting
73+
internal val appGradientSunset = SettingsAppIcon(
7174
activityAlias = ActivityAlias.AppGradientSunset,
7275
titleId = R.string.alternative_app_icon_option_gradient_sunset,
7376
)
@@ -150,6 +153,39 @@ data class SettingsAppIcon(
150153
appMomo,
151154
),
152155
)
156+
157+
/**
158+
* Returns the [SettingsAppIcon] associated with the provided [AliasSuffix].
159+
*
160+
* @param aliasSuffix The [AliasSuffix] used to find the corresponding [SettingsAppIcon].
161+
*/
162+
fun fromAliasSuffix(aliasSuffix: AliasSuffix): SettingsAppIcon =
163+
allIcons.find { it.activityAlias.aliasSuffix == aliasSuffix } ?: appDefault
164+
165+
private val allIcons = listOf(
166+
appDefault,
167+
appSolidLight,
168+
appSolidDark,
169+
appSolidRed,
170+
appSolidGreen,
171+
appSolidBlue,
172+
appSolidPurple,
173+
appSolidPurpleDark,
174+
appGradientSunrise,
175+
appGradientGoldenHour,
176+
appGradientSunset,
177+
appGradientBlueHour,
178+
appGradientTwilight,
179+
appGradientMidnight,
180+
appGradientNorthernLights,
181+
appRetro2004,
182+
appRetro2017,
183+
appPixelated,
184+
appMinimal,
185+
appPride,
186+
appCute,
187+
appMomo,
188+
)
153189
}
154190
}
155191

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
package org.mozilla.fenix.iconpicker
6+
7+
import org.mozilla.fenix.utils.Settings
8+
9+
/**
10+
* An interface for accessing available and selected app icon options, as well as persisting the
11+
* selected one.
12+
*/
13+
interface SettingsAppIconRepository {
14+
/**
15+
* The icon selected by the user or the default one.
16+
*/
17+
var selectedAppIcon: SettingsAppIcon
18+
19+
/**
20+
* Icons available for the user to choose from.
21+
*/
22+
val groupedAppIcons: Map<SettingsGroupTitle, List<SettingsAppIcon>>
23+
}
24+
25+
/**
26+
* Default implementation of the [SettingsAppIconRepository]
27+
*
28+
* @param settings The settings object used to persist the selected icon.
29+
*/
30+
class DefaultSettingsAppIconRepository(
31+
private val settings: Settings,
32+
) : SettingsAppIconRepository {
33+
override var selectedAppIcon: SettingsAppIcon
34+
get() = SettingsAppIcon.fromAliasSuffix(AliasSuffix.fromString(settings.selectedAppIcon))
35+
set(value) { settings.selectedAppIcon = value.activityAlias.aliasSuffix.suffix }
36+
37+
override val groupedAppIcons: Map<SettingsGroupTitle, List<SettingsAppIcon>>
38+
get() = SettingsAppIcon.groupedAppIcons
39+
}

mobile/android/fenix/app/src/main/java/org/mozilla/fenix/iconpicker/ui/AppIconPreference.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ import androidx.preference.Preference
2727
import androidx.preference.PreferenceViewHolder
2828
import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview
2929
import org.mozilla.fenix.R
30+
import org.mozilla.fenix.ext.settings
31+
import org.mozilla.fenix.iconpicker.DefaultSettingsAppIconRepository
3032
import org.mozilla.fenix.iconpicker.SettingsAppIcon
33+
import org.mozilla.fenix.iconpicker.SettingsAppIconRepository
3134
import org.mozilla.fenix.settings.CustomizationFragmentDirections
3235
import org.mozilla.fenix.theme.FirefoxTheme
3336

@@ -42,6 +45,10 @@ class AppIconPreference @JvmOverloads constructor(
4245
attrs: AttributeSet? = null,
4346
) : Preference(context, attrs) {
4447

48+
private val appIconRepository: SettingsAppIconRepository by lazy {
49+
DefaultSettingsAppIconRepository(context.settings())
50+
}
51+
4552
init {
4653
layoutResource = R.layout.app_icon_preference
4754
}
@@ -52,7 +59,7 @@ class AppIconPreference @JvmOverloads constructor(
5259
(holder.findViewById(R.id.compose_view) as ComposeView).setContent {
5360
FirefoxTheme {
5461
SelectAppIcon(
55-
appIcon = SettingsAppIcon.appDefault,
62+
appIcon = appIconRepository.selectedAppIcon,
5663
onClick = {
5764
val navController = holder.itemView.findNavController()
5865
navController.navigate(

mobile/android/fenix/app/src/main/java/org/mozilla/fenix/iconpicker/ui/AppIconSelectionFragment.kt

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,21 @@ import androidx.fragment.app.Fragment
1212
import androidx.navigation.fragment.findNavController
1313
import mozilla.components.support.base.feature.UserInteractionHandler
1414
import org.mozilla.fenix.R
15+
import org.mozilla.fenix.ext.settings
1516
import org.mozilla.fenix.ext.showToolbar
16-
import org.mozilla.fenix.iconpicker.ActivityAlias
17-
import org.mozilla.fenix.iconpicker.SettingsAppIcon
17+
import org.mozilla.fenix.iconpicker.DefaultSettingsAppIconRepository
18+
import org.mozilla.fenix.iconpicker.SettingsAppIconRepository
1819
import org.mozilla.fenix.theme.FirefoxTheme
1920

2021
/**
2122
* Fragment that displays a list of alternative app icons.
2223
*/
2324
class AppIconSelectionFragment : Fragment(), UserInteractionHandler {
2425

26+
private val appIconRepository: SettingsAppIconRepository by lazy {
27+
DefaultSettingsAppIconRepository(requireContext().settings())
28+
}
29+
2530
override fun onCreateView(
2631
inflater: LayoutInflater,
2732
container: ViewGroup?,
@@ -30,9 +35,13 @@ class AppIconSelectionFragment : Fragment(), UserInteractionHandler {
3035
setContent {
3136
FirefoxTheme {
3237
AppIconSelection(
33-
currentAppIcon = ActivityAlias.AppDefault,
34-
groupedIconOptions = SettingsAppIcon.groupedAppIcons,
35-
onClick = {},
38+
currentAppIcon = appIconRepository.selectedAppIcon.activityAlias,
39+
groupedIconOptions = appIconRepository.groupedAppIcons,
40+
onClick = { selectedAppIcon ->
41+
// a warning dialog about app restart will be added here
42+
// in https://bugzilla.mozilla.org/show_bug.cgi?id=1976776
43+
appIconRepository.selectedAppIcon = selectedAppIcon
44+
},
3645
)
3746
}
3847
}

mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import org.mozilla.fenix.ext.components
4949
import org.mozilla.fenix.ext.getPreferenceKey
5050
import org.mozilla.fenix.home.pocket.ContentRecommendationsFeatureHelper
5151
import org.mozilla.fenix.home.topsites.TopSitesConfigConstants.TOP_SITES_MAX_COUNT
52+
import org.mozilla.fenix.iconpicker.AliasSuffix
5253
import org.mozilla.fenix.nimbus.CookieBannersSection
5354
import org.mozilla.fenix.nimbus.FxNimbus
5455
import org.mozilla.fenix.nimbus.HomeScreenSection
@@ -2613,6 +2614,14 @@ class Settings(private val appContext: Context) : PreferencesHolder {
26132614
default = "",
26142615
)
26152616

2617+
/**
2618+
* Suffix of the currently selected app icon (launcher alias).
2619+
*/
2620+
var selectedAppIcon by stringPreference(
2621+
key = appContext.getPreferenceKey(R.string.pref_key_selected_app_icon),
2622+
default = AliasSuffix.AppDefault.suffix,
2623+
)
2624+
26162625
/**
26172626
* Indicates whether the app should automatically clean up downloaded files.
26182627
*/

mobile/android/fenix/app/src/main/res/values/preference_keys.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@
164164

165165
<!-- App icon settings-->
166166
<string name="pref_key_app_icon">pref_key_app_icon</string>
167+
<string name="pref_key_selected_app_icon">pref_key_selected_app_icon</string>
168+
167169
<!-- Menu -->
168170
<string name="pref_key_menu_cfr" translatable="false">pref_key_menu_cfr</string>
169171

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
package org.mozilla.fenix.iconpicker
6+
7+
import androidx.test.ext.junit.runners.AndroidJUnit4
8+
import io.mockk.every
9+
import mozilla.components.support.test.robolectric.testContext
10+
import org.junit.Assert.assertEquals
11+
import org.junit.Before
12+
import org.junit.Test
13+
import org.junit.runner.RunWith
14+
import org.mozilla.fenix.ext.settings
15+
import org.mozilla.fenix.utils.Settings
16+
17+
@RunWith(AndroidJUnit4::class)
18+
class DefaultSettingsAppIconRepositoryTest {
19+
private lateinit var settings: Settings
20+
21+
@Before
22+
fun setup() {
23+
settings = Settings(testContext)
24+
every { testContext.settings() } returns settings
25+
}
26+
27+
@Test
28+
fun `WHEN the selected app icon value changes in the repository THEN the shared preference updates as well`() {
29+
val repository = DefaultSettingsAppIconRepository(testContext.settings())
30+
val defaultIcon = SettingsAppIcon.appDefault
31+
val newAppIcon = SettingsAppIcon.appGradientSunset
32+
33+
assertEquals(defaultIcon.activityAlias.aliasSuffix.suffix, settings.selectedAppIcon)
34+
repository.selectedAppIcon = newAppIcon
35+
36+
assertEquals(newAppIcon.activityAlias.aliasSuffix.suffix, settings.selectedAppIcon)
37+
}
38+
39+
@Test
40+
fun `WHEN a new value has been assigned THEN the repository returns the new value`() {
41+
val repository = DefaultSettingsAppIconRepository(testContext.settings())
42+
val defaultIcon = SettingsAppIcon.appDefault
43+
val newAppIcon = SettingsAppIcon.appGradientSunset
44+
45+
assertEquals(defaultIcon, repository.selectedAppIcon)
46+
47+
repository.selectedAppIcon = SettingsAppIcon.appGradientSunset
48+
49+
assertEquals(newAppIcon, repository.selectedAppIcon)
50+
}
51+
52+
@Test
53+
fun `WHEN grouped icon list is requested THEN repository returns the settings app icon collection`() {
54+
val repository = DefaultSettingsAppIconRepository(testContext.settings())
55+
56+
val result = repository.groupedAppIcons
57+
58+
assertEquals(SettingsAppIcon.groupedAppIcons, result)
59+
}
60+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
package org.mozilla.fenix.iconpicker
6+
7+
import junit.framework.TestCase.assertEquals
8+
import org.junit.Test
9+
10+
class SettingsAppIconTest {
11+
12+
@Test
13+
fun `GIVEN a valid AliasSuffix WHEN fromAliasSuffix is called THEN matching SettingsAppIcon is returned`() {
14+
val input = AliasSuffix.AppGradientSunset
15+
16+
val result = SettingsAppIcon.fromAliasSuffix(input)
17+
18+
assertEquals(SettingsAppIcon.appGradientSunset, result)
19+
}
20+
21+
@Test
22+
fun `GIVEN an unknown AliasSuffix WHEN fromAliasSuffix is called THEN appDefault is returned`() {
23+
val unknownAliasSuffix = AliasSuffix.Unknown
24+
25+
val result = SettingsAppIcon.fromAliasSuffix(unknownAliasSuffix)
26+
27+
assertEquals(SettingsAppIcon.appDefault, result)
28+
}
29+
}

0 commit comments

Comments
 (0)