Add generated screen to show open source licenses.

For Google Play variant only
This commit is contained in:
Benoit Marty 2024-07-17 15:41:06 +02:00 committed by Benoit Marty
parent f5e866e18c
commit 766ae389ce
20 changed files with 442 additions and 5 deletions

View file

@ -27,6 +27,7 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.compound.theme.ElementTheme
import io.element.android.features.preferences.api.OpenSourceLicensesProvider
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab
import io.element.android.libraries.di.SessionScope
@ -35,6 +36,7 @@ class AboutNode @AssistedInject constructor(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
private val presenter: AboutPresenter,
private val openSourceLicensesProvider: OpenSourceLicensesProvider,
) : Node(buildContext, plugins = plugins) {
private fun onElementLegalClick(
activity: Activity,
@ -55,6 +57,9 @@ class AboutNode @AssistedInject constructor(
onElementLegalClick = { elementLegal ->
onElementLegalClick(activity, isDark, elementLegal)
},
onOpenSourceLicensesClick = {
openSourceLicensesProvider.navigateToOpenSourceLicenses(activity)
},
modifier = modifier
)
}

View file

@ -17,14 +17,18 @@
package io.element.android.features.preferences.impl.about
import androidx.compose.runtime.Composable
import io.element.android.features.preferences.api.OpenSourceLicensesProvider
import io.element.android.libraries.architecture.Presenter
import javax.inject.Inject
class AboutPresenter @Inject constructor() : Presenter<AboutState> {
class AboutPresenter @Inject constructor(
private val openSourceLicensesProvider: OpenSourceLicensesProvider,
) : Presenter<AboutState> {
@Composable
override fun present(): AboutState {
return AboutState(
elementLegals = getAllLegals(),
hasOpenSourcesLicenses = openSourceLicensesProvider.hasOpenSourceLicenses,
)
}
}

View file

@ -16,7 +16,7 @@
package io.element.android.features.preferences.impl.about
// Do not use default value, so no member get forgotten in the presenters.
data class AboutState(
val elementLegals: List<ElementLegal>,
val hasOpenSourcesLicenses: Boolean,
)

View file

@ -21,10 +21,14 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
open class AboutStateProvider : PreviewParameterProvider<AboutState> {
override val values: Sequence<AboutState>
get() = sequenceOf(
aAboutState(),
anAboutState(),
anAboutState(hasOpenSourcesLicenses = true),
)
}
fun aAboutState() = AboutState(
fun anAboutState(
hasOpenSourcesLicenses: Boolean = false,
) = AboutState(
elementLegals = getAllLegals(),
hasOpenSourcesLicenses = hasOpenSourcesLicenses,
)

View file

@ -30,6 +30,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
fun AboutView(
state: AboutState,
onElementLegalClick: (ElementLegal) -> Unit,
onOpenSourceLicensesClick: () -> Unit,
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
) {
@ -44,6 +45,12 @@ fun AboutView(
onClick = { onElementLegalClick(elementLegal) }
)
}
if (state.hasOpenSourcesLicenses) {
PreferenceText(
title = stringResource(id = CommonStrings.common_open_source_licenses),
onClick = onOpenSourceLicensesClick,
)
}
}
}
@ -53,6 +60,7 @@ internal fun AboutViewPreview(@PreviewParameter(AboutStateProvider::class) state
AboutView(
state = state,
onElementLegalClick = {},
onOpenSourceLicensesClick = {},
onBackClick = {},
)
}

View file

@ -31,12 +31,25 @@ class AboutPresenterTest {
@Test
fun `present - initial state`() = runTest {
val presenter = AboutPresenter()
val presenter = AboutPresenter(FakeOpenSourceLicensesProvider(hasOpenSourceLicenses = true))
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
assertThat(initialState.elementLegals).isEqualTo(getAllLegals())
assertThat(initialState.hasOpenSourcesLicenses).isTrue()
}
}
@Test
fun `present - initial state, no open source licenses`() = runTest {
val presenter = AboutPresenter(FakeOpenSourceLicensesProvider(hasOpenSourceLicenses = false))
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
assertThat(initialState.elementLegals).isEqualTo(getAllLegals())
assertThat(initialState.hasOpenSourcesLicenses).isFalse()
}
}
}

View file

@ -0,0 +1,100 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.preferences.impl.about
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.tests.testutils.EnsureNeverCalled
import io.element.android.tests.testutils.EnsureNeverCalledWithParam
import io.element.android.tests.testutils.clickOn
import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.ensureCalledOnceWithParam
import io.element.android.tests.testutils.pressBack
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class AboutViewTest {
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
@Test
fun `clicking on back invokes back callback`() {
ensureCalledOnce { callback ->
rule.setAboutView(
anAboutState(),
onBackClick = callback,
)
rule.pressBack()
}
}
@Test
fun `clicking on an item invokes the expected callback`() {
val state = anAboutState()
ensureCalledOnceWithParam(state.elementLegals.first()) { callback ->
rule.setAboutView(
state,
onElementLegalClick = callback,
)
rule.clickOn(state.elementLegals.first().titleRes)
}
}
@Test
fun `if open source licenses are not available, the entry is not displayed`() {
rule.setAboutView(
anAboutState(),
)
val text = rule.activity.getString(CommonStrings.common_open_source_licenses)
rule.onNodeWithText(text).assertDoesNotExist()
}
@Test
fun `if open source licenses are available, clicking on the entry invokes the expected callback`() {
ensureCalledOnce { callback ->
rule.setAboutView(
anAboutState(
hasOpenSourcesLicenses = true,
),
onOpenSourceLicensesClick = callback,
)
rule.clickOn(CommonStrings.common_open_source_licenses)
}
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setAboutView(
state: AboutState,
onElementLegalClick: (ElementLegal) -> Unit = EnsureNeverCalledWithParam(),
onOpenSourceLicensesClick: () -> Unit = EnsureNeverCalled(),
onBackClick: () -> Unit = EnsureNeverCalled(),
) {
setContent {
AboutView(
state = state,
onElementLegalClick = onElementLegalClick,
onOpenSourceLicensesClick = onOpenSourceLicensesClick,
onBackClick = onBackClick,
)
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.preferences.impl.about
import android.app.Activity
import io.element.android.features.preferences.api.OpenSourceLicensesProvider
class FakeOpenSourceLicensesProvider(
override val hasOpenSourceLicenses: Boolean,
) : OpenSourceLicensesProvider {
override fun navigateToOpenSourceLicenses(activity: Activity) = Unit
}