diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/MainActivityTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/MainActivityTest.kt index d457ee7eb3f591ca3d179da0f3dfe81b0e627c4b..6f2fb1680365478578ca595b9369fdaaea5ddd71 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/MainActivityTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/MainActivityTest.kt @@ -48,6 +48,7 @@ import de.rki.coronawarnapp.util.CWADebug import de.rki.coronawarnapp.util.device.BackgroundModeStatus import de.rki.coronawarnapp.util.device.PowerManagement import de.rki.coronawarnapp.util.security.EncryptionErrorResetTool +import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.worker.BackgroundWorkScheduler import io.mockk.MockKAnnotations @@ -89,6 +90,7 @@ class MainActivityTest : BaseUITest() { @MockK lateinit var appConfigProvider: AppConfigProvider @MockK lateinit var statisticsProvider: StatisticsProvider @MockK lateinit var deadmanNotificationScheduler: DeadmanNotificationScheduler + @MockK lateinit var appShortcutsHelper: AppShortcutsHelper // MainActivity mocks @MockK lateinit var environmentSetup: EnvironmentSetup @@ -352,7 +354,8 @@ class MainActivityTest : BaseUITest() { submissionStateProvider = submissionStateProvider, cwaSettings = cwaSettings, statisticsProvider = statisticsProvider, - deadmanNotificationScheduler = deadmanNotificationScheduler + deadmanNotificationScheduler = deadmanNotificationScheduler, + appShortcutsHelper = appShortcutsHelper ) ) @@ -396,7 +399,8 @@ class MainActivityTest : BaseUITest() { every { showLoweredRiskLevelDialog } returns MutableLiveData() every { homeItems } returns MutableLiveData(emptyList()) every { popupEvents } returns SingleLiveEvent() - every { showPopUpsOrNavigate() } just Runs + every { showPopUps() } just Runs + every { restoreAppShortcuts() } just Runs } setupMockViewModel( diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt index 96c7b1e301eb0cb66b5e58b38255556c6cfbdea4..56559565e14f6017f5530eb84e8fe3e57d1069d1 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt @@ -16,6 +16,7 @@ import de.rki.coronawarnapp.tracing.GeneralTracingStatus import de.rki.coronawarnapp.tracing.states.TracingStateProvider import de.rki.coronawarnapp.tracing.ui.statusbar.TracingHeaderState import de.rki.coronawarnapp.util.security.EncryptionErrorResetTool +import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper import de.rki.coronawarnapp.util.ui.SingleLiveEvent import io.mockk.MockKAnnotations import io.mockk.Runs @@ -47,6 +48,7 @@ class HomeFragmentTest : BaseUITest() { @MockK lateinit var appConfigProvider: AppConfigProvider @MockK lateinit var statisticsProvider: StatisticsProvider @MockK lateinit var deadmanNotificationScheduler: DeadmanNotificationScheduler + @MockK lateinit var appShortcutsHelper: AppShortcutsHelper private lateinit var viewModel: HomeFragmentViewModel @@ -61,7 +63,8 @@ class HomeFragmentTest : BaseUITest() { every { showLoweredRiskLevelDialog } returns MutableLiveData() every { homeItems } returns MutableLiveData(emptyList()) every { popupEvents } returns SingleLiveEvent() - every { showPopUpsOrNavigate() } just Runs + every { showPopUps() } just Runs + every { restoreAppShortcuts() } just Runs } setupMockViewModel( @@ -96,7 +99,8 @@ class HomeFragmentTest : BaseUITest() { submissionStateProvider = submissionStateProvider, cwaSettings = cwaSettings, statisticsProvider = statisticsProvider, - deadmanNotificationScheduler = deadmanNotificationScheduler + deadmanNotificationScheduler = deadmanNotificationScheduler, + appShortcutsHelper = appShortcutsHelper ) ) } diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultAvailableFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultAvailableFragmentTest.kt index a76ab1305ca8ab54dbd904b263dd222e1df31b6b..c099702b2c9d71f323151993717783e3cad916c5 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultAvailableFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultAvailableFragmentTest.kt @@ -9,9 +9,12 @@ import de.rki.coronawarnapp.submission.auto.AutoSubmission import de.rki.coronawarnapp.submission.data.tekhistory.TEKHistoryUpdater_Factory_Impl import de.rki.coronawarnapp.ui.submission.resultavailable.SubmissionTestResultAvailableFragment import de.rki.coronawarnapp.ui.submission.resultavailable.SubmissionTestResultAvailableViewModel +import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper import io.mockk.MockKAnnotations +import io.mockk.Runs import io.mockk.every import io.mockk.impl.annotations.MockK +import io.mockk.just import io.mockk.spyk import io.mockk.unmockkAll import kotlinx.coroutines.flow.flowOf @@ -34,6 +37,7 @@ class SubmissionTestResultAvailableFragmentTest : BaseUITest() { @MockK lateinit var submissionRepository: SubmissionRepository @MockK lateinit var tekHistoryUpdaterFactory: TEKHistoryUpdater_Factory_Impl @MockK lateinit var autoSubmission: AutoSubmission + @MockK lateinit var appShortcutsHelper: AppShortcutsHelper @Rule @JvmField @@ -48,6 +52,7 @@ class SubmissionTestResultAvailableFragmentTest : BaseUITest() { every { submissionRepository.deviceUIStateFlow } returns flowOf() every { submissionRepository.testResultReceivedDateFlow } returns flowOf() + every { appShortcutsHelper.removeAppShortcut() } just Runs viewModel = spyk( SubmissionTestResultAvailableViewModel( diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentModule.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..16dacec6ee92c71e4835df2ff3ed21f4fbc94eed --- /dev/null +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentModule.kt @@ -0,0 +1,18 @@ +package de.rki.coronawarnapp.test.deltaonboarding.ui + +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey + +@Module +abstract class DeltaOnboardingFragmentModule { + @Binds + @IntoMap + @CWAViewModelKey(DeltaOnboardingFragmentViewModel::class) + abstract fun testTaskControllerFragment( + factory: DeltaOnboardingFragmentViewModel.Factory + ): CWAViewModelFactory<out CWAViewModel> +} diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..6ca53f71453eed67437e683a2aa449027b609d08 --- /dev/null +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentViewModel.kt @@ -0,0 +1,53 @@ +package de.rki.coronawarnapp.test.deltaonboarding.ui + +import androidx.lifecycle.LiveData +import androidx.lifecycle.asLiveData +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.contactdiary.ui.ContactDiarySettings +import de.rki.coronawarnapp.environment.BuildConfigWrap +import de.rki.coronawarnapp.main.CWASettings +import de.rki.coronawarnapp.storage.LocalData +import de.rki.coronawarnapp.util.coroutine.DispatcherProvider +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory + +class DeltaOnboardingFragmentViewModel @AssistedInject constructor( + private val settings: CWASettings, + private val contactDiarySettings: ContactDiarySettings, + dispatcherProvider: DispatcherProvider +) : CWAViewModel(dispatcherProvider = dispatcherProvider) { + + val changelogVersion: LiveData<Long> = + settings.lastChangelogVersion.flow.asLiveData(context = dispatcherProvider.Default) + + fun updateChangelogVersion(value: Long) { + settings.lastChangelogVersion.update { value } + } + + fun resetChangelogVersion() { + settings.lastChangelogVersion.update { BuildConfigWrap.VERSION_CODE } + } + + fun clearChangelogVersion() { + settings.lastChangelogVersion.update { 1 } + } + + fun isContactJournalOnboardingDone() = contactDiarySettings.isOnboardingDone + + fun setContactJournalOnboardingDone(value: Boolean) { + contactDiarySettings.onboardingStatus = if (value) + ContactDiarySettings.OnboardingStatus.RISK_STATUS_1_12 + else + ContactDiarySettings.OnboardingStatus.NOT_ONBOARDED + } + + fun isDeltaOnboardingDone() = LocalData.isInteroperabilityShownAtLeastOnce + + fun setDeltaOboardinDone(value: Boolean) { + LocalData.isInteroperabilityShownAtLeastOnce = value + } + + @AssistedFactory + interface Factory : SimpleCWAViewModelFactory<DeltaOnboardingFragmentViewModel> +} diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaonboardingFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaonboardingFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..1179e8e0701823179fafe78cb56af5e5dc210d98 --- /dev/null +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaonboardingFragment.kt @@ -0,0 +1,62 @@ +package de.rki.coronawarnapp.test.deltaonboarding.ui + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.databinding.FragmentTestDeltaonboardingBinding +import de.rki.coronawarnapp.test.menu.ui.TestMenuItem +import de.rki.coronawarnapp.util.di.AutoInject +import de.rki.coronawarnapp.util.ui.viewBindingLazy +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider +import de.rki.coronawarnapp.util.viewmodel.cwaViewModels +import javax.inject.Inject + +@SuppressLint("SetTextI18n") +class DeltaonboardingFragment : Fragment(R.layout.fragment_test_deltaonboarding), AutoInject { + + @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory + private val viewModel: DeltaOnboardingFragmentViewModel by cwaViewModels { viewModelFactory } + + private val binding: FragmentTestDeltaonboardingBinding by viewBindingLazy() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.switchContactJournalOnboarding.isChecked = viewModel.isContactJournalOnboardingDone() + binding.switchDeltaOnboarding.isChecked = viewModel.isDeltaOnboardingDone() + viewModel.changelogVersion.observe(viewLifecycleOwner) { + binding.lastChangelogEdittext.setText(it.toString()) + } + + binding.buttonSet.setOnClickListener { + val value = binding.lastChangelogEdittext.text.toString().toLong() + viewModel.updateChangelogVersion(value) + } + + binding.buttonClear.setOnClickListener { + viewModel.clearChangelogVersion() + } + + binding.buttonReset.setOnClickListener { + viewModel.resetChangelogVersion() + } + + binding.switchContactJournalOnboarding.setOnCheckedChangeListener { _, value -> + viewModel.setContactJournalOnboardingDone(value) + } + + binding.switchDeltaOnboarding.setOnCheckedChangeListener { _, value -> + viewModel.setDeltaOboardinDone(value) + } + } + + companion object { + val MENU_ITEM = TestMenuItem( + title = "Onboarding options", + description = "Set delta onboarding or new release screen", + targetId = R.id.test_deltaonboarding_fragment + ) + } +} diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt index 45d99a414bdc53b84a241f66ef74ebd777229696..6d09d4e6bc5d38de5e6556b177aa116a3a37b415 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt @@ -9,6 +9,7 @@ import de.rki.coronawarnapp.test.contactdiary.ui.ContactDiaryTestFragment import de.rki.coronawarnapp.test.crash.ui.SettingsCrashReportFragment import de.rki.coronawarnapp.test.datadonation.ui.DataDonationTestFragment import de.rki.coronawarnapp.test.debugoptions.ui.DebugOptionsFragment +import de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaonboardingFragment import de.rki.coronawarnapp.test.keydownload.ui.KeyDownloadTestFragment import de.rki.coronawarnapp.test.playground.ui.PlaygroundFragment import de.rki.coronawarnapp.test.risklevel.ui.TestRiskLevelCalculationFragment @@ -32,7 +33,8 @@ class TestMenuFragmentViewModel @AssistedInject constructor() : CWAViewModel() { MiscInfoFragment.MENU_ITEM, ContactDiaryTestFragment.MENU_ITEM, PlaygroundFragment.MENU_ITEM, - DataDonationTestFragment.MENU_ITEM + DataDonationTestFragment.MENU_ITEM, + DeltaonboardingFragment.MENU_ITEM ).let { MutableLiveData(it) } } val showTestScreenEvent = SingleLiveEvent<TestMenuItem>() diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt index c0511503a46fcdbcb518cc3415e2a36e0b4889c4..da9c43ddb5f631cfcd805aea75a1852960dd46ff 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt @@ -12,6 +12,8 @@ import de.rki.coronawarnapp.test.datadonation.ui.DataDonationTestFragment import de.rki.coronawarnapp.test.datadonation.ui.DataDonationTestFragmentModule import de.rki.coronawarnapp.test.debugoptions.ui.DebugOptionsFragment import de.rki.coronawarnapp.test.debugoptions.ui.DebugOptionsFragmentModule +import de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaOnboardingFragmentModule +import de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaonboardingFragment import de.rki.coronawarnapp.test.keydownload.ui.KeyDownloadTestFragment import de.rki.coronawarnapp.test.keydownload.ui.KeyDownloadTestFragmentModule import de.rki.coronawarnapp.test.menu.ui.TestMenuFragment @@ -60,4 +62,7 @@ abstract class MainActivityTestModule { @ContributesAndroidInjector(modules = [DataDonationTestFragmentModule::class]) abstract fun dataDonation(): DataDonationTestFragment + + @ContributesAndroidInjector(modules = [DeltaOnboardingFragmentModule::class]) + abstract fun deltaOnboarding(): DeltaonboardingFragment } diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_deltaonboarding.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_deltaonboarding.xml new file mode 100644 index 0000000000000000000000000000000000000000..374995566303f641149887fb280e156f3eec5f80 --- /dev/null +++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_deltaonboarding.xml @@ -0,0 +1,150 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fillViewport="true" + tools:ignore="HardcodedText"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="@dimen/spacing_tiny" + android:orientation="vertical"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/debug_container" + style="@style/Card" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="@dimen/spacing_tiny"> + + <TextView + android:id="@+id/delta_onboarding_title" + style="@style/headline6" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="New release info" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/explanation_label" + style="@style/body2" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:text="By changing the last changelog version you may invoke the 'New release' screen in the home screen." + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/delta_onboarding_title" /> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/last_changelog_layout" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:hint="Last chnagelog version" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/explanation_label"> + + <EditText + android:id="@+id/last_changelog_edittext" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="number" /> + + </com.google.android.material.textfield.TextInputLayout> + + <Button + android:id="@+id/button_set" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_tiny" + android:text="Update" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/last_changelog_layout" /> + + <Button + android:id="@+id/button_clear" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_tiny" + android:text="Clear" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/button_set" /> + + <Button + android:id="@+id/button_reset" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_tiny" + android:text="Reset" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/button_clear" /> + + + </androidx.constraintlayout.widget.ConstraintLayout> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/debug_container2" + style="@style/Card" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="@dimen/spacing_tiny"> + + <TextView + android:id="@+id/contact_journal_onboarding_title" + style="@style/headline6" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Contact Journal onboarding" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <com.google.android.material.switchmaterial.SwitchMaterial + android:id="@+id/switch_contact_journal_onboarding" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:text="Onboarding finished" + app:layout_constraintTop_toBottomOf="@id/contact_journal_onboarding_title" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/debug_container3" + style="@style/Card" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="@dimen/spacing_tiny"> + + <TextView + android:id="@+id/cdelta_onboarding_title" + style="@style/headline6" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Delta onboarding" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <com.google.android.material.switchmaterial.SwitchMaterial + android:id="@+id/switch_delta_onboarding" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:text="Onboarding finished" + app:layout_constraintTop_toBottomOf="@id/cdelta_onboarding_title" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + + </LinearLayout> +</androidx.core.widget.NestedScrollView> diff --git a/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml b/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml index efdf3476513872f03ceb96c1e4951e82525b808e..3fd5c94a377c5b2af530a939ceecc65a54017cc8 100644 --- a/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml +++ b/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml @@ -43,6 +43,9 @@ <action android:id="@+id/action_test_menu_fragment_to_dataDonationFragment" app:destination="@id/test_datadonation_fragment" /> + <action + android:id="@+id/action_test_menu_fragment_to_deltaonboardingFragment" + app:destination="@id/test_deltaonboarding_fragment" /> </fragment> <fragment @@ -116,4 +119,10 @@ tools:layout="@layout/fragment_test_datadonation" android:name="de.rki.coronawarnapp.test.datadonation.ui.DataDonationTestFragment" android:label="DataDonationFragment" /> + <fragment + android:id="@+id/test_deltaonboarding_fragment" + android:name="de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaonboardingFragment" + android:label="DeltaonboardingFragment" + tools:layout="@layout/fragment_test_deltaonboarding" /> + </navigation> diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt index e01721dc3e30acb05c08c3cf52134c0eb6ffd92a..3155f696f2dc4ed38b301836766f82adb3c1515e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt @@ -3,7 +3,9 @@ package de.rki.coronawarnapp.contactdiary.ui.onboarding import android.os.Bundle import android.view.View import android.view.accessibility.AccessibilityEvent +import androidx.core.net.toUri import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import de.rki.coronawarnapp.R import de.rki.coronawarnapp.contactdiary.ui.ContactDiarySettings @@ -15,6 +17,7 @@ import de.rki.coronawarnapp.util.ui.observe2 import de.rki.coronawarnapp.util.ui.viewBindingLazy import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModels +import org.joda.time.LocalDate import javax.inject.Inject class ContactDiaryOnboardingFragment : Fragment(R.layout.contact_diary_onboarding_fragment), AutoInject { @@ -31,7 +34,6 @@ class ContactDiaryOnboardingFragment : Fragment(R.layout.contact_diary_onboardin super.onViewCreated(view, savedInstanceState) binding.apply { contactDiaryOnboardingNextButton.setOnClickListener { - vm.onNextButtonClick() } if (!args.showBottomNav) { @@ -63,10 +65,17 @@ class ContactDiaryOnboardingFragment : Fragment(R.layout.contact_diary_onboardin ContactDiaryOnboardingNavigationEvents.NavigateToOverviewFragment -> { onboardingComplete() - doNavigate( - ContactDiaryOnboardingFragmentDirections - .actionContactDiaryOnboardingFragmentToContactDiaryOverviewFragment() - ) + if (arguments?.containsKey(OPEN_CURRENT_DAY) == true) { + findNavController().apply { + popBackStack(R.id.contactDiaryOnboardingFragment, true) + navigate("coronawarnapp://contact-journal/day/${LocalDate()}".toUri()) + } + } else { + doNavigate( + ContactDiaryOnboardingFragmentDirections + .actionContactDiaryOnboardingFragmentToContactDiaryOverviewFragment() + ) + } } } } @@ -80,4 +89,8 @@ class ContactDiaryOnboardingFragment : Fragment(R.layout.contact_diary_onboardin super.onResume() binding.contentContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) } + + companion object { + private const val OPEN_CURRENT_DAY = "goToDay" + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivity.kt index 8740ce471c92a2ff3b31c2c13d4295228a67fb3f..90c3fe4bce7c607f8ef654177a82015d632e8a30 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivity.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivity.kt @@ -9,6 +9,7 @@ import de.rki.coronawarnapp.R import de.rki.coronawarnapp.ui.main.MainActivity import de.rki.coronawarnapp.ui.onboarding.OnboardingActivity import de.rki.coronawarnapp.util.di.AppInjector +import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModels import javax.inject.Inject @@ -16,6 +17,7 @@ import javax.inject.Inject class LauncherActivity : AppCompatActivity() { @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory + private val vm: LauncherActivityViewModel by cwaViewModels( ownerProducer = { viewModelStore }, factoryProducer = { viewModelFactory } @@ -28,12 +30,12 @@ class LauncherActivity : AppCompatActivity() { vm.events.observe(this) { when (it) { LauncherEvent.GoToOnboarding -> { - OnboardingActivity.start(this) + OnboardingActivity.start(this, AppShortcutsHelper.getShortcutType(intent)) this.overridePendingTransition(0, 0) finish() } LauncherEvent.GoToMainActivity -> { - MainActivity.start(this) + MainActivity.start(this, AppShortcutsHelper.getShortcutType(intent)) this.overridePendingTransition(0, 0) finish() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModel.kt index d42ce56f03bf50c7fbc47a54ec23d3b458a1f5bc..acd7d63e6dc7763ab70d3da95733c8f1e61afded 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModel.kt @@ -2,6 +2,8 @@ package de.rki.coronawarnapp.ui.launcher import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.environment.BuildConfigWrap +import de.rki.coronawarnapp.main.CWASettings import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.update.UpdateChecker import de.rki.coronawarnapp.util.coroutine.DispatcherProvider @@ -11,7 +13,8 @@ import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory class LauncherActivityViewModel @AssistedInject constructor( private val updateChecker: UpdateChecker, - dispatcherProvider: DispatcherProvider + dispatcherProvider: DispatcherProvider, + private val cwaSettings: CWASettings, ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { val events = SingleLiveEvent<LauncherEvent>() @@ -21,12 +24,16 @@ class LauncherActivityViewModel @AssistedInject constructor( val updateResult = updateChecker.checkForUpdate() when { updateResult.isUpdateNeeded -> LauncherEvent.ShowUpdateDialog(updateResult.updateIntent?.invoke()!!) - LocalData.isOnboarded() -> LauncherEvent.GoToMainActivity - else -> LauncherEvent.GoToOnboarding + isJustInstalledOrUpdated() -> LauncherEvent.GoToOnboarding + else -> LauncherEvent.GoToMainActivity }.let { events.postValue(it) } } } + private fun isJustInstalledOrUpdated() = + !LocalData.isOnboarded() || !LocalData.isInteroperabilityShownAtLeastOnce || + cwaSettings.lastChangelogVersion.value < BuildConfigWrap.VERSION_CODE + @AssistedFactory interface Factory : SimpleCWAViewModelFactory<LauncherActivityViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt index a0c2547e99906dc956149bebca5d47edf4e317a5..f6984c21e47c7cae1c08ef02749d3d15df21926b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt @@ -7,21 +7,25 @@ import android.os.Bundle import android.provider.Settings import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.core.net.toUri import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.navigation.NavController import androidx.navigation.NavGraph +import com.google.android.material.bottomnavigation.BottomNavigationView import dagger.android.AndroidInjector import dagger.android.DispatchingAndroidInjector import dagger.android.HasAndroidInjector import de.rki.coronawarnapp.R import de.rki.coronawarnapp.contactdiary.retention.ContactDiaryWorkScheduler +import de.rki.coronawarnapp.contactdiary.ui.overview.ContactDiaryOverviewFragmentDirections import de.rki.coronawarnapp.databinding.ActivityMainBinding import de.rki.coronawarnapp.datadonation.analytics.worker.DataDonationAnalyticsScheduler import de.rki.coronawarnapp.deadman.DeadmanNotificationScheduler import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.ui.base.startActivitySafely import de.rki.coronawarnapp.ui.setupWithNavController2 +import de.rki.coronawarnapp.util.AppShortcuts import de.rki.coronawarnapp.util.CWADebug import de.rki.coronawarnapp.util.ConnectivityHelper import de.rki.coronawarnapp.util.DialogHelper @@ -31,6 +35,7 @@ import de.rki.coronawarnapp.util.ui.findNavController import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModels import de.rki.coronawarnapp.worker.BackgroundWorkScheduler +import org.joda.time.LocalDate import javax.inject.Inject /** @@ -42,8 +47,24 @@ import javax.inject.Inject */ class MainActivity : AppCompatActivity(), HasAndroidInjector { companion object { - fun start(context: Context) { - context.startActivity(Intent(context, MainActivity::class.java)) + private const val EXTRA_DATA = "shortcut" + + fun start(context: Context, shortcut: AppShortcuts? = null) { + val intent = Intent(context, MainActivity::class.java).apply { + if (shortcut != null) { + putExtra(EXTRA_DATA, shortcut.toString()) + flags = flags or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK + } + } + context.startActivity(intent) + } + + private fun getShortcutFromIntent(intent: Intent): AppShortcuts? { + val extra = intent.getStringExtra(EXTRA_DATA) + if (extra != null) { + return AppShortcuts.valueOf(extra) + } + return null } } @@ -91,6 +112,37 @@ class MainActivity : AppCompatActivity(), HasAndroidInjector { vm.isOnboardingDone.observe(this) { isOnboardingDone -> startNestedGraphDestination(navController, isOnboardingDone) } + + if (savedInstanceState == null) { + processExtraParameters() + } + } + + private fun processExtraParameters() { + when (getShortcutFromIntent(intent)) { + AppShortcuts.CONTACT_DIARY -> { + goToContactJournal() + } + } + } + + private fun goToContactJournal() { + val navController = supportFragmentManager.findNavController(R.id.nav_host_fragment) + findViewById<BottomNavigationView>(R.id.main_bottom_navigation).selectedItemId = + R.id.contact_diary_nav_graph + val nestedGraph = navController.graph.findNode(R.id.contact_diary_nav_graph) as NavGraph + + if (vm.isOnboardingDone.value == true) { + nestedGraph.startDestination = R.id.contactDiaryOverviewFragment + navController.navigate( + ContactDiaryOverviewFragmentDirections.actionContactDiaryOverviewFragmentToContactDiaryDayFragment( + selectedDay = LocalDate().toString() + ) + ) + } else { + nestedGraph.startDestination = R.id.contactDiaryOnboardingFragment + navController.navigate("coronawarnapp://contact-journal/oboarding/?goToDay=true".toUri()) + } } private fun startNestedGraphDestination(navController: NavController, isOnboardingDone: Boolean) { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityActions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityActions.kt new file mode 100644 index 0000000000000000000000000000000000000000..f836e972d735d5bb966c4ba88972daac197e6899 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityActions.kt @@ -0,0 +1,5 @@ +package de.rki.coronawarnapp.ui.main + +enum class MainActivityActions { + ADD_DIARY_ENTRY +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt index f4c94877b38e49d3153cdcc70f89e5d24a258838..7db7bcbe8087531746be32bc27e7c5473de74daa 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt @@ -1,5 +1,7 @@ package de.rki.coronawarnapp.ui.main +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.contactdiary.ui.ContactDiarySettings @@ -27,7 +29,8 @@ class MainActivityViewModel @AssistedInject constructor( val showBackgroundJobDisabledNotification = SingleLiveEvent<Unit>() val showEnergyOptimizedEnabledForBackground = SingleLiveEvent<Unit>() - val isOnboardingDone = SingleLiveEvent<Boolean>() + private val mutableIsOnboardingDone = MutableLiveData<Boolean>() + val isOnboardingDone: LiveData<Boolean> = mutableIsOnboardingDone init { if (CWADebug.isDeviceForTestersBuild) { @@ -64,7 +67,7 @@ class MainActivityViewModel @AssistedInject constructor( } fun onBottomNavSelected() { - isOnboardingDone.value = contactDiarySettings.isOnboardingDone + mutableIsOnboardingDone.value = contactDiarySettings.isOnboardingDone } private suspend fun checkForEnergyOptimizedEnabled() { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt index 0689d01448e93b884529c2396fb1d98f6a610bac..cf4fa15014bc8b981fe90186c02150ba84bc6b2f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt @@ -80,11 +80,6 @@ class HomeFragment : Fragment(R.layout.home_fragment_layout), AutoInject { vm.popupEvents.observe2(this) { event -> when (event) { - HomeFragmentEvents.ShowInteropDeltaOnboarding -> { - doNavigate( - HomeFragmentDirections.actionMainFragmentToOnboardingDeltaInteroperabilityFragment() - ) - } is HomeFragmentEvents.ShowTracingExplanation -> { tracingExplanationDialog.show(event.activeTracingDaysInRetentionPeriod) { vm.tracingExplanationWasShown() @@ -97,11 +92,6 @@ class HomeFragment : Fragment(R.layout.home_fragment_layout), AutoInject { ) } HomeFragmentEvents.ShowDeleteTestDialog -> showRemoveTestDialog() - - HomeFragmentEvents.ShowNewReleaseFragment -> doNavigate( - HomeFragmentDirections.actionMainFragmentToNewReleaseInfoFragment(false) - ) - HomeFragmentEvents.GoToStatisticsExplanation -> doNavigate( HomeFragmentDirections.actionMainFragmentToStatisticsExplanationFragment() ) @@ -111,7 +101,7 @@ class HomeFragment : Fragment(R.layout.home_fragment_layout), AutoInject { } } - vm.showPopUpsOrNavigate() + vm.showPopUps() vm.showLoweredRiskLevelDialog.observe2(this) { if (it) showRiskLevelLoweredDialog() @@ -127,6 +117,7 @@ class HomeFragment : Fragment(R.layout.home_fragment_layout), AutoInject { override fun onResume() { super.onResume() vm.refreshRequiredData() + vm.restoreAppShortcuts() binding.container.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt index 422dd904a258620d1727cb5f893a2d46c64fa0ad..595e0aa4f4b7a69e4f8d2267f6430c70597731c7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt @@ -1,7 +1,6 @@ package de.rki.coronawarnapp.ui.main.home sealed class HomeFragmentEvents { - object ShowInteropDeltaOnboarding : HomeFragmentEvents() data class ShowTracingExplanation( val activeTracingDaysInRetentionPeriod: Long @@ -13,7 +12,5 @@ sealed class HomeFragmentEvents { object ShowReactivateRiskCheckDialog : HomeFragmentEvents() - object ShowNewReleaseFragment : HomeFragmentEvents() - object GoToStatisticsExplanation : HomeFragmentEvents() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt index bb3950f1568e551acd8769870541a9a752027efc..86309e8ec5e3746a6f683f364e0117eb8a5c6be4 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt @@ -7,7 +7,6 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.deadman.DeadmanNotificationScheduler -import de.rki.coronawarnapp.environment.BuildConfigWrap import de.rki.coronawarnapp.main.CWASettings import de.rki.coronawarnapp.notification.ShareTestResultNotificationService import de.rki.coronawarnapp.risk.TimeVariables @@ -50,7 +49,6 @@ import de.rki.coronawarnapp.tracing.ui.homecards.TracingProgressCard import de.rki.coronawarnapp.tracing.ui.statusbar.TracingHeaderState import de.rki.coronawarnapp.tracing.ui.statusbar.toHeaderState import de.rki.coronawarnapp.ui.main.home.HomeFragmentEvents.ShowErrorResetDialog -import de.rki.coronawarnapp.ui.main.home.HomeFragmentEvents.ShowInteropDeltaOnboarding import de.rki.coronawarnapp.ui.main.home.HomeFragmentEvents.ShowTracingExplanation import de.rki.coronawarnapp.ui.main.home.items.FAQCard import de.rki.coronawarnapp.ui.main.home.items.HomeItem @@ -59,6 +57,7 @@ import de.rki.coronawarnapp.util.DeviceUIState import de.rki.coronawarnapp.util.NetworkRequestWrapper.Companion.withSuccess import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.security.EncryptionErrorResetTool +import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory @@ -81,7 +80,8 @@ class HomeFragmentViewModel @AssistedInject constructor( private val cwaSettings: CWASettings, appConfigProvider: AppConfigProvider, statisticsProvider: StatisticsProvider, - private val deadmanNotificationScheduler: DeadmanNotificationScheduler + private val deadmanNotificationScheduler: DeadmanNotificationScheduler, + private val appShortcutsHelper: AppShortcutsHelper ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { private val tracingStateProvider by lazy { tracingStateProviderFactory.create(isDetailsMode = false) } @@ -95,29 +95,19 @@ class HomeFragmentViewModel @AssistedInject constructor( val popupEvents = SingleLiveEvent<HomeFragmentEvents>() - fun showPopUpsOrNavigate() { - when { - !LocalData.isInteroperabilityShownAtLeastOnce -> { - popupEvents.postValue(ShowInteropDeltaOnboarding) - } - cwaSettings.lastChangelogVersion.value < BuildConfigWrap.VERSION_CODE -> { - popupEvents.postValue(HomeFragmentEvents.ShowNewReleaseFragment) + fun showPopUps() { + launch { + if (!LocalData.tracingExplanationDialogWasShown()) { + popupEvents.postValue( + ShowTracingExplanation( + TimeVariables.getActiveTracingDaysInRetentionPeriod() + ) + ) } - else -> { - launch { - if (!LocalData.tracingExplanationDialogWasShown()) { - popupEvents.postValue( - ShowTracingExplanation( - TimeVariables.getActiveTracingDaysInRetentionPeriod() - ) - ) - } - } - launch { - if (errorResetTool.isResetNoticeToBeShown) { - popupEvents.postValue(ShowErrorResetDialog) - } - } + } + launch { + if (errorResetTool.isResetNoticeToBeShown) { + popupEvents.postValue(ShowErrorResetDialog) } } } @@ -307,6 +297,12 @@ class HomeFragmentViewModel @AssistedInject constructor( } } + fun restoreAppShortcuts() { + launch { + appShortcutsHelper.restoreAppShortcut() + } + } + fun tracingExplanationWasShown() { LocalData.tracingExplanationDialogWasShown(true) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivity.kt index a6b5218dbb30dd70cd26729978092a90712e2e02..fe8a9252ff56112046101f42edf4e0a5c6ff8861 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivity.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivity.kt @@ -15,6 +15,7 @@ import de.rki.coronawarnapp.environment.BuildConfigWrap import de.rki.coronawarnapp.main.CWASettings import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.ui.main.MainActivity +import de.rki.coronawarnapp.util.AppShortcuts import de.rki.coronawarnapp.util.di.AppInjector import javax.inject.Inject @@ -26,11 +27,21 @@ import javax.inject.Inject class OnboardingActivity : AppCompatActivity(), LifecycleObserver, HasAndroidInjector { companion object { private val TAG: String? = OnboardingActivity::class.simpleName + private const val EXTRA_DATA = "shortcut" - fun start(context: Context) { - val intent = Intent(context, OnboardingActivity::class.java) + fun start(context: Context, shortcut: AppShortcuts? = null) { + val intent = Intent(context, OnboardingActivity::class.java).apply { + putExtra(EXTRA_DATA, shortcut?.toString()) + } context.startActivity(intent) } + + fun getShortcutFromIntent(intent: Intent?): AppShortcuts? { + intent?.getStringExtra(EXTRA_DATA)?.let { + return AppShortcuts.valueOf(it) + } + return null + } } @Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any> diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivityModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivityModule.kt index 34bc9a8a6129b6cdc3beaa7c444df8f77f791617..3c52d5e7cc18f9ebf6d42522e206dc7d718ab0b9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivityModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingActivityModule.kt @@ -4,6 +4,8 @@ import dagger.Module import dagger.android.ContributesAndroidInjector import de.rki.coronawarnapp.datadonation.analytics.ui.AnalyticsUIModule import de.rki.coronawarnapp.datadonation.analytics.ui.input.AnalyticsUserInputFragment +import de.rki.coronawarnapp.release.NewReleaseInfoFragment +import de.rki.coronawarnapp.release.NewReleaseInfoFragmentModule @Module internal abstract class OnboardingActivityModule { @@ -16,14 +18,31 @@ internal abstract class OnboardingActivityModule { @ContributesAndroidInjector(modules = [OnboardingTracingModule::class]) abstract fun onboardingScreen(): OnboardingTracingFragment + @ContributesAndroidInjector(modules = [OnboardingPrivacyModule::class]) abstract fun onboardingPrivacyFragment(): OnboardingPrivacyFragment + @ContributesAndroidInjector(modules = [OnboardingTestModule::class]) abstract fun onboardingTestFragment(): OnboardingTestFragment + @ContributesAndroidInjector(modules = [OnboardingNotificationsModule::class]) abstract fun onboardingNotificationsFragment(): OnboardingNotificationsFragment + @ContributesAndroidInjector(modules = [OnboardingAnalyticsModule::class]) abstract fun onboardingAnalyticsFragment(): OnboardingAnalyticsFragment + @ContributesAndroidInjector(modules = [AnalyticsUIModule::class]) abstract fun ppaUserInfoSelection(): AnalyticsUserInputFragment + + @ContributesAndroidInjector(modules = [OnboardingLoadingModule::class]) + abstract fun onboardingLoadingScreen(): OnboardingLoadingFragment + + @ContributesAndroidInjector(modules = [NewReleaseInfoFragmentModule::class]) + abstract fun newReleaseInfoFragment(): NewReleaseInfoFragment + + @ContributesAndroidInjector(modules = [OnboardingDeltaInteroperabilityModule::class]) + abstract fun onboardingDeltaInteroperabilityFragment(): OnboardingDeltaInteroperabilityFragment + + @ContributesAndroidInjector(modules = [OnboardingDeltaAnalyticsModule::class]) + abstract fun onboardingDeltaAnalyticsFragment(): OnboardingDeltaAnalyticsFragment } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragment.kt index 595e10ca90bad238e1a3e1031c604f337a7da653..52710365102bf8daf3bf875db70d5a1898be829f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingDeltaInteroperabilityFragment.kt @@ -8,10 +8,10 @@ import androidx.navigation.fragment.findNavController import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentOnboardingDeltaInteroperabilityBinding import de.rki.coronawarnapp.ui.doNavigate -import de.rki.coronawarnapp.ui.main.MainActivity import de.rki.coronawarnapp.util.convertToHyperlink import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.ui.observe2 +import de.rki.coronawarnapp.util.ui.popBackStack import de.rki.coronawarnapp.util.ui.viewBindingLazy import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModels @@ -53,7 +53,7 @@ class OnboardingDeltaInteroperabilityFragment : vm.navigateBack.observe2(this) { if (it) { - (requireActivity() as MainActivity).goBack() + popBackStack() } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..7d7c6475ff8122c86e7ae6b8e42260c447f8da71 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingFragment.kt @@ -0,0 +1,53 @@ +package de.rki.coronawarnapp.ui.onboarding + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ui.main.MainActivity +import de.rki.coronawarnapp.util.di.AutoInject +import de.rki.coronawarnapp.util.ui.doNavigate +import de.rki.coronawarnapp.util.ui.observe2 +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider +import de.rki.coronawarnapp.util.viewmodel.cwaViewModels +import javax.inject.Inject + +class OnboardingLoadingFragment : Fragment(R.layout.onboaring_loading_layout), AutoInject { + + @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory + private val viewModel: OnboardingLoadingViewModel by cwaViewModels( + ownerProducer = { requireActivity().viewModelStore }, + factoryProducer = { viewModelFactory } + ) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewModel.navigationEvents.observe2(this) { event -> + when (event) { + OnboardingFragmentEvents.ShowInteropDeltaOnboarding -> + doNavigate( + OnboardingLoadingFragmentDirections + .actionLoadingFragmentToOnboardingDeltaInteroperabilityFragment() + ) + OnboardingFragmentEvents.ShowNewReleaseFragment -> + doNavigate( + OnboardingLoadingFragmentDirections + .actionLoadingFragmentToNewReleaseInfoFragment() + ) + OnboardingFragmentEvents.ShowOnboarding -> + doNavigate( + OnboardingLoadingFragmentDirections + .actionLoadingFragmentToOnboardingFragment() + ) + OnboardingFragmentEvents.OnboardingDone -> { + MainActivity.start(requireContext(), OnboardingActivity.getShortcutFromIntent(activity?.intent)) + activity?.overridePendingTransition(0, 0) + activity?.finish() + } + } + } + + viewModel.navigate() + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..40bb8156450d950e5780e5bbde58f5568c762855 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingModule.kt @@ -0,0 +1,22 @@ +package de.rki.coronawarnapp.ui.onboarding + +import dagger.Binds +import dagger.Module +import dagger.android.ContributesAndroidInjector +import dagger.multibindings.IntoMap +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey + +@Module +abstract class OnboardingLoadingModule { + @Binds + @IntoMap + @CWAViewModelKey(OnboardingLoadingViewModel::class) + abstract fun onboardingLoadingVM( + factory: OnboardingLoadingViewModel.Factory + ): CWAViewModelFactory<out CWAViewModel> + + @ContributesAndroidInjector + abstract fun onboardingLoadingFragment(): OnboardingLoadingFragment +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..57dbcfc194b293b008651726c8c4dc7124f45fc5 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/onboarding/OnboardingLoadingViewModel.kt @@ -0,0 +1,46 @@ +package de.rki.coronawarnapp.ui.onboarding + +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.environment.BuildConfigWrap +import de.rki.coronawarnapp.main.CWASettings +import de.rki.coronawarnapp.storage.LocalData +import de.rki.coronawarnapp.util.ui.SingleLiveEvent +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory + +class OnboardingLoadingViewModel @AssistedInject constructor(private val cwaSettings: CWASettings) : CWAViewModel() { + + val navigationEvents = SingleLiveEvent<OnboardingFragmentEvents>() + + fun navigate() { + when { + !LocalData.isOnboarded() -> { + navigationEvents.postValue(OnboardingFragmentEvents.ShowOnboarding) + } + !LocalData.isInteroperabilityShownAtLeastOnce -> { + navigationEvents.postValue(OnboardingFragmentEvents.ShowInteropDeltaOnboarding) + } + cwaSettings.lastChangelogVersion.value < BuildConfigWrap.VERSION_CODE -> { + navigationEvents.postValue(OnboardingFragmentEvents.ShowNewReleaseFragment) + } + else -> { + navigationEvents.postValue(OnboardingFragmentEvents.OnboardingDone) + } + } + } + + @AssistedFactory + interface Factory : SimpleCWAViewModelFactory<OnboardingLoadingViewModel> +} + +sealed class OnboardingFragmentEvents { + + object ShowInteropDeltaOnboarding : OnboardingFragmentEvents() + + object ShowNewReleaseFragment : OnboardingFragmentEvents() + + object ShowOnboarding : OnboardingFragmentEvents() + + object OnboardingDone : OnboardingFragmentEvents() +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableFragment.kt index 6a265ce44d9184285e656b5dc74af6a4e4076ccc..c498a79425bd19d785fbf8635f6b5810cba0bd9b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/resultavailable/SubmissionTestResultAvailableFragment.kt @@ -12,6 +12,7 @@ import de.rki.coronawarnapp.tracing.ui.TracingConsentDialog import de.rki.coronawarnapp.ui.submission.SubmissionBlockingDialog import de.rki.coronawarnapp.util.DialogHelper import de.rki.coronawarnapp.util.di.AutoInject +import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper import de.rki.coronawarnapp.util.ui.doNavigate import de.rki.coronawarnapp.util.ui.observe2 import de.rki.coronawarnapp.util.ui.viewBindingLazy @@ -26,6 +27,7 @@ import javax.inject.Inject */ class SubmissionTestResultAvailableFragment : Fragment(R.layout.fragment_submission_test_result_available), AutoInject { + @Inject lateinit var appShortcutsHelper: AppShortcutsHelper @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory private val vm: SubmissionTestResultAvailableViewModel by cwaViewModels { viewModelFactory } private val binding: FragmentSubmissionTestResultAvailableBinding by viewBindingLazy() @@ -88,6 +90,7 @@ class SubmissionTestResultAvailableFragment : Fragment(R.layout.fragment_submiss override fun onResume() { super.onResume() binding.submissionTestResultAvailableContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) + appShortcutsHelper.removeAppShortcut() } private fun showCloseDialog() { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/AppShortcuts.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/AppShortcuts.kt new file mode 100644 index 0000000000000000000000000000000000000000..b4b340aab0ba3df0a32615936c7d3d420d448722 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/AppShortcuts.kt @@ -0,0 +1,5 @@ +package de.rki.coronawarnapp.util + +enum class AppShortcuts { + CONTACT_DIARY +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/shortcuts/AppShortcutsHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/shortcuts/AppShortcutsHelper.kt new file mode 100644 index 0000000000000000000000000000000000000000..0aeb1345d353a2b058806d25494139d272b83d14 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/shortcuts/AppShortcutsHelper.kt @@ -0,0 +1,54 @@ +package de.rki.coronawarnapp.util.shortcuts + +import android.content.Context +import android.content.Intent +import androidx.core.content.pm.ShortcutInfoCompat +import androidx.core.content.pm.ShortcutManagerCompat +import androidx.core.graphics.drawable.IconCompat +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ui.launcher.LauncherActivity +import de.rki.coronawarnapp.util.AppShortcuts +import de.rki.coronawarnapp.util.di.AppContext +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AppShortcutsHelper @Inject constructor(@AppContext private val context: Context) { + + suspend fun restoreAppShortcut() = withContext(Dispatchers.IO) { + if (ShortcutManagerCompat.getDynamicShortcuts(context).size == 0) { + val shortcut = ShortcutInfoCompat.Builder(context, CONTACT_DIARY_SHORTCUT_ID) + .setShortLabel(context.getString(R.string.app_shortcut_contact_diary_title)) + .setLongLabel(context.getString(R.string.app_shortcut_contact_diary_title)) + .setIcon(IconCompat.createWithResource(context, R.drawable.ic_contact_diary_shortcut_icon)) + .setIntent(createContactDiaryIntent()) + .build() + + ShortcutManagerCompat.addDynamicShortcuts(context, listOf(shortcut)) + } + } + + fun removeAppShortcut() { + ShortcutManagerCompat.removeDynamicShortcuts(context, listOf(CONTACT_DIARY_SHORTCUT_ID)) + } + + private fun createContactDiaryIntent() = Intent(context, LauncherActivity::class.java).apply { + action = Intent.ACTION_VIEW + putExtra(SHORTCUT_EXTRA_ID, AppShortcuts.CONTACT_DIARY.toString()) + } + + companion object { + private const val CONTACT_DIARY_SHORTCUT_ID = "contact_diary_id" + private const val SHORTCUT_EXTRA_ID = "shortcut_extra" + + fun getShortcutType(intent: Intent): AppShortcuts? { + intent.getStringExtra(SHORTCUT_EXTRA_ID)?.let { + return AppShortcuts.valueOf(it) + } + + return null + } + } +} diff --git a/Corona-Warn-App/src/main/res/drawable/ic_contact_diary_shortcut_icon.xml b/Corona-Warn-App/src/main/res/drawable/ic_contact_diary_shortcut_icon.xml new file mode 100644 index 0000000000000000000000000000000000000000..b032e95724c50691682b18b343ded2797988b7b2 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_contact_diary_shortcut_icon.xml @@ -0,0 +1,12 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="34dp" + android:height="34dp" + android:viewportWidth="34" + android:viewportHeight="34"> + <path + android:pathData="M17,17m-17,0a17,17 0,1 1,34 0a17,17 0,1 1,-34 0" + android:fillColor="#F5F5F5"/> + <path + android:pathData="M24.3816,24.6345V10.1056C24.3816,8.8422 23.476,8 22.2126,8H11.9787C10.9164,8 9.9932,9.0445 10,10.1056C10,14.7983 10,19.7317 10,24.4238C10,25.6872 10.8425,26.5294 12.1546,26.5294H23.8971C23.8971,26.5294 24.3182,26.5294 24.3182,26.1083C24.3182,25.6872 24.3182,25.266 24.3182,25.266C24.3182,25.266 24.3182,24.8496 23.8971,24.8449C23.476,24.8402 12.948,24.8449 12.948,24.8449C12.948,24.8449 11.6845,24.8449 11.6845,23.5816C11.6845,23.0036 11.6845,22.7393 11.6845,22.7393C11.6845,21.4759 12.948,21.4759 12.948,21.4759C11.8333,21.4759 21.1642,21.4759 23.4759,21.4759C23.4759,21.4759 23.0548,22.3182 23.0548,23.1604C23.0548,23.6429 23.4759,24.6345 23.4759,24.6345C23.8971,24.6345 24.3816,24.6345 24.3816,24.6345Z" + android:fillColor="#777677"/> +</vector> diff --git a/Corona-Warn-App/src/main/res/layout/onboaring_loading_layout.xml b/Corona-Warn-App/src/main/res/layout/onboaring_loading_layout.xml new file mode 100644 index 0000000000000000000000000000000000000000..ab53df91a84d6d51d7405b5a7643e43ab24d6a6b --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/onboaring_loading_layout.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + <ProgressBar + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + android:layout_width="32dp" + android:layout_height="32dp" /> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/navigation/contact_diary_nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/contact_diary_nav_graph.xml index a11f6a23613373c2b2692c2a8450b60d447d676f..97572c5b032ecd86fc0757b48b245b2fd8103007 100644 --- a/Corona-Warn-App/src/main/res/navigation/contact_diary_nav_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/contact_diary_nav_graph.xml @@ -19,6 +19,7 @@ <action android:id="@+id/action_contactDiaryDayFragment_to_contactDiaryLocationBottomSheetDialogFragment" app:destination="@id/contactDiaryLocationBottomSheetDialogFragment" /> + <deepLink app:uri="coronawarnapp://contact-journal/day/{selectedDay}" /> </fragment> <fragment android:id="@+id/contactDiaryPersonListFragment" @@ -87,6 +88,9 @@ android:name="showBottomNav" android:defaultValue="true" app:argType="boolean" /> + <deepLink app:uri="coronawarnapp://contact-journal/oboarding/?goToDay={goToDay}" + app:popUpTo="@id/contact_diary_nav_graph" + app:popUpToInclusive="true" /> </fragment> <fragment android:id="@+id/contactDiaryInformationPrivacyFragment" diff --git a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml index 5bfc4298ee70fe6e40a186094e9eb81731265b53..b4fe423e381104f4636848740dbe208f909fe8a7 100644 --- a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml @@ -49,18 +49,12 @@ <action android:id="@+id/action_mainFragment_to_submissionDispatcher" app:destination="@id/submissionDispatcherFragment" /> - <action - android:id="@+id/action_mainFragment_to_onboardingDeltaInteroperabilityFragment" - app:destination="@id/onboardingDeltaInteroperabilityFragment" /> <action android:id="@+id/action_mainFragment_to_test_nav_graph" app:destination="@id/test_nav_graph" /> <action android:id="@+id/action_mainFragment_to_submissionTestResultAvailableFragment" app:destination="@id/submissionTestResultAvailableFragment" /> - <action - android:id="@+id/action_mainFragment_to_newReleaseInfoFragment" - app:destination="@id/newReleaseInfoFragment" /> <action android:id="@+id/action_mainFragment_to_statisticsExplanationFragment" app:destination="@id/statisticsExplanationFragment" /> diff --git a/Corona-Warn-App/src/main/res/navigation/nav_graph_onboarding.xml b/Corona-Warn-App/src/main/res/navigation/nav_graph_onboarding.xml index 0e2a1bc2bb30cd407ad9e132f4e26c7b10ccfa4a..3c128a61f3a3f9b98e3b3efcd31e7167af410cf7 100644 --- a/Corona-Warn-App/src/main/res/navigation/nav_graph_onboarding.xml +++ b/Corona-Warn-App/src/main/res/navigation/nav_graph_onboarding.xml @@ -3,8 +3,23 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/nav_graph_onboarding" - app:startDestination="@id/onboardingFragment"> + app:startDestination="@id/loadingFragment"> + <fragment + android:id="@+id/loadingFragment" + android:name="de.rki.coronawarnapp.ui.onboarding.OnboardingLoadingFragment" + android:label="LoadingFragment" + tools:layout="@layout/onboaring_loading_layout"> + <action + android:id="@+id/action_loadingFragment_to_newReleaseInfoFragment" + app:destination="@id/newReleaseInfoFragment" /> + <action + android:id="@+id/action_loadingFragment_to_onboardingDeltaInteroperabilityFragment" + app:destination="@id/onboardingDeltaInteroperabilityFragment2" /> + <action + android:id="@+id/action_loadingFragment_to_onboardingFragment" + app:destination="@id/onboardingFragment" /> + </fragment> <fragment android:id="@+id/onboardingFragment" android:name="de.rki.coronawarnapp.ui.onboarding.OnboardingFragment" @@ -76,4 +91,48 @@ android:name="de.rki.coronawarnapp.datadonation.analytics.ui.PpaMoreInfoFragment" android:label="PpaMoreInfoFragment" tools:layout="@layout/fragment_ppa_more_info" /> + + <!-- New Release --> + <fragment + android:id="@+id/newReleaseInfoFragment" + android:name="de.rki.coronawarnapp.release.NewReleaseInfoFragment" + tools:layout="@layout/new_release_info_screen_fragment" + android:label="NewReleaseInfoFragment"> + <argument + android:name="comesFromInfoScreen" + android:defaultValue="false" + app:argType="boolean" /> + <action + android:id="@+id/action_newReleaseInfoFragment_to_onboardingDeltaAnalyticsFragment" + app:destination="@id/onboardingDeltaAnalyticsFragment" + app:popUpTo="@id/loadingFragment" + app:popUpToInclusive="false" /> + </fragment> + <fragment + android:id="@+id/onboardingDeltaAnalyticsFragment" + android:name="de.rki.coronawarnapp.ui.onboarding.OnboardingDeltaAnalyticsFragment" + android:label="OnboardingDeltaAnalyticsFragment" + tools:layout="@layout/fragment_onboarding_delta_ppa"> + <action + android:id="@+id/action_onboardingDeltaAnalyticsFragment_to_analyticsUserInputFragment" + app:destination="@id/analyticsUserInputFragment" /> + <action + android:id="@+id/action_onboardingDeltaAnalyticsFragment_to_ppaMoreInfoFragment" + app:destination="@id/ppaMoreInfoFragment" /> + </fragment> + <fragment + android:id="@+id/onboardingDeltaInteroperabilityFragment2" + android:name="de.rki.coronawarnapp.ui.onboarding.OnboardingDeltaInteroperabilityFragment" + android:label="OnboardingDeltaInteroperabilityFragment" + tools:layout="@layout/fragment_onboarding_delta_interoperability"> + <action + android:id="@+id/action_onboardingDeltaInteroperabilityFragment_to_informationTermsFragment" + app:destination="@id/informationTermsFragment" /> + </fragment> + + <fragment + android:id="@+id/informationTermsFragment" + android:name="de.rki.coronawarnapp.ui.information.InformationTermsFragment" + android:label="@layout/fragment_information_terms" + tools:layout="@layout/fragment_information_terms" /> </navigation> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-de/strings.xml b/Corona-Warn-App/src/main/res/values-de/strings.xml index baa753516352ada5d7007222af7e080782478cba..21ad6bdfe6638df5d6ae9b85f666dea8c8513f84 100644 --- a/Corona-Warn-App/src/main/res/values-de/strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/strings.xml @@ -1755,6 +1755,9 @@ <!-- XBUT: Abort button for test result positive no consent screen --> <string name="submission_test_result_positive_no_consent_button_abort">Abbrechen</string> + <!-- XHED: Title of contact diary app shortcut --> + <string name="app_shortcut_contact_diary_title">"Eintrag hinzufügen"</string> + <!-- XTXT: Statistics information (Accessibilty) --> <string name="statistics_info_button">"Weitere Informationen"</string> <!-- XHED: Title for statistics reproduction card --> @@ -1878,4 +1881,5 @@ <string name="analytics_userinput_district_title">Ihr Kreis/Bezirk</string> <!-- XTXT: Analytics voluntary user input, district: UNSPECIFIED --> <string name="analytics_userinput_district_unspecified">keine Angabe</string> + </resources> diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml index de1bd20084af2a084408b0bc6b3b8c904a8b6e2a..1971d65abbab3b0d66f688e25ac1e108cb42febe 100644 --- a/Corona-Warn-App/src/main/res/values/strings.xml +++ b/Corona-Warn-App/src/main/res/values/strings.xml @@ -1770,6 +1770,9 @@ <!-- XBUT: Abort button for test result positive no consent screen --> <string name="submission_test_result_positive_no_consent_button_abort">"Cancel"</string> + <!-- XHED: Title of contact diary app shortcut --> + <string name="app_shortcut_contact_diary_title">"Eintrag hinzufügen"</string> + <!-- XTXT: Statistics information (Accessibilty) --> <string name="statistics_info_button">"Further information"</string> <!-- XHED: Title for statistics reproduction card --> diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt index c7c651a717af120aeb9a78af50b72de03f638623..0ffe699cec58e0430ac21977fba37fa706189f81 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt @@ -24,6 +24,7 @@ import de.rki.coronawarnapp.util.DeviceUIState.PAIRED_POSITIVE import de.rki.coronawarnapp.util.DeviceUIState.PAIRED_POSITIVE_TELETAN import de.rki.coronawarnapp.util.NetworkRequestWrapper import de.rki.coronawarnapp.util.security.EncryptionErrorResetTool +import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations import io.mockk.clearAllMocks @@ -65,6 +66,7 @@ class HomeFragmentViewModelTest : BaseTest() { @MockK lateinit var appConfigProvider: AppConfigProvider @MockK lateinit var statisticsProvider: StatisticsProvider @MockK lateinit var deadmanNotificationScheduler: DeadmanNotificationScheduler + @MockK lateinit var appShortcutsHelper: AppShortcutsHelper @BeforeEach fun setup() { @@ -100,7 +102,8 @@ class HomeFragmentViewModelTest : BaseTest() { cwaSettings = cwaSettings, appConfigProvider = appConfigProvider, statisticsProvider = statisticsProvider, - deadmanNotificationScheduler = deadmanNotificationScheduler + deadmanNotificationScheduler = deadmanNotificationScheduler, + appShortcutsHelper = appShortcutsHelper ) @Test @@ -196,16 +199,10 @@ class HomeFragmentViewModelTest : BaseTest() { every { errorResetTool.isResetNoticeToBeShown } returns false andThen true with(createInstance()) { - showPopUpsOrNavigate() - popupEvents.getOrAwaitValue() shouldBe HomeFragmentEvents.ShowInteropDeltaOnboarding - - showPopUpsOrNavigate() - popupEvents.getOrAwaitValue() shouldBe HomeFragmentEvents.ShowNewReleaseFragment - - showPopUpsOrNavigate() + showPopUps() popupEvents.getOrAwaitValue() shouldBe HomeFragmentEvents.ShowTracingExplanation(1) - showPopUpsOrNavigate() + showPopUps() popupEvents.getOrAwaitValue() shouldBe HomeFragmentEvents.ShowErrorResetDialog } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModelTest.kt index 63b1203140a7d028f8dce3f052407a3439210f97..930986dbe941afee2117dae7a527aed1fc0afb96 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/launcher/LauncherActivityViewModelTest.kt @@ -1,5 +1,7 @@ package de.rki.coronawarnapp.ui.launcher +import de.rki.coronawarnapp.environment.BuildConfigWrap +import de.rki.coronawarnapp.main.CWASettings import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.update.UpdateChecker import io.kotest.matchers.shouldBe @@ -16,11 +18,13 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import testhelpers.TestDispatcherProvider import testhelpers.extensions.InstantExecutorExtension +import testhelpers.preferences.mockFlowPreference @ExtendWith(InstantExecutorExtension::class) class LauncherActivityViewModelTest { @MockK lateinit var updateChecker: UpdateChecker + @MockK lateinit var cwaSettings: CWASettings @BeforeEach fun setupFreshViewModel() { @@ -29,12 +33,16 @@ class LauncherActivityViewModelTest { mockkObject(LocalData) every { LocalData.isOnboarded() } returns false + mockkObject(BuildConfigWrap) + every { BuildConfigWrap.VERSION_CODE } returns 10L + coEvery { updateChecker.checkForUpdate() } returns UpdateChecker.Result(isUpdateNeeded = false) } private fun createViewModel() = LauncherActivityViewModel( updateChecker = updateChecker, - dispatcherProvider = TestDispatcherProvider() + dispatcherProvider = TestDispatcherProvider(), + cwaSettings = cwaSettings ) @Test @@ -59,6 +67,8 @@ class LauncherActivityViewModelTest { @Test fun `onboarding finished`() { every { LocalData.isOnboarded() } returns true + every { LocalData.isInteroperabilityShownAtLeastOnce } returns true + every { cwaSettings.lastChangelogVersion } returns mockFlowPreference(10L) val vm = createViewModel()