Add icon picker for zen modes

Bug: 333901673
Test: atest ZenModeIconPickerListPreferenceControllerTest
Flag: android.app.modes_ui
Change-Id: Ib6faaac7c93b25423d18dbdcd0a2310de15fa86f
This commit is contained in:
Matías Hernández
2024-06-06 12:28:49 +02:00
parent b04e2d4692
commit 8d397f038f
18 changed files with 665 additions and 11 deletions

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2024 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?android:attr/colorControlNormal"
android:viewportHeight="960"
android:viewportWidth="960">
<path
android:fillColor="@android:color/white"
android:pathData="M620,440Q645,440 662.5,422.5Q680,405 680,380Q680,355 662.5,337.5Q645,320 620,320Q595,320 577.5,337.5Q560,355 560,380Q560,405 577.5,422.5Q595,440 620,440ZM340,440Q365,440 382.5,422.5Q400,405 400,380Q400,355 382.5,337.5Q365,320 340,320Q315,320 297.5,337.5Q280,355 280,380Q280,405 297.5,422.5Q315,440 340,440ZM480,700Q548,700 603.5,661.5Q659,623 684,560L276,560Q301,623 356.5,661.5Q412,700 480,700ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800Z" />
</vector>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 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.
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/icon_list"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:clipToPadding="true"
android:nestedScrollingEnabled="false"
android:paddingStart="12dp"
android:paddingEnd="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 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.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/zen_mode_icon_list_item_size"
android:clickable="true">
<!-- width is match_parent to distribute remaining horizontal space -->
<ImageView
android:id="@+id/icon_image_view"
android:layout_width="@dimen/zen_mode_icon_list_circle_diameter"
android:layout_height="@dimen/zen_mode_icon_list_circle_diameter"
android:importantForAccessibility="no"
android:layout_gravity="center" />
</FrameLayout>

View File

@@ -491,4 +491,9 @@
<dimen name="audio_streams_qrcode_size">264dp</dimen>
<dimen name="audio_streams_qrcode_preview_radius">30dp</dimen>
<!-- Zen Modes -->
<dimen name="zen_mode_icon_list_item_size">96dp</dimen>
<dimen name="zen_mode_icon_list_circle_diameter">56dp</dimen>
<dimen name="zen_mode_icon_list_icon_size">32dp</dimen>
</resources>

View File

@@ -9309,6 +9309,15 @@
<!-- [CHAR LIMIT=NONE] Zen mode summary spoken when changing mode by voice: Turn on all notifications. -->
<string name="zen_mode_summary_always">Change to always interrupt</string>
<!-- [CHAR LIMIT=20] Caption of the action button to change the name of a mode. -->
<string name="zen_mode_action_change_name">Rename</string>
<!-- [CHAR LIMIT=20] Caption of the action button to change the icon of a mode. -->
<string name="zen_mode_action_change_icon">Change icon</string>
<!-- [CHAR LIMIT=40] Zen mode settings: Title for the "choose mode icon" screen -->
<string name="zen_mode_icon_picker_title">Change icon</string>
<!-- Content description for help icon button [CHAR LIMIT=20] -->
<string name="warning_button_text">Warning</string>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="zen_mode_icon_picker_page"
settings:searchable="false"
android:title="@string/zen_mode_icon_picker_title">
<com.android.settingslib.widget.LayoutPreference
android:key="current_icon"
android:layout="@layout/settings_entity_header" />
<com.android.settings.applications.SpacePreference
android:layout_height="16dp" />
<com.android.settingslib.widget.LayoutPreference
android:key="icon_list"
android:selectable="false"
android:layout="@layout/modes_icon_list"/>
</PreferenceScreen>

View File

@@ -28,6 +28,10 @@
android:selectable="false"
android:layout="@layout/modes_activation_button"/>
<com.android.settingslib.widget.ActionButtonsPreference
android:key="actions"
android:selectable="true" />
<PreferenceCategory
android:title="@string/mode_interruption_filter_title"
android:key="modes_filters">

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2024 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.settings.notification.modes;
import static com.google.common.base.Preconditions.checkNotNull;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import com.android.settings.R;
import com.android.settingslib.Utils;
class IconUtil {
static Drawable applyTint(@NonNull Context context, @NonNull Drawable icon) {
icon = icon.mutate();
icon.setTintList(
Utils.getColorAttr(context, android.R.attr.colorControlNormal));
return icon;
}
/**
* Returns a variant of the supplied {@code icon} to be used in the icon picker. The inner icon
* is 36x36dp and it's contained into a circle of diameter 54dp.
*/
static Drawable makeIconCircle(@NonNull Context context, @NonNull Drawable icon) {
ShapeDrawable background = new ShapeDrawable(new OvalShape());
background.getPaint().setColor(Utils.getColorAttrDefaultColor(context,
com.android.internal.R.attr.materialColorSecondaryContainer));
icon.setTint(Utils.getColorAttrDefaultColor(context,
com.android.internal.R.attr.materialColorOnSecondaryContainer));
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[] { background, icon });
int circleDiameter = context.getResources().getDimensionPixelSize(
R.dimen.zen_mode_icon_list_circle_diameter);
int iconSize = context.getResources().getDimensionPixelSize(
R.dimen.zen_mode_icon_list_icon_size);
int iconPadding = (circleDiameter - iconSize) / 2;
layerDrawable.setBounds(0, 0, circleDiameter, circleDiameter);
layerDrawable.setLayerInset(1, iconPadding, iconPadding, iconPadding, iconPadding);
return layerDrawable;
}
static Drawable makeIconCircle(@NonNull Context context, @DrawableRes int iconResId) {
return makeIconCircle(context, checkNotNull(context.getDrawable(iconResId)));
}
}

View File

@@ -204,6 +204,14 @@ class ZenMode {
: new ZenDeviceEffects.Builder().build();
}
public boolean canEditName() {
return !isManualDnd();
}
public boolean canEditIcon() {
return !isManualDnd();
}
public boolean canBeDeleted() {
return !mIsManualDnd;
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2024 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.settings.notification.modes;
import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.widget.ActionButtonsPreference;
class ZenModeActionsPreferenceController extends AbstractZenModePreferenceController {
private ActionButtonsPreference mPreference;
ZenModeActionsPreferenceController(@NonNull Context context, @NonNull String key,
@Nullable ZenModesBackend backend) {
super(context, key, backend);
}
@Override
void updateState(Preference preference, @NonNull ZenMode zenMode) {
ActionButtonsPreference buttonsPreference = (ActionButtonsPreference) preference;
// TODO: b/346278854 - Add rename action (with setButton1Enabled(zenMode.canEditName())
buttonsPreference.setButton1Text(R.string.zen_mode_action_change_name);
buttonsPreference.setButton1Icon(R.drawable.ic_mode_edit);
buttonsPreference.setButton1Enabled(false);
buttonsPreference.setButton2Text(R.string.zen_mode_action_change_icon);
buttonsPreference.setButton2Icon(R.drawable.ic_zen_mode_action_change_icon);
buttonsPreference.setButton2Enabled(zenMode.canEditIcon());
buttonsPreference.setButton2OnClickListener(v -> {
Bundle bundle = new Bundle();
bundle.putString(MODE_ID, zenMode.getId());
new SubSettingLauncher(mContext)
.setDestination(ZenModeIconPickerFragment.class.getName())
// TODO: b/332937635 - Update metrics category
.setSourceMetricsCategory(0)
.setArguments(bundle)
.launch();
});
}
}

View File

@@ -38,6 +38,7 @@ public class ZenModeFragment extends ZenModeFragmentBase {
List<AbstractPreferenceController> prefControllers = new ArrayList<>();
prefControllers.add(new ZenModeHeaderController(context, "header", this, mBackend));
prefControllers.add(new ZenModeButtonPreferenceController(context, "activate", mBackend));
prefControllers.add(new ZenModeActionsPreferenceController(context, "actions", mBackend));
prefControllers.add(new ZenModePeopleLinkPreferenceController(
context, "zen_mode_people", mBackend));
prefControllers.add(new ZenModeAppsLinkPreferenceController(

View File

@@ -51,12 +51,12 @@ abstract class ZenModeFragmentBase extends ZenModesFragmentBase {
if (bundle != null && bundle.containsKey(MODE_ID)) {
String id = bundle.getString(MODE_ID);
if (!reloadMode(id)) {
Log.d(TAG, "Mode id " + id + " not found");
Log.e(TAG, "Mode id " + id + " not found");
toastAndFinish();
return;
}
} else {
Log.d(TAG, "Mode id required to set mode config settings");
Log.e(TAG, "Mode id required to set mode config settings");
toastAndFinish();
return;
}

View File

@@ -63,9 +63,8 @@ class ZenModeHeaderController extends AbstractZenModePreferenceController {
FutureUtil.whenDone(
zenMode.getIcon(mContext, IconLoader.getInstance()),
icon -> mHeaderController.setIcon(icon)
.setLabel(zenMode.getRule().getName())
.done(false /* rebindActions */),
icon -> mHeaderController.setIcon(IconUtil.applyTint(mContext, icon))
.done(/* rebindActions= */ false),
mContext.getMainExecutor());
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2024 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.settings.notification.modes;
import android.app.settings.SettingsEnums;
import android.content.Context;
import com.android.settings.R;
import com.android.settingslib.core.AbstractPreferenceController;
import com.google.common.collect.ImmutableList;
import java.util.List;
public class ZenModeIconPickerFragment extends ZenModeFragmentBase {
@Override
protected int getPreferenceScreenResId() {
return R.xml.modes_icon_picker;
}
@Override
public int getMetricsCategory() {
// TODO: b/332937635 - make this the correct metrics category
return SettingsEnums.NOTIFICATION_ZEN_MODE_AUTOMATION;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return ImmutableList.of(
new ZenModeIconPickerIconPreferenceController(context, "current_icon", this,
mBackend),
new ZenModeIconPickerListPreferenceController(context, "icon_list", this,
mBackend));
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2024 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.settings.notification.modes;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.widget.LayoutPreference;
class ZenModeIconPickerIconPreferenceController extends AbstractZenModePreferenceController {
private final DashboardFragment mFragment;
private EntityHeaderController mHeaderController;
ZenModeIconPickerIconPreferenceController(@NonNull Context context, @NonNull String key,
@NonNull DashboardFragment fragment, @Nullable ZenModesBackend backend) {
super(context, key, backend);
mFragment = fragment;
}
@Override
void updateState(Preference preference, @NonNull ZenMode zenMode) {
preference.setSelectable(false);
if (mHeaderController == null) {
final LayoutPreference pref = (LayoutPreference) preference;
mHeaderController = EntityHeaderController.newInstance(
mFragment.getActivity(),
mFragment,
pref.findViewById(R.id.entity_header));
}
FutureUtil.whenDone(
zenMode.getIcon(mContext, IconLoader.getInstance()),
icon -> mHeaderController.setIcon(IconUtil.applyTint(mContext, icon))
.done(/* rebindActions= */ false),
mContext.getMainExecutor());
}
}

View File

@@ -0,0 +1,170 @@
/*
* Copyright (C) 2024 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.settings.notification.modes;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.widget.LayoutPreference;
import com.google.common.collect.ImmutableList;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
class ZenModeIconPickerListPreferenceController extends AbstractZenModePreferenceController {
private final DashboardFragment mFragment;
private IconAdapter mAdapter;
ZenModeIconPickerListPreferenceController(@NonNull Context context, @NonNull String key,
@NonNull DashboardFragment fragment, @Nullable ZenModesBackend backend) {
super(context, key, backend);
mFragment = fragment;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
LayoutPreference pref = screen.findPreference(getPreferenceKey());
if (pref == null) {
return;
}
if (mAdapter == null) {
// TODO: b/333901673 - This is just an example; replace with correct list.
List<IconInfo> exampleIcons =
Arrays.stream(android.R.drawable.class.getFields())
.filter(
f -> Modifier.isStatic(f.getModifiers())
&& f.getName().startsWith("ic_"))
.sorted(Comparator.comparing(Field::getName))
.limit(20)
.map(f -> {
try {
return new IconInfo(f.getInt(null), f.getName());
} catch (IllegalAccessException e) {
return null;
}
})
.filter(Objects::nonNull)
.toList();
mAdapter = new IconAdapter(exampleIcons);
}
RecyclerView recyclerView = pref.findViewById(R.id.icon_list);
recyclerView.setLayoutManager(new AutoFitGridLayoutManager(mContext));
recyclerView.setAdapter(mAdapter);
recyclerView.setHasFixedSize(true);
}
@VisibleForTesting
void onIconSelected(@DrawableRes int resId) {
saveMode(mode -> {
mode.getRule().setIconResId(resId);
return mode;
});
mFragment.finish();
}
@Override
void updateState(Preference preference, @NonNull ZenMode zenMode) {
// Nothing to do, the current icon is shown in a different preference.
}
private record IconInfo(@DrawableRes int resId, String description) { }
private class IconHolder extends RecyclerView.ViewHolder {
private final ImageView mImageView;
IconHolder(@NonNull View itemView) {
super(itemView);
mImageView = itemView.findViewById(R.id.icon_image_view);
}
void bindIcon(IconInfo icon) {
mImageView.setImageDrawable(
IconUtil.makeIconCircle(itemView.getContext(), icon.resId()));
itemView.setContentDescription(icon.description());
itemView.setOnClickListener(v -> onIconSelected(icon.resId()));
}
}
private class IconAdapter extends RecyclerView.Adapter<IconHolder> {
private final ImmutableList<IconInfo> mIconResources;
private IconAdapter(List<IconInfo> iconOptions) {
mIconResources = ImmutableList.copyOf(iconOptions);
}
@NonNull
@Override
public IconHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(
R.layout.modes_icon_list_item, parent, false);
return new IconHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull IconHolder holder, int position) {
holder.bindIcon(mIconResources.get(position));
}
@Override
public int getItemCount() {
return mIconResources.size();
}
}
private static class AutoFitGridLayoutManager extends GridLayoutManager {
private final float mColumnWidth;
AutoFitGridLayoutManager(Context context) {
super(context, /* spanCount= */ 1);
this.mColumnWidth = context
.getResources()
.getDimensionPixelSize(R.dimen.zen_mode_icon_list_item_size);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
final int totalSpace = getWidth() - getPaddingRight() - getPaddingLeft();
final int spanCount = Math.max(1, (int) (totalSpace / mColumnWidth));
setSpanCount(spanCount);
super.onLayoutChildren(recycler, state);
}
}
}

View File

@@ -23,7 +23,6 @@ import android.os.Bundle;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.Utils;
/**
* Preference representing a single mode item on the modes aggregator page. Clicking on this
@@ -59,11 +58,7 @@ class ZenModeListPreference extends RestrictedPreference {
FutureUtil.whenDone(
mZenMode.getIcon(mContext, IconLoader.getInstance()),
icon -> {
icon.setTintList(
Utils.getColorAttr(mContext, android.R.attr.colorControlNormal));
setIcon(icon);
},
icon -> setIcon(IconUtil.applyTint(mContext, icon)),
mContext.getMainExecutor());
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2024 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.settings.notification.modes;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AutomaticZenRule;
import android.content.Context;
import android.net.Uri;
import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.widget.LayoutPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class ZenModeIconPickerListPreferenceControllerTest {
private static final ZenMode ZEN_MODE = new ZenMode(
"mode_id",
new AutomaticZenRule.Builder("mode name", Uri.parse("mode")).build(),
/* isActive= */ false);
private ZenModesBackend mBackend;
private ZenModeIconPickerListPreferenceController mController;
private PreferenceScreen mPreferenceScreen;
private RecyclerView mRecyclerView;
@Before
public void setUp() {
Context context = RuntimeEnvironment.getApplication();
mBackend = mock(ZenModesBackend.class);
DashboardFragment fragment = mock(DashboardFragment.class);
mController = new ZenModeIconPickerListPreferenceController(
RuntimeEnvironment.getApplication(), "icon_list", fragment, mBackend);
mRecyclerView = new RecyclerView(context);
mRecyclerView.setId(R.id.icon_list);
LayoutPreference layoutPreference = new LayoutPreference(context, mRecyclerView);
mPreferenceScreen = mock(PreferenceScreen.class);
when(mPreferenceScreen.findPreference(eq("icon_list"))).thenReturn(layoutPreference);
}
@Test
public void displayPreference_loadsIcons() {
mController.displayPreference(mPreferenceScreen);
assertThat(mRecyclerView.getAdapter()).isNotNull();
assertThat(mRecyclerView.getAdapter().getItemCount()).isEqualTo(20);
}
@Test
public void selectIcon_updatesMode() {
mController.setZenMode(ZEN_MODE);
mController.onIconSelected(R.drawable.ic_android);
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());
assertThat(captor.getValue().getRule().getIconResId()).isEqualTo(R.drawable.ic_android);
}
}