Customizable lock screen affordance entry-point. am: abda67b66e
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/ThemePicker/+/20601630
Change-Id: I6078aef1e634239fe6f61ad68468a397f2f45c7e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/res/layout/keyguard_quick_affordance_section_view.xml b/res/layout/keyguard_quick_affordance_section_view.xml
new file mode 100644
index 0000000..4e14fc8
--- /dev/null
+++ b/res/layout/keyguard_quick_affordance_section_view.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ 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
+
+ http://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.
+-->
+<com.android.customization.picker.quickaffordance.ui.view.KeyguardQuickAffordanceSectionView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?selectableItemBackground"
+ android:clickable="true"
+ android:paddingVertical="@dimen/section_top_padding"
+ android:paddingHorizontal="@dimen/section_horizontal_padding"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/keyguard_quick_affordance_title"
+ style="@style/SectionTitleTextStyle" />
+
+ <TextView
+ android:id="@+id/keyguard_quick_affordance_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/SectionSubtitleTextStyle"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="@dimen/option_tile_width"
+ android:layout_height="@dimen/option_tile_width"
+ android:orientation="horizontal"
+ android:background="@drawable/option_border_color"
+ android:importantForAccessibility="noHideDescendants"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/icon_1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
+ <View
+ android:id="@+id/icon_spacer"
+ android:layout_width="14dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+
+ <ImageView
+ android:id="@+id/icon_2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+
+</com.android.customization.picker.quickaffordance.ui.view.KeyguardQuickAffordanceSectionView>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f645dc4..1bb5f3c 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -313,4 +313,28 @@
[CHAR LIMIT=10].
-->
<string name="keyguard_affordance_enablement_dialog_dismiss_button">Done</string>
+
+ <!--
+ Label for a menu item on a settings screen that helps the user open a new screen where they can
+ configure the lock screen shortcut buttons that appear on the device without unlocking.
+ [CHAR LIMIT=16].
+ -->
+ <string name="keyguard_quick_affordance_title">Shortcuts</string>
+
+ <!--
+ Template for text that shows the names of two currently-selected lock screen shortcuts on the
+ lock screen. For example, it may say "Camera, Wallet", if the first selected shortcut opens the
+ camera app and the second one opens the tap-to-pay wallet experience.
+ [CHAR LIMIT=60].
+ -->
+ <string name="keyguard_quick_affordance_two_selected_template"><xliff:g id="first">%1$s</xliff:g>, <xliff:g id="second">%2$s</xliff:g></string>
+
+ <!--
+ Placeholder text that shows when no lock screen shortcuts are currently selected on the lock
+ screen. When selected, "None" is replaced by another string that shows what is currently
+ selected. For example, it may say "Camera, Wallet", if the first selected shortcut opens the
+ camera app and the second one opens the tap-to-pay wallet experience.
+ [CHAR LIMIT=60].
+ -->
+ <string name="keyguard_quick_affordance_none_selected">None</string>
</resources>
diff --git a/robolectric_tests/src/com/android/customization/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepositoryTest.kt b/robolectric_tests/src/com/android/customization/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepositoryTest.kt
deleted file mode 100644
index 771fd3b..0000000
--- a/robolectric_tests/src/com/android/customization/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepositoryTest.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * 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
- *
- * http://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 com.android.customization.quickaffordance.data.repository
-
-import androidx.test.filters.SmallTest
-import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.toList
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(JUnit4::class)
-class KeyguardQuickAffordancePickerRepositoryTest {
-
- private lateinit var underTest: KeyguardQuickAffordancePickerRepository
-
- private lateinit var client: FakeKeyguardQuickAffordanceProviderClient
-
- @Before
- fun setUp() {
- client = FakeKeyguardQuickAffordanceProviderClient()
-
- underTest =
- KeyguardQuickAffordancePickerRepository(
- client = client,
- )
- }
-
- @Test
- fun `isFeatureEnabled - enabled`() = runTest {
- client.setFlag(
- com.android.systemui.shared.quickaffordance.data.content
- .KeyguardQuickAffordanceProviderContract
- .FlagsTable
- .FLAG_NAME_FEATURE_ENABLED,
- true,
- )
- val values = mutableListOf<Boolean>()
- val job = launch(UnconfinedTestDispatcher()) { underTest.isFeatureEnabled.toList(values) }
-
- assertThat(values.last()).isTrue()
-
- job.cancel()
- }
-
- @Test
- fun `isFeatureEnabled - not enabled`() = runTest {
- client.setFlag(
- com.android.systemui.shared.quickaffordance.data.content
- .KeyguardQuickAffordanceProviderContract
- .FlagsTable
- .FLAG_NAME_FEATURE_ENABLED,
- false,
- )
- val values = mutableListOf<Boolean>()
- val job = launch(UnconfinedTestDispatcher()) { underTest.isFeatureEnabled.toList(values) }
-
- assertThat(values.last()).isFalse()
-
- job.cancel()
- }
-}
diff --git a/src/com/android/customization/module/CustomizationInjector.java b/src/com/android/customization/module/CustomizationInjector.java
index 85853de..2cc1245 100644
--- a/src/com/android/customization/module/CustomizationInjector.java
+++ b/src/com/android/customization/module/CustomizationInjector.java
@@ -22,6 +22,7 @@
import com.android.customization.model.theme.OverlayManagerCompat;
import com.android.customization.model.theme.ThemeBundleProvider;
import com.android.customization.model.theme.ThemeManager;
+import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor;
import com.android.wallpaper.module.Injector;
public interface CustomizationInjector extends Injector {
@@ -30,4 +31,11 @@
ThemeManager getThemeManager(ThemeBundleProvider provider, FragmentActivity activity,
OverlayManagerCompat overlayManagerCompat, ThemesUserEventLogger logger);
+
+
+ /**
+ * Get {@link KeyguardQuickAffordancePickerInteractor}
+ */
+ KeyguardQuickAffordancePickerInteractor getKeyguardQuickAffordancePickerInteractor(
+ Context context);
}
diff --git a/src/com/android/customization/module/DefaultCustomizationSections.java b/src/com/android/customization/module/DefaultCustomizationSections.java
index 21f2c84..7eb8865 100644
--- a/src/com/android/customization/module/DefaultCustomizationSections.java
+++ b/src/com/android/customization/module/DefaultCustomizationSections.java
@@ -1,9 +1,9 @@
package com.android.customization.module;
-import android.app.Activity;
import android.os.Bundle;
import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LifecycleOwner;
import com.android.customization.model.color.ColorSectionController;
@@ -28,9 +28,12 @@
public final class DefaultCustomizationSections implements CustomizationSections {
@Override
- public List<CustomizationSectionController<?>> getAllSectionControllers(Activity activity,
- LifecycleOwner lifecycleOwner, WallpaperColorsViewModel wallpaperColorsViewModel,
- WorkspaceViewModel workspaceViewModel, PermissionRequester permissionRequester,
+ public List<CustomizationSectionController<?>> getAllSectionControllers(
+ FragmentActivity activity,
+ LifecycleOwner lifecycleOwner,
+ WallpaperColorsViewModel wallpaperColorsViewModel,
+ WorkspaceViewModel workspaceViewModel,
+ PermissionRequester permissionRequester,
WallpaperPreviewNavigator wallpaperPreviewNavigator,
CustomizationSectionNavigationController sectionNavigationController,
@Nullable Bundle savedInstanceState) {
diff --git a/src/com/android/customization/module/ThemePickerInjector.java b/src/com/android/customization/module/ThemePickerInjector.java
index ef2b60a..4069b50 100644
--- a/src/com/android/customization/module/ThemePickerInjector.java
+++ b/src/com/android/customization/module/ThemePickerInjector.java
@@ -21,6 +21,7 @@
import static com.android.wallpaper.picker.PreviewFragment.ARG_VIEW_AS_HOME;
import static com.android.wallpaper.picker.PreviewFragment.ARG_WALLPAPER;
+import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -32,6 +33,11 @@
import com.android.customization.model.theme.OverlayManagerCompat;
import com.android.customization.model.theme.ThemeBundleProvider;
import com.android.customization.model.theme.ThemeManager;
+import com.android.customization.picker.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository;
+import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor;
+import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel;
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient;
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClientImpl;
import com.android.wallpaper.model.LiveWallpaperInfo;
import com.android.wallpaper.model.WallpaperInfo;
import com.android.wallpaper.module.CustomizationSections;
@@ -42,6 +48,8 @@
import com.android.wallpaper.picker.LivePreviewFragment;
import com.android.wallpaper.picker.PreviewFragment;
+import kotlinx.coroutines.Dispatchers;
+
/**
* A concrete, real implementation of the dependency provider.
*/
@@ -50,9 +58,12 @@
private CustomizationSections mCustomizationSections;
private ThemesUserEventLogger mUserEventLogger;
private WallpaperPreferences mPrefs;
+ private KeyguardQuickAffordancePickerInteractor mKeyguardQuickAffordancePickerInteractor;
+ private KeyguardQuickAffordancePickerViewModel.Factory
+ mKeyguardQuickAffordancePickerViewModelFactory;
@Override
- public CustomizationSections getCustomizationSections() {
+ public CustomizationSections getCustomizationSections(Activity activity) {
if (mCustomizationSections == null) {
mCustomizationSections = new DefaultCustomizationSections();
}
@@ -122,4 +133,31 @@
OverlayManagerCompat overlayManagerCompat, ThemesUserEventLogger logger) {
return new ThemeManager(provider, activity, overlayManagerCompat, logger);
}
+
+ @Override
+ public KeyguardQuickAffordancePickerInteractor getKeyguardQuickAffordancePickerInteractor(
+ Context context) {
+ if (mKeyguardQuickAffordancePickerInteractor == null) {
+ final KeyguardQuickAffordanceProviderClient client =
+ new KeyguardQuickAffordanceProviderClientImpl(context, Dispatchers.getIO());
+ mKeyguardQuickAffordancePickerInteractor = new KeyguardQuickAffordancePickerInteractor(
+ new KeyguardQuickAffordancePickerRepository(client, Dispatchers.getIO()),
+ client);
+ }
+ return mKeyguardQuickAffordancePickerInteractor;
+ }
+
+ /**
+ * Returns a {@link KeyguardQuickAffordancePickerViewModel.Factory}.
+ */
+ public KeyguardQuickAffordancePickerViewModel.Factory
+ getKeyguardQuickAffordancePickerViewModelFactory(Context context) {
+ if (mKeyguardQuickAffordancePickerViewModelFactory == null) {
+ mKeyguardQuickAffordancePickerViewModelFactory =
+ new KeyguardQuickAffordancePickerViewModel.Factory(
+ context,
+ getKeyguardQuickAffordancePickerInteractor(context));
+ }
+ return mKeyguardQuickAffordancePickerViewModelFactory;
+ }
}
diff --git a/src/com/android/customization/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepository.kt b/src/com/android/customization/picker/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepository.kt
similarity index 72%
rename from src/com/android/customization/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepository.kt
rename to src/com/android/customization/picker/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepository.kt
index 480e113..5846107 100644
--- a/src/com/android/customization/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepository.kt
+++ b/src/com/android/customization/picker/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepository.kt
@@ -15,30 +15,29 @@
*
*/
-package com.android.customization.quickaffordance.data.repository
+package com.android.customization.picker.quickaffordance.data.repository
-import com.android.customization.quickaffordance.shared.model.KeyguardQuickAffordancePickerAffordanceModel as AffordanceModel
-import com.android.customization.quickaffordance.shared.model.KeyguardQuickAffordancePickerSelectionModel as SelectionModel
-import com.android.customization.quickaffordance.shared.model.KeyguardQuickAffordancePickerSlotModel as SlotModel
+import com.android.customization.picker.quickaffordance.shared.model.KeyguardQuickAffordancePickerAffordanceModel as AffordanceModel
+import com.android.customization.picker.quickaffordance.shared.model.KeyguardQuickAffordancePickerSelectionModel as SelectionModel
+import com.android.customization.picker.quickaffordance.shared.model.KeyguardQuickAffordancePickerSlotModel as SlotModel
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient as Client
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.withContext
/**
* Abstracts access to application state related to functionality for selecting, picking, or setting
* lock screen quick affordances.
*/
class KeyguardQuickAffordancePickerRepository(
- client: Client,
+ private val client: Client,
+ private val backgroundDispatcher: CoroutineDispatcher,
) {
/** Whether the feature is enabled. */
val isFeatureEnabled: Flow<Boolean> =
- client.observeFlags().map { flags ->
- flags
- .find { flag -> flag.name == Contract.FlagsTable.FLAG_NAME_FEATURE_ENABLED }
- ?.value == true
- }
+ client.observeFlags().map { flags -> flags.isFeatureEnabled() }
/** List of slots available on the device. */
val slots: Flow<List<SlotModel>> =
@@ -56,6 +55,15 @@
selections.map { selection -> selection.toModel() }
}
+ suspend fun isFeatureEnabled(): Boolean {
+ return withContext(backgroundDispatcher) { client.queryFlags().isFeatureEnabled() }
+ }
+
+ private fun List<Client.Flag>.isFeatureEnabled(): Boolean {
+ return find { flag -> flag.name == Contract.FlagsTable.FLAG_NAME_FEATURE_ENABLED }?.value ==
+ true
+ }
+
private fun Client.Slot.toModel(): SlotModel {
return SlotModel(
id = id,
diff --git a/src/com/android/customization/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractor.kt b/src/com/android/customization/picker/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractor.kt
similarity index 78%
rename from src/com/android/customization/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractor.kt
rename to src/com/android/customization/picker/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractor.kt
index f60fc12..87cedf5 100644
--- a/src/com/android/customization/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractor.kt
+++ b/src/com/android/customization/picker/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractor.kt
@@ -15,14 +15,14 @@
*
*/
-package com.android.customization.quickaffordance.domain.interactor
+package com.android.customization.picker.quickaffordance.domain.interactor
import android.graphics.drawable.Drawable
import androidx.annotation.DrawableRes
-import com.android.customization.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository
-import com.android.customization.quickaffordance.shared.model.KeyguardQuickAffordancePickerAffordanceModel as AffordanceModel
-import com.android.customization.quickaffordance.shared.model.KeyguardQuickAffordancePickerSelectionModel as SelectionModel
-import com.android.customization.quickaffordance.shared.model.KeyguardQuickAffordancePickerSlotModel as SlotModel
+import com.android.customization.picker.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository
+import com.android.customization.picker.quickaffordance.shared.model.KeyguardQuickAffordancePickerAffordanceModel as AffordanceModel
+import com.android.customization.picker.quickaffordance.shared.model.KeyguardQuickAffordancePickerSelectionModel as SelectionModel
+import com.android.customization.picker.quickaffordance.shared.model.KeyguardQuickAffordancePickerSlotModel as SlotModel
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient as Client
import kotlinx.coroutines.flow.Flow
@@ -31,7 +31,7 @@
* the lock screen.
*/
class KeyguardQuickAffordancePickerInteractor(
- repository: KeyguardQuickAffordancePickerRepository,
+ private val repository: KeyguardQuickAffordancePickerRepository,
private val client: Client,
) {
/** Whether the feature is enabled. */
@@ -83,4 +83,9 @@
): Drawable {
return client.getAffordanceIcon(iconResourceId)
}
+
+ /** Returns `true` if the feature is enabled; `false` otherwise. */
+ suspend fun isFeatureEnabled(): Boolean {
+ return repository.isFeatureEnabled()
+ }
}
diff --git a/src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerAffordanceModel.kt b/src/com/android/customization/picker/quickaffordance/shared/model/KeyguardQuickAffordancePickerAffordanceModel.kt
similarity index 95%
rename from src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerAffordanceModel.kt
rename to src/com/android/customization/picker/quickaffordance/shared/model/KeyguardQuickAffordancePickerAffordanceModel.kt
index 0a61cc2..1b18af7 100644
--- a/src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerAffordanceModel.kt
+++ b/src/com/android/customization/picker/quickaffordance/shared/model/KeyguardQuickAffordancePickerAffordanceModel.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.quickaffordance.shared.model
+package com.android.customization.picker.quickaffordance.shared.model
import androidx.annotation.DrawableRes
diff --git a/src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSelectionModel.kt b/src/com/android/customization/picker/quickaffordance/shared/model/KeyguardQuickAffordancePickerSelectionModel.kt
similarity index 91%
rename from src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSelectionModel.kt
rename to src/com/android/customization/picker/quickaffordance/shared/model/KeyguardQuickAffordancePickerSelectionModel.kt
index d72bc71..eea8b2a 100644
--- a/src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSelectionModel.kt
+++ b/src/com/android/customization/picker/quickaffordance/shared/model/KeyguardQuickAffordancePickerSelectionModel.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.quickaffordance.shared.model
+package com.android.customization.picker.quickaffordance.shared.model
/** Models a selection of an affordance on a slot. */
data class KeyguardQuickAffordancePickerSelectionModel(
diff --git a/src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSlotModel.kt b/src/com/android/customization/picker/quickaffordance/shared/model/KeyguardQuickAffordancePickerSlotModel.kt
similarity index 92%
rename from src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSlotModel.kt
rename to src/com/android/customization/picker/quickaffordance/shared/model/KeyguardQuickAffordancePickerSlotModel.kt
index 7044e06..7e662e0 100644
--- a/src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSlotModel.kt
+++ b/src/com/android/customization/picker/quickaffordance/shared/model/KeyguardQuickAffordancePickerSlotModel.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.quickaffordance.shared.model
+package com.android.customization.picker.quickaffordance.shared.model
/** Models a lock screen quick affordance slot (or position) where affordances can be displayed. */
data class KeyguardQuickAffordancePickerSlotModel(
diff --git a/src/com/android/customization/quickaffordance/ui/adapter/AffordancesAdapter.kt b/src/com/android/customization/picker/quickaffordance/ui/adapter/AffordancesAdapter.kt
similarity index 93%
rename from src/com/android/customization/quickaffordance/ui/adapter/AffordancesAdapter.kt
rename to src/com/android/customization/picker/quickaffordance/ui/adapter/AffordancesAdapter.kt
index f63fa7a..609c6ae 100644
--- a/src/com/android/customization/quickaffordance/ui/adapter/AffordancesAdapter.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/adapter/AffordancesAdapter.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.quickaffordance.ui.adapter
+package com.android.customization.picker.quickaffordance.ui.adapter
import android.view.LayoutInflater
import android.view.View
@@ -23,7 +23,7 @@
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
-import com.android.customization.quickaffordance.ui.viewmodel.KeyguardQuickAffordanceViewModel
+import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordanceViewModel
import com.android.wallpaper.R
/** Adapts between lock screen quick affordance items and views. */
diff --git a/src/com/android/customization/quickaffordance/ui/adapter/SlotTabAdapter.kt b/src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt
similarity index 92%
rename from src/com/android/customization/quickaffordance/ui/adapter/SlotTabAdapter.kt
rename to src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt
index 953b632..acafef4 100644
--- a/src/com/android/customization/quickaffordance/ui/adapter/SlotTabAdapter.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/adapter/SlotTabAdapter.kt
@@ -15,14 +15,14 @@
*
*/
-package com.android.customization.quickaffordance.ui.adapter
+package com.android.customization.picker.quickaffordance.ui.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
-import com.android.customization.quickaffordance.ui.viewmodel.KeyguardQuickAffordanceSlotViewModel
+import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordanceSlotViewModel
import com.android.wallpaper.R
/** Adapts between lock screen quick affordance slot items and views. */
diff --git a/src/com/android/customization/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
similarity index 93%
rename from src/com/android/customization/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
rename to src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
index 4486e86..605bd7b 100644
--- a/src/com/android/customization/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.quickaffordance.ui.binder
+package com.android.customization.picker.quickaffordance.ui.binder
import android.app.AlertDialog
import android.app.Dialog
@@ -28,9 +28,9 @@
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.android.customization.quickaffordance.ui.adapter.AffordancesAdapter
-import com.android.customization.quickaffordance.ui.adapter.SlotTabAdapter
-import com.android.customization.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel
+import com.android.customization.picker.quickaffordance.ui.adapter.AffordancesAdapter
+import com.android.customization.picker.quickaffordance.ui.adapter.SlotTabAdapter
+import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel
import com.android.wallpaper.R
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
diff --git a/src/com/android/customization/quickaffordance/ui/binder/KeyguardQuickAffordancePickerPreviewBinder.kt b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerPreviewBinder.kt
similarity index 93%
rename from src/com/android/customization/quickaffordance/ui/binder/KeyguardQuickAffordancePickerPreviewBinder.kt
rename to src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerPreviewBinder.kt
index 399c033..13ee553 100644
--- a/src/com/android/customization/quickaffordance/ui/binder/KeyguardQuickAffordancePickerPreviewBinder.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerPreviewBinder.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.quickaffordance.ui.binder
+package com.android.customization.picker.quickaffordance.ui.binder
import android.view.View
import android.widget.ImageView
@@ -23,7 +23,7 @@
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import com.android.customization.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel
+import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.wallpaper.R
import kotlinx.coroutines.flow.map
diff --git a/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordanceSectionViewBinder.kt b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordanceSectionViewBinder.kt
new file mode 100644
index 0000000..e832cc2
--- /dev/null
+++ b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordanceSectionViewBinder.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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
+ *
+ * http://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 com.android.customization.picker.quickaffordance.ui.binder
+
+import android.view.View
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.core.view.isVisible
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.flowWithLifecycle
+import androidx.lifecycle.lifecycleScope
+import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel
+import com.android.wallpaper.R
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+
+object KeyguardQuickAffordanceSectionViewBinder {
+ fun bind(
+ view: View,
+ viewModel: KeyguardQuickAffordancePickerViewModel,
+ lifecycleOwner: LifecycleOwner,
+ onClicked: () -> Unit,
+ ) {
+ view.setOnClickListener { onClicked() }
+
+ val descriptionView: TextView =
+ view.requireViewById(R.id.keyguard_quick_affordance_description)
+ val icon1: ImageView = view.requireViewById(R.id.icon_1)
+ val icon2: ImageView = view.requireViewById(R.id.icon_2)
+ val iconSpacer: View = view.requireViewById(R.id.icon_spacer)
+
+ lifecycleOwner.lifecycleScope.launch {
+ viewModel.summary
+ .flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.RESUMED)
+ .collectLatest { summary ->
+ descriptionView.text = summary.description
+
+ icon1.setImageDrawable(summary.icon1)
+ icon1.isVisible = summary.icon1 != null
+
+ icon2.setImageDrawable(summary.icon2)
+ icon2.isVisible = summary.icon2 != null
+
+ iconSpacer.isVisible = summary.isIconSpacingVisible
+ }
+ }
+ }
+}
diff --git a/src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSlotModel.kt b/src/com/android/customization/picker/quickaffordance/ui/fragment/KeyguardQuickAffordancePickerFragment.kt
similarity index 60%
copy from src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSlotModel.kt
copy to src/com/android/customization/picker/quickaffordance/ui/fragment/KeyguardQuickAffordancePickerFragment.kt
index 7044e06..e910488 100644
--- a/src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSlotModel.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/fragment/KeyguardQuickAffordancePickerFragment.kt
@@ -15,11 +15,16 @@
*
*/
-package com.android.customization.quickaffordance.shared.model
+package com.android.customization.picker.quickaffordance.ui.fragment
-/** Models a lock screen quick affordance slot (or position) where affordances can be displayed. */
-data class KeyguardQuickAffordancePickerSlotModel(
- val id: String,
- /** Maximum number of affordances allowed to be set on this slot. */
- val maxSelectedQuickAffordances: Int,
-)
+import com.android.wallpaper.picker.AppbarFragment
+
+class KeyguardQuickAffordancePickerFragment : AppbarFragment() {
+ companion object {
+ fun newInstance(): KeyguardQuickAffordancePickerFragment {
+ return KeyguardQuickAffordancePickerFragment()
+ }
+ }
+
+ // TODO(b/254858701): implement this UI.
+}
diff --git a/src/com/android/customization/picker/quickaffordance/ui/section/KeyguardQuickAffordanceSectionController.kt b/src/com/android/customization/picker/quickaffordance/ui/section/KeyguardQuickAffordanceSectionController.kt
new file mode 100644
index 0000000..6b35d7c
--- /dev/null
+++ b/src/com/android/customization/picker/quickaffordance/ui/section/KeyguardQuickAffordanceSectionController.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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
+ *
+ * http://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 com.android.customization.picker.quickaffordance.ui.section
+
+import android.content.Context
+import android.view.LayoutInflater
+import androidx.lifecycle.LifecycleOwner
+import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
+import com.android.customization.picker.quickaffordance.ui.binder.KeyguardQuickAffordanceSectionViewBinder
+import com.android.customization.picker.quickaffordance.ui.fragment.KeyguardQuickAffordancePickerFragment
+import com.android.customization.picker.quickaffordance.ui.view.KeyguardQuickAffordanceSectionView
+import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel
+import com.android.wallpaper.R
+import com.android.wallpaper.model.CustomizationSectionController
+import com.android.wallpaper.model.CustomizationSectionController.CustomizationSectionNavigationController as NavigationController
+import kotlinx.coroutines.runBlocking
+
+class KeyguardQuickAffordanceSectionController(
+ private val navigationController: NavigationController,
+ private val interactor: KeyguardQuickAffordancePickerInteractor,
+ private val viewModel: KeyguardQuickAffordancePickerViewModel,
+ private val lifecycleOwner: LifecycleOwner,
+) : CustomizationSectionController<KeyguardQuickAffordanceSectionView> {
+
+ private val isFeatureEnabled: Boolean = runBlocking { interactor.isFeatureEnabled() }
+
+ override fun isAvailable(context: Context?): Boolean {
+ return isFeatureEnabled
+ }
+
+ override fun createView(context: Context?): KeyguardQuickAffordanceSectionView {
+ val view =
+ LayoutInflater.from(context)
+ .inflate(
+ R.layout.keyguard_quick_affordance_section_view,
+ null,
+ ) as KeyguardQuickAffordanceSectionView
+ KeyguardQuickAffordanceSectionViewBinder.bind(
+ view = view,
+ viewModel = viewModel,
+ lifecycleOwner = lifecycleOwner,
+ ) {
+ navigationController.navigateTo(KeyguardQuickAffordancePickerFragment.newInstance())
+ }
+ return view
+ }
+}
diff --git a/src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSelectionModel.kt b/src/com/android/customization/picker/quickaffordance/ui/view/KeyguardQuickAffordanceSectionView.kt
similarity index 65%
copy from src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSelectionModel.kt
copy to src/com/android/customization/picker/quickaffordance/ui/view/KeyguardQuickAffordanceSectionView.kt
index d72bc71..daace7d 100644
--- a/src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSelectionModel.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/view/KeyguardQuickAffordanceSectionView.kt
@@ -15,10 +15,17 @@
*
*/
-package com.android.customization.quickaffordance.shared.model
+package com.android.customization.picker.quickaffordance.ui.view
-/** Models a selection of an affordance on a slot. */
-data class KeyguardQuickAffordancePickerSelectionModel(
- val slotId: String,
- val affordanceId: String,
-)
+import android.content.Context
+import android.util.AttributeSet
+import com.android.wallpaper.picker.SectionView
+
+class KeyguardQuickAffordanceSectionView(
+ context: Context?,
+ attrs: AttributeSet?,
+) :
+ SectionView(
+ context,
+ attrs,
+ )
diff --git a/src/com/android/customization/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt b/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt
similarity index 81%
rename from src/com/android/customization/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt
rename to src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt
index e947c9a..e69c639 100644
--- a/src/com/android/customization/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModel.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.quickaffordance.ui.viewmodel
+package com.android.customization.picker.quickaffordance.ui.viewmodel
import android.annotation.SuppressLint
import android.content.Context
@@ -25,7 +25,7 @@
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
-import com.android.customization.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
+import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
import com.android.wallpaper.R
@@ -34,6 +34,7 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
/** Models UI state for a lock screen quick affordance picker experience. */
@@ -161,6 +162,34 @@
}
}
+ @SuppressLint("UseCompatLoadingForDrawables")
+ val summary: Flow<KeyguardQuickAffordanceSummaryViewModel> =
+ slots.map { slots ->
+ val icon2 =
+ slots[KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END]
+ ?.selectedQuickAffordances
+ ?.firstOrNull()
+ ?.icon
+ val icon1 =
+ slots[KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START]
+ ?.selectedQuickAffordances
+ ?.firstOrNull()
+ ?.icon
+
+ val isIconSpacingVisible = icon1 != null && icon2 != null
+ KeyguardQuickAffordanceSummaryViewModel(
+ description = toDescriptionText(context, slots),
+ icon1 = icon1
+ ?: if (icon2 == null) {
+ context.getDrawable(R.drawable.link_off)
+ } else {
+ null
+ },
+ icon2 = icon2,
+ isIconSpacingVisible = isIconSpacingVisible,
+ )
+ }
+
private val _dialog = MutableStateFlow<DialogViewModel?>(null)
/**
* The current dialog to show. If `null`, no dialog should be shown.
@@ -257,6 +286,36 @@
val intent: Intent?,
)
+ private fun toDescriptionText(
+ context: Context,
+ slots: Map<String, KeyguardQuickAffordanceSlotViewModel>,
+ ): String {
+ val bottomStartAffordanceName =
+ slots[KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START]
+ ?.selectedQuickAffordances
+ ?.firstOrNull()
+ ?.contentDescription
+ val bottomEndAffordanceName =
+ slots[KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END]
+ ?.selectedQuickAffordances
+ ?.firstOrNull()
+ ?.contentDescription
+
+ return when {
+ !bottomStartAffordanceName.isNullOrEmpty() &&
+ !bottomEndAffordanceName.isNullOrEmpty() -> {
+ context.getString(
+ R.string.keyguard_quick_affordance_two_selected_template,
+ bottomStartAffordanceName,
+ bottomEndAffordanceName,
+ )
+ }
+ !bottomStartAffordanceName.isNullOrEmpty() -> bottomStartAffordanceName
+ !bottomEndAffordanceName.isNullOrEmpty() -> bottomEndAffordanceName
+ else -> context.getString(R.string.keyguard_quick_affordance_none_selected)
+ }
+ }
+
class Factory(
private val context: Context,
private val interactor: KeyguardQuickAffordancePickerInteractor,
diff --git a/src/com/android/customization/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceSlotViewModel.kt b/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceSlotViewModel.kt
similarity index 95%
rename from src/com/android/customization/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceSlotViewModel.kt
rename to src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceSlotViewModel.kt
index a0b77fa..bb9b29b 100644
--- a/src/com/android/customization/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceSlotViewModel.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceSlotViewModel.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.quickaffordance.ui.viewmodel
+package com.android.customization.picker.quickaffordance.ui.viewmodel
/** Models UI state for a single lock screen quick affordance slot in a picker experience. */
data class KeyguardQuickAffordanceSlotViewModel(
diff --git a/src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSelectionModel.kt b/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceSummaryViewModel.kt
similarity index 68%
copy from src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSelectionModel.kt
copy to src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceSummaryViewModel.kt
index d72bc71..3860a9f 100644
--- a/src/com/android/customization/quickaffordance/shared/model/KeyguardQuickAffordancePickerSelectionModel.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceSummaryViewModel.kt
@@ -15,10 +15,13 @@
*
*/
-package com.android.customization.quickaffordance.shared.model
+package com.android.customization.picker.quickaffordance.ui.viewmodel
-/** Models a selection of an affordance on a slot. */
-data class KeyguardQuickAffordancePickerSelectionModel(
- val slotId: String,
- val affordanceId: String,
+import android.graphics.drawable.Drawable
+
+data class KeyguardQuickAffordanceSummaryViewModel(
+ val description: String,
+ val icon1: Drawable?,
+ val icon2: Drawable?,
+ val isIconSpacingVisible: Boolean,
)
diff --git a/src/com/android/customization/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt b/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
similarity index 96%
rename from src/com/android/customization/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
rename to src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
index 8f24145..d720b0c 100644
--- a/src/com/android/customization/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.customization.quickaffordance.ui.viewmodel
+package com.android.customization.picker.quickaffordance.ui.viewmodel
import android.annotation.SuppressLint
import android.content.Context
diff --git a/tests/src/com/android/customization/model/picker/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepositoryTest.kt b/tests/src/com/android/customization/model/picker/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepositoryTest.kt
new file mode 100644
index 0000000..4a88f3b
--- /dev/null
+++ b/tests/src/com/android/customization/model/picker/quickaffordance/data/repository/KeyguardQuickAffordancePickerRepositoryTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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
+ *
+ * http://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 com.android.customization.model.picker.quickaffordance.data.repository
+
+import androidx.test.filters.SmallTest
+import com.android.customization.picker.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository
+import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardQuickAffordancePickerRepositoryTest {
+
+ private lateinit var underTest: KeyguardQuickAffordancePickerRepository
+
+ private lateinit var testScope: TestScope
+ private lateinit var client: FakeKeyguardQuickAffordanceProviderClient
+
+ @Before
+ fun setUp() {
+ client = FakeKeyguardQuickAffordanceProviderClient()
+ val coroutineDispatcher = UnconfinedTestDispatcher()
+ testScope = TestScope(coroutineDispatcher)
+ Dispatchers.setMain(coroutineDispatcher)
+
+ underTest =
+ KeyguardQuickAffordancePickerRepository(
+ client = client,
+ backgroundDispatcher = coroutineDispatcher,
+ )
+ }
+
+ @After
+ fun tearDown() {
+ Dispatchers.resetMain()
+ }
+
+ @Test
+ fun `isFeatureEnabled - enabled`() =
+ testScope.runTest {
+ client.setFlag(
+ com.android.systemui.shared.quickaffordance.data.content
+ .KeyguardQuickAffordanceProviderContract
+ .FlagsTable
+ .FLAG_NAME_FEATURE_ENABLED,
+ true,
+ )
+ val values = mutableListOf<Boolean>()
+ val job = launch { underTest.isFeatureEnabled.toList(values) }
+
+ assertThat(values.last()).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun `isFeatureEnabled - not enabled`() =
+ testScope.runTest {
+ client.setFlag(
+ com.android.systemui.shared.quickaffordance.data.content
+ .KeyguardQuickAffordanceProviderContract
+ .FlagsTable
+ .FLAG_NAME_FEATURE_ENABLED,
+ false,
+ )
+ val values = mutableListOf<Boolean>()
+ val job = launch { underTest.isFeatureEnabled.toList(values) }
+
+ assertThat(values.last()).isFalse()
+
+ job.cancel()
+ }
+}
diff --git a/robolectric_tests/src/com/android/customization/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractorTest.kt b/tests/src/com/android/customization/model/picker/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractorTest.kt
similarity index 88%
rename from robolectric_tests/src/com/android/customization/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractorTest.kt
rename to tests/src/com/android/customization/model/picker/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractorTest.kt
index 227fd6a..d8a136d 100644
--- a/robolectric_tests/src/com/android/customization/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractorTest.kt
+++ b/tests/src/com/android/customization/model/picker/quickaffordance/domain/interactor/KeyguardQuickAffordancePickerInteractorTest.kt
@@ -15,11 +15,12 @@
*
*/
-package com.android.customization.quickaffordance.domain.interactor
+package com.android.customization.model.picker.quickaffordance.domain.interactor
import androidx.test.filters.SmallTest
-import com.android.customization.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository
-import com.android.customization.quickaffordance.shared.model.KeyguardQuickAffordancePickerSelectionModel
+import com.android.customization.picker.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository
+import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
+import com.android.customization.picker.quickaffordance.shared.model.KeyguardQuickAffordancePickerSelectionModel
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
import com.google.common.truth.Truth.assertThat
@@ -59,6 +60,7 @@
repository =
KeyguardQuickAffordancePickerRepository(
client = client,
+ backgroundDispatcher = coroutineDispatcher,
),
client = client,
)
@@ -73,7 +75,7 @@
fun select() =
testScope.runTest {
val selections = mutableListOf<List<KeyguardQuickAffordancePickerSelectionModel>>()
- val job = launch(UnconfinedTestDispatcher()) { underTest.selections.toList(selections) }
+ val job = launch { underTest.selections.toList(selections) }
underTest.select(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
@@ -110,7 +112,7 @@
fun unselect() =
testScope.runTest {
val selections = mutableListOf<List<KeyguardQuickAffordancePickerSelectionModel>>()
- val job = launch(UnconfinedTestDispatcher()) { underTest.selections.toList(selections) }
+ val job = launch { underTest.selections.toList(selections) }
underTest.select(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
@@ -131,7 +133,7 @@
testScope.runTest {
client.setSlotCapacity(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, 3)
val selections = mutableListOf<List<KeyguardQuickAffordancePickerSelectionModel>>()
- val job = launch(UnconfinedTestDispatcher()) { underTest.selections.toList(selections) }
+ val job = launch { underTest.selections.toList(selections) }
underTest.select(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
diff --git a/robolectric_tests/src/com/android/customization/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModelTest.kt b/tests/src/com/android/customization/model/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModelTest.kt
similarity index 65%
rename from robolectric_tests/src/com/android/customization/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModelTest.kt
rename to tests/src/com/android/customization/model/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModelTest.kt
index 126b22b..756ffb4 100644
--- a/robolectric_tests/src/com/android/customization/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModelTest.kt
+++ b/tests/src/com/android/customization/model/picker/quickaffordance/ui/viewmodel/KeyguardQuickAffordancePickerViewModelTest.kt
@@ -15,14 +15,20 @@
*
*/
-package com.android.customization.quickaffordance.ui.viewmodel
+package com.android.customization.model.picker.quickaffordance.ui.viewmodel
+import android.content.Context
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.customization.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository
-import com.android.customization.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
+import com.android.customization.picker.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository
+import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
+import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel
+import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordanceSlotViewModel
+import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordanceSummaryViewModel
+import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordanceViewModel
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.Dispatchers
@@ -47,11 +53,13 @@
private lateinit var underTest: KeyguardQuickAffordancePickerViewModel
+ private lateinit var context: Context
private lateinit var testScope: TestScope
private lateinit var client: FakeKeyguardQuickAffordanceProviderClient
@Before
fun setUp() {
+ context = InstrumentationRegistry.getInstrumentation().targetContext
val coroutineDispatcher = UnconfinedTestDispatcher()
testScope = TestScope(coroutineDispatcher)
Dispatchers.setMain(coroutineDispatcher)
@@ -59,12 +67,13 @@
underTest =
KeyguardQuickAffordancePickerViewModel.Factory(
- context = InstrumentationRegistry.getInstrumentation().targetContext,
+ context = context,
interactor =
KeyguardQuickAffordancePickerInteractor(
repository =
KeyguardQuickAffordancePickerRepository(
client = client,
+ backgroundDispatcher = coroutineDispatcher,
),
client = client,
),
@@ -84,12 +93,8 @@
val quickAffordances = mutableListOf<List<KeyguardQuickAffordanceViewModel>>()
val jobs = buildList {
- add(launch(UnconfinedTestDispatcher()) { underTest.slots.toList(slots) })
- add(
- launch(UnconfinedTestDispatcher()) {
- underTest.quickAffordances.toList(quickAffordances)
- }
- )
+ add(launch { underTest.slots.toList(slots) })
+ add(launch { underTest.quickAffordances.toList(quickAffordances) })
}
// Initially, the first slot is selected with the "none" affordance selected.
@@ -177,12 +182,8 @@
val quickAffordances = mutableListOf<List<KeyguardQuickAffordanceViewModel>>()
val jobs = buildList {
- add(launch(UnconfinedTestDispatcher()) { underTest.slots.toList(slots) })
- add(
- launch(UnconfinedTestDispatcher()) {
- underTest.quickAffordances.toList(quickAffordances)
- }
- )
+ add(launch { underTest.slots.toList(slots) })
+ add(launch { underTest.quickAffordances.toList(quickAffordances) })
}
// Select "affordance 1" for the first slot.
@@ -225,13 +226,9 @@
val dialog = mutableListOf<KeyguardQuickAffordancePickerViewModel.DialogViewModel?>()
val jobs = buildList {
- add(launch(UnconfinedTestDispatcher()) { underTest.slots.toList(slots) })
- add(
- launch(UnconfinedTestDispatcher()) {
- underTest.quickAffordances.toList(quickAffordances)
- }
- )
- add(launch(UnconfinedTestDispatcher()) { underTest.dialog.toList(dialog) })
+ add(launch { underTest.slots.toList(slots) })
+ add(launch { underTest.quickAffordances.toList(quickAffordances) })
+ add(launch { underTest.dialog.toList(dialog) })
}
val enablementInstructions = listOf("header", "enablementInstructions")
val enablementActionText = "enablementActionText"
@@ -241,17 +238,15 @@
// Lets add a disabled affordance to the picker:
val affordanceIndex =
client.addAffordance(
- com.android.systemui.shared.quickaffordance.data.content
- .KeyguardQuickAffordanceProviderClient
- .Affordance(
- id = "disabled",
- name = "disabled",
- iconResourceId = 0,
- isEnabled = false,
- enablementInstructions = enablementInstructions,
- enablementActionText = enablementActionText,
- enablementActionComponentName = enablementActionComponentName,
- )
+ KeyguardQuickAffordanceProviderClient.Affordance(
+ id = "disabled",
+ name = "disabled",
+ iconResourceId = 0,
+ isEnabled = false,
+ enablementInstructions = enablementInstructions,
+ enablementActionText = enablementActionText,
+ enablementActionComponentName = enablementActionComponentName,
+ )
)
// Lets try to select that disabled affordance:
@@ -274,6 +269,116 @@
jobs.forEach { it.cancel() }
}
+ @Test
+ fun `summary - affordance selected in both bottom-start and bottom-end`() =
+ testScope.runTest {
+ val slots = mutableListOf<Map<String, KeyguardQuickAffordanceSlotViewModel>>()
+ val quickAffordances = mutableListOf<List<KeyguardQuickAffordanceViewModel>>()
+ val summary = mutableListOf<KeyguardQuickAffordanceSummaryViewModel>()
+ val jobs = buildList {
+ add(launch { underTest.slots.toList(slots) })
+ add(launch { underTest.quickAffordances.toList(quickAffordances) })
+ add(launch { underTest.summary.toList(summary) })
+ }
+
+ // Select "affordance 1" for the first slot.
+ quickAffordances.last()[1].onClicked?.invoke()
+ // Select an affordance for the second slot.
+ // First, switch to the second slot:
+ slots.last()[KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END]?.onClicked?.invoke()
+ // Second, select the "affordance 3" affordance:
+ quickAffordances.last()[3].onClicked?.invoke()
+
+ assertThat(summary.last())
+ .isEqualTo(
+ KeyguardQuickAffordanceSummaryViewModel(
+ description =
+ "${FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1}," +
+ " ${FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_3}",
+ icon1 = FakeKeyguardQuickAffordanceProviderClient.ICON_1,
+ icon2 = FakeKeyguardQuickAffordanceProviderClient.ICON_3,
+ isIconSpacingVisible = true,
+ )
+ )
+ jobs.forEach { it.cancel() }
+ }
+
+ @Test
+ fun `summary - affordance selected only on bottom-start`() =
+ testScope.runTest {
+ val slots = mutableListOf<Map<String, KeyguardQuickAffordanceSlotViewModel>>()
+ val quickAffordances = mutableListOf<List<KeyguardQuickAffordanceViewModel>>()
+ val summary = mutableListOf<KeyguardQuickAffordanceSummaryViewModel>()
+ val jobs = buildList {
+ add(launch { underTest.slots.toList(slots) })
+ add(launch { underTest.quickAffordances.toList(quickAffordances) })
+ add(launch { underTest.summary.toList(summary) })
+ }
+
+ // Select "affordance 1" for the first slot.
+ quickAffordances.last()[1].onClicked?.invoke()
+
+ assertThat(summary.last())
+ .isEqualTo(
+ KeyguardQuickAffordanceSummaryViewModel(
+ description = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+ icon1 = FakeKeyguardQuickAffordanceProviderClient.ICON_1,
+ icon2 = null,
+ isIconSpacingVisible = false,
+ )
+ )
+ jobs.forEach { it.cancel() }
+ }
+
+ @Test
+ fun `summary - affordance selected only on bottom-end`() =
+ testScope.runTest {
+ val slots = mutableListOf<Map<String, KeyguardQuickAffordanceSlotViewModel>>()
+ val quickAffordances = mutableListOf<List<KeyguardQuickAffordanceViewModel>>()
+ val summary = mutableListOf<KeyguardQuickAffordanceSummaryViewModel>()
+ val jobs = buildList {
+ add(launch { underTest.slots.toList(slots) })
+ add(launch { underTest.quickAffordances.toList(quickAffordances) })
+ add(launch { underTest.summary.toList(summary) })
+ }
+
+ // Select an affordance for the second slot.
+ // First, switch to the second slot:
+ slots.last()[KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END]?.onClicked?.invoke()
+ // Second, select the "affordance 3" affordance:
+ quickAffordances.last()[3].onClicked?.invoke()
+
+ assertThat(summary.last())
+ .isEqualTo(
+ KeyguardQuickAffordanceSummaryViewModel(
+ description = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_3,
+ icon1 = null,
+ icon2 = FakeKeyguardQuickAffordanceProviderClient.ICON_3,
+ isIconSpacingVisible = false,
+ )
+ )
+ jobs.forEach { it.cancel() }
+ }
+
+ @Test
+ fun `summary - no affordances selected`() =
+ testScope.runTest {
+ val slots = mutableListOf<Map<String, KeyguardQuickAffordanceSlotViewModel>>()
+ val quickAffordances = mutableListOf<List<KeyguardQuickAffordanceViewModel>>()
+ val summary = mutableListOf<KeyguardQuickAffordanceSummaryViewModel>()
+ val jobs = buildList {
+ add(launch { underTest.slots.toList(slots) })
+ add(launch { underTest.quickAffordances.toList(quickAffordances) })
+ add(launch { underTest.summary.toList(summary) })
+ }
+
+ assertThat(summary.last().description).isEqualTo("None")
+ assertThat(summary.last().icon1).isNotNull()
+ assertThat(summary.last().icon2).isNull()
+ assertThat(summary.last().isIconSpacingVisible).isFalse()
+ jobs.forEach { it.cancel() }
+ }
+
/**
* Asserts the entire picker UI state is what is expected. This includes the slot tabs and the
* affordance list.
diff --git a/tests/src/com/android/customization/testing/TestCustomizationInjector.java b/tests/src/com/android/customization/testing/TestCustomizationInjector.java
index dbbdb74..15898c1 100644
--- a/tests/src/com/android/customization/testing/TestCustomizationInjector.java
+++ b/tests/src/com/android/customization/testing/TestCustomizationInjector.java
@@ -10,11 +10,18 @@
import com.android.customization.module.CustomizationInjector;
import com.android.customization.module.CustomizationPreferences;
import com.android.customization.module.ThemesUserEventLogger;
+import com.android.customization.picker.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository;
+import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor;
+import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel;
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient;
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClientImpl;
import com.android.wallpaper.module.DrawableLayerResolver;
import com.android.wallpaper.module.PackageStatusNotifier;
import com.android.wallpaper.module.UserEventLogger;
import com.android.wallpaper.testing.TestInjector;
+import kotlinx.coroutines.Dispatchers;
+
/**
* Test implementation of the dependency injector.
*/
@@ -24,6 +31,9 @@
private PackageStatusNotifier mPackageStatusNotifier;
private DrawableLayerResolver mDrawableLayerResolver;
private UserEventLogger mUserEventLogger;
+ private KeyguardQuickAffordancePickerInteractor mKeyguardQuickAffordancePickerInteractor;
+ private KeyguardQuickAffordancePickerViewModel.Factory
+ mKeyguardQuickAffordancePickerViewModelFactory;
@Override
public CustomizationPreferences getCustomizationPreferences(Context context) {
@@ -68,4 +78,17 @@
}
return mUserEventLogger;
}
+
+ @Override
+ public KeyguardQuickAffordancePickerInteractor getKeyguardQuickAffordancePickerInteractor(
+ Context context) {
+ if (mKeyguardQuickAffordancePickerInteractor == null) {
+ final KeyguardQuickAffordanceProviderClient client =
+ new KeyguardQuickAffordanceProviderClientImpl(context, Dispatchers.getIO());
+ mKeyguardQuickAffordancePickerInteractor = new KeyguardQuickAffordancePickerInteractor(
+ new KeyguardQuickAffordancePickerRepository(client, Dispatchers.getIO()),
+ client);
+ }
+ return mKeyguardQuickAffordancePickerInteractor;
+ }
}