Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

font scale setting screen #6453

Merged
merged 6 commits into from
Jul 18, 2022
Merged

font scale setting screen #6453

merged 6 commits into from
Jul 18, 2022

Conversation

fedrunov
Copy link
Contributor

@fedrunov fedrunov commented Jul 4, 2022

Type of change

  • Feature
  • Bugfix
  • Technical
  • Other :

Content

Screen, where user can select font scaling specifically for element app, or choose to use system-wide setting

Motivation and context

#5687

Screenshots / GIFs

Light theme DarkTheme
Screenshot 2022-07-07 at 11 11 52 Screenshot 2022-07-07 at 11 12 01

Tests

  • Navigate to Settings->Prefences
  • Click on Font Size
  • Choose settings

Tested devices

  • Physical
  • Emulator
  • OS version(s): Android 12

Checklist

@fedrunov fedrunov requested a review from ouchadam July 4, 2022 11:06
@fedrunov fedrunov marked this pull request as ready for review July 7, 2022 09:32
fun getSystemFontScale(): Float
}

class SystemSettingsProviderImpl @Inject constructor(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than Impl we could provide more context here by renaming the implementation AndroidSystemSettingsProvider, for other cases where the interface and implementation are the same (extracted for testing or design reasons) the convention is to use Default{Interface}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really think there is any other system rather than Android we could have, so System and AndroidSystem are quite same for me. And since so, I'll rename to AndroidSystemSettingsProvider :)

import javax.inject.Inject

/**
* A helper to get system settings.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: helper is one of those generic terms that doesn't mean much, everything can be a helper/util 😄

it also asks the questions if we need to leak the knowledge of the SystemSettings, does the consumer of getSystemFontScale benefit from knowing it's a System setting, would other Systems provide that information in the same way? 🤔

this might be more flexible if it was coupled to the font domain rather than SystemSettings, eg FontSettingsProvider with an AndroidSystemSettingsProvider providing the implementation

what do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does the consumer of getSystemFontScale benefit from knowing it's a System setting

Yes, the main purpose of this class is to provide system settings to be compared with app settings or used instead of it. So there is a clear and significant difference between system setting and app setting for consumer of this class

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this might be more flexible if it was coupled to the font domain rather than SystemSettings, eg FontSettingsProvider

yes, it would be more flexible, but that's also a level of abstraction which we don't need and most likely we won't need in a future. There are no other system settings we provide for now and adding another class for font domain will just increase number of classes involved in resolving this setting, without giving us any real benefit from it's flexibility

}

@Test
fun `when handling FontScaleChangedAction, then changes state and emits RestartActivity event`() = runTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a convention commonly used by gherkin style unit tests is to use line breaks as a way to signify the different parts of the test

// given
setup parameters and dependencies

// when
call method under test

// then
assert result


@Test
fun `when handling FontScaleChangedAction, then changes state and emits RestartActivity event`() = runTest {
val scaleOptions = fakeFontScalePreferences.getAvailableScales()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ideally our tests would only make real calls to the method currently under test, which for this test case is ViewModel.handle

by calling methods on other classes we're coupling our tests to them, if FontScalePreferences.getAvailableScales were to change, this test would also need to be updated

something else to bear in mind is which information is needed by the test, for this test case we want to know if given a list of options, when selecting one, is the state updated, restart emitted and the selection persisted, which means we can simplify to...

// A reusable fixture for the given data
private val A_SELECTION = aFontScaleValue(index = 1)

// the ViewModel tests don't need to know about the real font scale options
private val A_SCALE_OPTIONS_WITH_SELECTION = listOf(
        aFontScaleValue(index = 0),
        A_SELECTION,
)

@Test
fun `given scale options containing selection, when handling FontScaleChangedAction with selection, then persists selection, updates selected index and emits RestartActivity`() {
    // we setup the fake with our static test data related to the test name
    fakeFontScalePreferences.givenAvailableScaleOptions(A_SCALE_OPTIONS_WITH_SELECTION)
    viewModelWith(initialState)
    val test = viewModel.test()

    viewModel.handle(FontScaleSettingAction.FontScaleChangedAction(A_SELECTION))

    test
            .assertStatesChanges(
                    // the ViewModel mutates the initial state in its `init`
                    initialState.copy(availableScaleOptions = A_SCALE_OPTIONS_WITH_SELECTION),
                    { copy(persistedSettingIndex = A_SELECTION.index) }
            )
            .assertEvents(FontScaleSettingViewEvents.RestartActivity)
            .finish()
    fakeFontScalePreferences.verifyAppScaleFontValue(A_SELECTION)
}

// our tests only make use of the index 
fun aFontScaleValue(index: Int) = FontScaleValue(index, "foo", -1f, 0)

/**
* Object to manage the Font Scale choice of the user.
*/
@Singleton
Copy link
Contributor

@ouchadam ouchadam Jul 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the reason for using @Singleton? I would lean against the usage as the class isn't heavy to instantiate and doesn't directly hold any mutable state

once the classloader encounters a singleton it (and its dependencies dependencies) will be in memory for the lifetime of the application (this class wouldn't be needed once the app is in the background for example)

@fedrunov fedrunov requested a review from ouchadam July 15, 2022 09:58
@sonarqubecloud
Copy link

SonarCloud Quality Gate failed.    Quality Gate failed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

42.9% 42.9% Coverage
0.0% 0.0% Duplication

@Provides
@Singleton
@DefaultPreferences
fun providesDefaultSharedPreferences(context: Context): SharedPreferences {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

Copy link
Contributor

@ouchadam ouchadam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the updates, looks great! 💯

@fedrunov fedrunov merged commit 79762d9 into develop Jul 18, 2022
@fedrunov fedrunov deleted the feature/nfe/font_scaling branch July 18, 2022 07:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants