Snap for 12116631 from 2004df98f1 to 24Q4-release

Change-Id: I25146143976f7c437163219c9bcf5732f63e53f0
This commit is contained in:
Android Build Coastguard Worker
2024-07-20 01:21:11 +00:00
22 changed files with 481 additions and 118 deletions

View File

@@ -0,0 +1,28 @@
<!--
~ 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.
-->
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="oval">
<size
android:width="@dimen/zen_mode_circular_icon_diameter"
android:height="@dimen/zen_mode_circular_icon_diameter" />
<solid android:color="?androidprv:attr/materialColorSecondaryContainer" />
<!-- TODO: b/346551087 - Include border (or not) according to final design
<stroke android:width="1dp" android:color="?androidprv:attr/materialColorOnSecondaryContainer" />
-->
</shape>

View File

@@ -17,8 +17,8 @@
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/zen_mode_circular_icon_size"
android:layout_height="@dimen/zen_mode_circular_icon_size"
android:layout_width="@dimen/zen_mode_circular_icon_diameter"
android:layout_height="@dimen/zen_mode_circular_icon_diameter"
android:layout_marginTop="@dimen/zen_mode_circular_icon_margin_vertical"
android:layout_marginBottom="@dimen/zen_mode_circular_icon_margin_vertical"
android:layout_marginEnd="@dimen/zen_mode_circular_icon_margin_between" />

View File

@@ -0,0 +1,32 @@
<?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.
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="@dimen/zen_mode_circular_icon_diameter"
android:layout_height="@dimen/zen_mode_circular_icon_diameter"
android:layout_marginTop="@dimen/zen_mode_circular_icon_margin_vertical"
android:layout_marginBottom="@dimen/zen_mode_circular_icon_margin_vertical"
android:gravity="center"
android:padding="4dp"
android:drawablePadding="0dp"
android:background="@drawable/preference_circular_icons_plus_item_background"
android:textColor="?androidprv:attr/materialColorOnSecondaryContainer"
android:maxLines="1"
android:autoSizeTextType="uniform"
android:autoSizeMinTextSize="6sp" />

View File

@@ -509,7 +509,8 @@
<dimen name="zen_mode_icon_list_item_circle_diameter">56dp</dimen>
<dimen name="zen_mode_icon_list_item_icon_size">32dp</dimen>
<!-- For the items in the CircularIconsPreference (contacts, apps, sound channels). -->
<dimen name="zen_mode_circular_icon_size">32dp</dimen>
<dimen name="zen_mode_circular_icon_diameter">32dp</dimen>
<dimen name="zen_mode_circular_icon_inner_icon_size">20dp</dimen>
<dimen name="zen_mode_circular_icon_margin_between">4dp</dimen>
<dimen name="zen_mode_circular_icon_margin_vertical">8dp</dimen>
</resources>

View File

@@ -8871,9 +8871,10 @@
<string name="notif_listener_more_settings_desc">More settings are available inside this app</string>
<!-- Title for Polite Notifications setting [CHAR LIMIT=45]-->
<string name="notification_polite_title">Adaptive Notifications</string>
<string name="notification_polite_main_control_title">Use adaptive notifications</string>
<string name="notification_polite_description">When you get many notifications within a short time, your phone will lower volume and minimize pop-ups on screen for up to two minutes. Calls, alarms, and priority conversations still vibrate, make a sound, or show up on the screen, and all notifications are easy to find when you pull down from the top of the screen.</string>
<string name="notification_polite_title">Notification cooldown</string>
<string name="notification_polite_main_control_title">Use notification cooldown</string>
<string name="notification_polite_description">When you receive many notifications within a short time, your device will lower it\u2019s volume and minimize alerts for up to 2 minutes. Calls, alarms, and priority conversations aren\u2019t affected.
\n\nNotifications received during the cooldown can be found by pulling down from the top of the screen.</string>
<string name="notification_polite_work">Apply to work profiles</string>
<string name="notification_polite_work_summary">Apply to work profile apps</string>
@@ -9365,6 +9366,8 @@
<string name="zen_mode_apps_work_app"><xliff:g id="app_label" example="Chrome">%s</xliff:g> (Work)</string>
<!-- Text displayed (for a brief time) while the list of bypassing apps is being fetched. Will be replaced by a zen_mode_apps_subtext. [CHAR_LIMIT=60] -->
<string name="zen_mode_apps_calculating">Calculating\u2026</string>
<!-- Priority Modes: Format for a string displayed when there are more items (e.g. apps, contacts) that can be shown. For example, we show (A)(B)(C)(+5), where this string represents the "+5" value. Needs to be as compact as possible, since it will be drawn in a really small area. [CHAR_LIMIT=4] -->
<string name="zen_mode_plus_n_items">+<xliff:g id="number" example="42">%d</xliff:g></string>
<!-- [CHAR LIMIT=100] Zen mode settings: Allow apps to bypass DND -->
<string name="zen_mode_bypassing_apps">Allow apps to override</string>

View File

@@ -955,11 +955,16 @@
<item name="biometricsEnrollProgressHelpWithTalkback">@color/udfps_enroll_progress_help_with_talkback</item>
</style>
<style name="ScreenLockPasswordHintTextFontStyle">
<style name="ScreenLockPasswordHintTextFontStyleError">
<item name="android:textColor">?android:attr/colorError</item>
<item name="android:fontFamily">google-sans-text</item>
</style>
<style name="ScreenLockPasswordHintTextFontStyle">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:fontFamily">google-sans-text</item>
</style>
<style name="PrivateSpaceSetupTextFontStyle" parent="@android:style/TextAppearance.DeviceDefault">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:fontFamily">google-sans-text</item>

View File

@@ -154,7 +154,7 @@ public class AccessibilitySettings extends DashboardFragment implements
};
@VisibleForTesting
final AccessibilitySettingsContentObserver mSettingsContentObserver;
AccessibilitySettingsContentObserver mSettingsContentObserver;
private final Map<String, PreferenceCategory> mCategoryToPrefCategoryMap =
new ArrayMap<>();
@@ -168,9 +168,14 @@ public class AccessibilitySettings extends DashboardFragment implements
private boolean mIsForeground = true;
public AccessibilitySettings() {
mSettingsContentObserver = new AccessibilitySettingsContentObserver(mHandler);
}
private void initializeSettingsContentObserver() {
// Observe changes to anything that the shortcut can toggle, so we can reflect updates
final Collection<AccessibilityShortcutController.FrameworkFeatureInfo> features =
AccessibilityShortcutController.getFrameworkShortcutFeaturesMap().values();
AccessibilityShortcutController
.getFrameworkShortcutFeaturesMap().values();
final List<String> shortcutFeatureKeys = new ArrayList<>(features.size());
for (AccessibilityShortcutController.FrameworkFeatureInfo feature : features) {
final String key = feature.getSettingKey();
@@ -188,7 +193,6 @@ public class AccessibilitySettings extends DashboardFragment implements
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_STICKY_KEYS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SLOW_KEYS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS);
mSettingsContentObserver = new AccessibilitySettingsContentObserver(mHandler);
mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys,
key -> onContentChanged());
}
@@ -213,6 +217,7 @@ public class AccessibilitySettings extends DashboardFragment implements
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
initializeSettingsContentObserver();
initializeAllPreferences();
updateAllPreferences();
mNeedPreferencesUpdate = false;

View File

@@ -22,6 +22,7 @@ import android.graphics.drawable.Drawable;
import androidx.annotation.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
@@ -61,6 +62,15 @@ class CircularIconSet<T> {
mCachedIcons = new ConcurrentHashMap<>();
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("items", mItems).toString();
}
boolean hasSameItemsAs(CircularIconSet<?> other) {
return other != null && this.mItems.equals(other.mItems);
}
int size() {
return mItems.size();
}

View File

@@ -21,15 +21,17 @@ import static com.google.common.base.Preconditions.checkState;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -37,11 +39,12 @@ import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.Utils;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@@ -50,8 +53,9 @@ public class CircularIconsPreference extends RestrictedPreference {
private Executor mUiExecutor;
@Nullable private LinearLayout mIconContainer;
@Nullable private CircularIconSet<?> mPendingIconSet;
@Nullable private ListenableFuture<?> mPendingLoadIconsFuture;
@Nullable private CircularIconSet<?> mIconSet;
@Nullable private CircularIconSet<?> mPendingDisplayIconSet;
@Nullable private ListenableFuture<List<Drawable>> mPendingLoadIconsFuture;
public CircularIconsPreference(Context context) {
super(context);
@@ -94,17 +98,25 @@ public class CircularIconsPreference extends RestrictedPreference {
}
private void displayIconsIfPending() {
CircularIconSet<?> pendingIconSet = mPendingIconSet;
CircularIconSet<?> pendingIconSet = mPendingDisplayIconSet;
if (pendingIconSet != null) {
mPendingIconSet = null;
displayIcons(pendingIconSet);
mPendingDisplayIconSet = null;
displayIconsInternal(pendingIconSet);
}
}
void displayIcons(CircularIconSet<?> iconSet) {
if (mIconSet != null && mIconSet.hasSameItemsAs(iconSet)) {
return;
}
mIconSet = iconSet;
displayIconsInternal(iconSet);
}
void displayIconsInternal(CircularIconSet<?> iconSet) {
if (mIconContainer == null) {
// Too soon, wait for bind.
mPendingIconSet = iconSet;
mPendingDisplayIconSet = iconSet;
return;
}
mIconContainer.setVisibility(iconSet.size() != 0 ? View.VISIBLE : View.GONE);
@@ -113,30 +125,31 @@ public class CircularIconsPreference extends RestrictedPreference {
}
if (mIconContainer.getMeasuredWidth() == 0) {
// Too soon, wait for first measure to know width.
mPendingIconSet = iconSet;
ViewTreeObserver vto = mIconContainer.getViewTreeObserver();
vto.addOnGlobalLayoutListener(() ->
mPendingDisplayIconSet = iconSet;
mIconContainer.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
vto.removeOnGlobalLayoutListener(this);
checkNotNull(mIconContainer).getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
displayIconsIfPending();
}
});
}
);
return;
}
mIconContainer.setVisibility(View.VISIBLE);
Resources res = getContext().getResources();
int availableSpace = mIconContainer.getMeasuredWidth();
int iconHorizontalSpace = res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_size)
int iconHorizontalSpace = res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter)
+ res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_margin_between);
int numIconsThatFit = availableSpace / iconHorizontalSpace;
List<ListenableFuture<Drawable>> iconFutures;
int extraItems = 0;
int extraItems;
if (iconSet.size() > numIconsThatFit) {
// Reserve one space for the (+xx) circle.
// Reserve one space for the (+xx) textview.
int numIconsToShow = numIconsThatFit - 1;
if (numIconsToShow < 0) {
numIconsToShow = 0;
@@ -146,6 +159,7 @@ public class CircularIconsPreference extends RestrictedPreference {
} else {
// Fit exactly or with remaining space.
iconFutures = iconSet.getIcons();
extraItems = 0;
}
displayIconsWhenReady(iconFutures, extraItems);
@@ -158,33 +172,45 @@ public class CircularIconsPreference extends RestrictedPreference {
mPendingLoadIconsFuture.cancel(true);
}
int numCircles = iconFutures.size() + (extraItems > 0 ? 1 : 0);
if (mIconContainer.getChildCount() > numCircles) {
mIconContainer.removeViews(numCircles, mIconContainer.getChildCount() - numCircles);
// Rearrange child views until we have <numImages> ImageViews...
LayoutInflater inflater = LayoutInflater.from(getContext());
int numImages = iconFutures.size();
int numImageViews = getChildCount(mIconContainer, ImageView.class);
if (numImages > numImageViews) {
for (int i = 0; i < numImages - numImageViews; i++) {
ImageView imageView = (ImageView) inflater.inflate(
R.layout.preference_circular_icons_item, mIconContainer, false);
mIconContainer.addView(imageView, 0);
}
} else if (numImageViews > numImages) {
for (int i = 0; i < numImageViews - numImages; i++) {
mIconContainer.removeViewAt(0);
}
}
for (int i = mIconContainer.getChildCount(); i < numCircles; i++) {
ImageView imageView = (ImageView) LayoutInflater.from(getContext()).inflate(
R.layout.preference_circular_icons_item, mIconContainer, false);
mIconContainer.addView(imageView);
// ... plus 0/1 TextViews at the end.
if (extraItems > 0 && !(getLastChild(mIconContainer) instanceof TextView)) {
// TODO: b/346551087 - Check TODO in preference_circular_icons_plus_item_background
TextView plusView = (TextView) inflater.inflate(
R.layout.preference_circular_icons_plus_item, mIconContainer, false);
mIconContainer.addView(plusView);
} else if (extraItems == 0 && (getLastChild(mIconContainer) instanceof TextView)) {
mIconContainer.removeViewAt(mIconContainer.getChildCount() - 1);
}
// Set up placeholders and extra items indicator.
for (int i = 0; i < iconFutures.size(); i++) {
for (int i = 0; i < numImages; i++) {
ImageView imageView = (ImageView) mIconContainer.getChildAt(i);
// TODO: b/346551087 - proper color and shape, should be a gray circle.
imageView.setImageDrawable(new ColorDrawable(Color.RED));
imageView.setImageDrawable(getPlaceholderImage(getContext()));
}
if (extraItems > 0) {
ImageView imageView = (ImageView) mIconContainer.getChildAt(
mIconContainer.getChildCount() - 1);
// TODO: b/346551087 - proper color and shape and number.
imageView.setImageDrawable(new ColorDrawable(Color.BLUE));
TextView textView = (TextView) checkNotNull(getLastChild(mIconContainer));
textView.setText(getContext().getString(R.string.zen_mode_plus_n_items, extraItems));
}
// Display icons when all are ready (more consistent than randomly loading).
mPendingLoadIconsFuture = Futures.allAsList(iconFutures);
FutureUtil.whenDone(
Futures.allAsList(iconFutures),
mPendingLoadIconsFuture,
icons -> {
checkState(mIconContainer != null);
for (int i = 0; i < icons.size(); i++) {
@@ -194,15 +220,54 @@ public class CircularIconsPreference extends RestrictedPreference {
mUiExecutor);
}
private static Drawable getPlaceholderImage(Context context) {
ShapeDrawable placeholder = new ShapeDrawable(new OvalShape());
placeholder.setTintList(Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorSecondaryContainer));
return placeholder;
}
private static int getChildCount(ViewGroup parent, Class<? extends View> childClass) {
int count = 0;
for (int i = 0; i < parent.getChildCount(); i++) {
if (childClass.isInstance(parent.getChildAt(i))) {
count++;
}
}
return count;
}
@Nullable
private static View getLastChild(ViewGroup parent) {
if (parent.getChildCount() == 0) {
return null;
}
return parent.getChildAt(parent.getChildCount() - 1);
}
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
ImmutableList<ImageView> getIconViews() {
List<Drawable> getIcons() {
if (mIconContainer == null) {
return ImmutableList.of();
return List.of();
}
ImmutableList.Builder<ImageView> imageViews = new ImmutableList.Builder<>();
for (int i = 0; i < mIconContainer.getChildCount(); i++) {
imageViews.add((ImageView) mIconContainer.getChildAt(i));
ArrayList<Drawable> drawables = new ArrayList<>();
for (int i = 0; i < getChildCount(mIconContainer, ImageView.class); i++) {
drawables.add(((ImageView) mIconContainer.getChildAt(i)).getDrawable());
}
return drawables;
}
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
@Nullable
String getPlusText() {
if (mIconContainer == null) {
return null;
}
View lastChild = getLastChild(mIconContainer);
if (lastChild instanceof TextView tv) {
return tv.getText() != null ? tv.getText().toString() : null;
} else {
return null;
}
return imageViews.build();
}
}

View File

@@ -55,7 +55,7 @@ class IconUtil {
* Returns a variant of the supplied {@code icon} to be used as the header in the icon picker.
* The inner icon is 48x48dp and it's contained into a circle of diameter 90dp.
*/
static Drawable makeBigIconCircle(@NonNull Context context, Drawable icon) {
static Drawable makeIconPickerHeader(@NonNull Context context, Drawable icon) {
return composeIconCircle(
Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorSecondaryContainer),
@@ -73,7 +73,7 @@ class IconUtil {
* The inner icon is 36x36dp and it's contained into a circle of diameter 54dp. It's also set up
* so that selection and pressed states are represented in the color.
*/
static Drawable makeSmallIconCircle(@NonNull Context context, @DrawableRes int iconResId) {
static Drawable makeIconPickerItem(@NonNull Context context, @DrawableRes int iconResId) {
return composeIconCircle(
context.getColorStateList(R.color.modes_icon_picker_item_background),
context.getResources().getDimensionPixelSize(
@@ -84,6 +84,24 @@ class IconUtil {
R.dimen.zen_mode_icon_list_item_icon_size));
}
/**
* Returns a variant of the supplied icon to be used in a {@link CircularIconsPreference}. The
* inner icon is 20x20 dp and it's contained in a circle of diameter 32dp, and is tinted
* with the "material secondary container" color combination.
*/
static Drawable makeSoundIcon(@NonNull Context context, @DrawableRes int iconResId) {
return composeIconCircle(
Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorSecondaryContainer),
context.getResources().getDimensionPixelSize(
R.dimen.zen_mode_circular_icon_diameter),
checkNotNull(context.getDrawable(iconResId)),
Utils.getColorAttr(context,
com.android.internal.R.attr.materialColorOnSecondaryContainer),
context.getResources().getDimensionPixelSize(
R.dimen.zen_mode_circular_icon_inner_icon_size));
}
private static Drawable composeIconCircle(ColorStateList circleColor, @Px int circleDiameterPx,
Drawable icon, ColorStateList iconColor, @Px int iconSizePx) {
ShapeDrawable background = new ShapeDrawable(new OvalShape());
@@ -93,11 +111,11 @@ class IconUtil {
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[] { background, foreground });
layerDrawable.setBounds(0, 0, circleDiameterPx, circleDiameterPx);
layerDrawable.setLayerSize(0, circleDiameterPx, circleDiameterPx);
layerDrawable.setLayerGravity(1, Gravity.CENTER);
layerDrawable.setLayerSize(1, iconSizePx, iconSizePx);
layerDrawable.setBounds(0, 0, circleDiameterPx, circleDiameterPx);
return layerDrawable;
}
}

View File

@@ -64,7 +64,7 @@ class ZenModeIconPickerIconPreferenceController extends AbstractZenModePreferenc
FutureUtil.whenDone(
zenMode.getIcon(mContext, ZenIconLoader.getInstance()),
icon -> mHeaderController.setIcon(IconUtil.makeBigIconCircle(mContext, icon))
icon -> mHeaderController.setIcon(IconUtil.makeIconPickerHeader(mContext, icon))
.done(/* rebindActions= */ false),
mContext.getMainExecutor());
}

View File

@@ -156,7 +156,7 @@ class ZenModeIconPickerListPreferenceController extends AbstractZenModePreferenc
public void onBindViewHolder(@NonNull IconHolder holder, int position) {
IconOptionsProvider.IconInfo iconInfo = mIconResources.get(position);
Drawable iconDrawable = mIconCache.computeIfAbsent(iconInfo,
info -> IconUtil.makeSmallIconCircle(mContext, info.resId()));
info -> IconUtil.makeIconPickerItem(mContext, info.resId()));
holder.bindIcon(iconInfo, iconDrawable);
}

View File

@@ -17,19 +17,44 @@
package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_ALARMS;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_EVENTS;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MEDIA;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM;
import android.content.Context;
import android.service.notification.ZenPolicy;
import androidx.annotation.NonNull;
import androidx.preference.Preference;
import com.android.settingslib.notification.modes.ZenMode;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
/**
* Preference with a link and summary about what other sounds can break through the mode
*/
class ZenModeOtherLinkPreferenceController extends AbstractZenModePreferenceController {
// TODO: b/346551087 - Use proper icons
private static final ImmutableMap</* @PriorityCategory */ Integer, /* @DrawableRes */ Integer>
PRIORITIES_TO_ICONS = ImmutableMap.of(
PRIORITY_CATEGORY_ALARMS,
com.android.internal.R.drawable.ic_audio_alarm,
PRIORITY_CATEGORY_MEDIA,
com.android.settings.R.drawable.ic_media_stream,
PRIORITY_CATEGORY_SYSTEM,
com.android.settings.R.drawable.ic_settings_keyboards,
PRIORITY_CATEGORY_REMINDERS,
com.android.internal.R.drawable.ic_popup_reminder,
PRIORITY_CATEGORY_EVENTS,
com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar);
private final ZenModeSummaryHelper mSummaryHelper;
public ZenModeOtherLinkPreferenceController(Context context, String key,
@@ -51,7 +76,17 @@ class ZenModeOtherLinkPreferenceController extends AbstractZenModePreferenceCont
zenMode.getId(), 0).toIntent());
preference.setSummary(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode));
// TODO: b/346551087 - Show media icons
((CircularIconsPreference) preference).displayIcons(CircularIconSet.EMPTY);
((CircularIconsPreference) preference).displayIcons(getSoundIcons(zenMode.getPolicy()));
}
private CircularIconSet<Integer> getSoundIcons(ZenPolicy policy) {
ImmutableList.Builder<Integer> icons = new ImmutableList.Builder<>();
for (Map.Entry<Integer, Integer> entry : PRIORITIES_TO_ICONS.entrySet()) {
if (policy.isCategoryAllowed(entry.getKey(), false)) {
icons.add(entry.getValue());
}
}
return new CircularIconSet<>(icons.build(),
iconResId -> IconUtil.makeSoundIcon(mContext, iconResId));
}
}

View File

@@ -56,6 +56,8 @@ import com.android.settings.R;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.notification.modes.ZenMode;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -85,14 +87,18 @@ class ZenModeSummaryHelper {
PRIORITY_CATEGORY_REPEAT_CALLERS,
};
static final ImmutableList</* @PriorityCategory */ Integer> OTHER_SOUND_CATEGORIES =
ImmutableList.of(
PRIORITY_CATEGORY_ALARMS,
PRIORITY_CATEGORY_MEDIA,
PRIORITY_CATEGORY_SYSTEM,
PRIORITY_CATEGORY_REMINDERS,
PRIORITY_CATEGORY_EVENTS);
String getOtherSoundCategoriesSummary(ZenMode zenMode) {
List<String> enabledCategories = getEnabledCategories(
zenMode.getPolicy(),
category -> PRIORITY_CATEGORY_ALARMS == category
|| PRIORITY_CATEGORY_MEDIA == category
|| PRIORITY_CATEGORY_SYSTEM == category
|| PRIORITY_CATEGORY_REMINDERS == category
|| PRIORITY_CATEGORY_EVENTS == category,
OTHER_SOUND_CATEGORIES::contains,
true);
int numCategories = enabledCategories.size();
MessageFormat msgFormat = new MessageFormat(

View File

@@ -271,6 +271,8 @@ public class ChooseLockPassword extends SettingsActivity {
private static final int CONFIRM_EXISTING_REQUEST = 58;
static final int RESULT_FINISHED = RESULT_FIRST_USER;
private boolean mIsErrorTooShort = true;
/** Used to store the profile type for which pin/password is being set */
protected enum ProfileType {
None,
@@ -672,6 +674,11 @@ public class ChooseLockPassword extends SettingsActivity {
view.addView(mPasswordRestrictionView);
}
@VisibleForTesting
View getPasswordRequirementsView() {
return mPasswordRestrictionView;
}
private void createHintMessageView(ViewGroup view) {
if (mPasswordRestrictionView != null) {
return;
@@ -855,6 +862,7 @@ public class ChooseLockPassword extends SettingsActivity {
*/
String[] convertErrorCodeToMessages() {
List<String> messages = new ArrayList<>();
mIsErrorTooShort = false;
for (PasswordValidationError error : mValidationErrors) {
switch (error.errorCode) {
case CONTAINS_INVALID_CHARACTERS:
@@ -889,6 +897,7 @@ public class ChooseLockPassword extends SettingsActivity {
R.string.lockpassword_password_requires_nonnumerical));
break;
case TOO_SHORT:
mIsErrorTooShort = true;
String message = StringUtil.getIcuPluralsString(getContext(),
error.requirement,
mIsAlphaMode
@@ -951,12 +960,13 @@ public class ChooseLockPassword extends SettingsActivity {
? LockscreenCredential.createPassword(mPasswordEntry.getText())
: LockscreenCredential.createPin(mPasswordEntry.getText());
final int length = password.size();
if (mUiStage == Stage.Introduction) {
mPasswordRestrictionView.setVisibility(View.VISIBLE);
final boolean passwordCompliant = validatePassword(password);
String[] messages = convertErrorCodeToMessages();
// Update the fulfillment of requirements.
mPasswordRequirementAdapter.setRequirements(messages);
mPasswordRequirementAdapter.setRequirements(messages, mIsErrorTooShort);
// set the visibility of pin_auto_confirm option accordingly
setAutoPinConfirmOption(passwordCompliant, length);
// Enable/Disable the next button accordingly.

View File

@@ -36,6 +36,7 @@ public class PasswordRequirementAdapter extends
private String[] mRequirements;
private Context mContext;
private boolean mIsTooShortError = true;
public PasswordRequirementAdapter(Context context) {
mContext = context;
@@ -54,8 +55,9 @@ public class PasswordRequirementAdapter extends
return mRequirements.length;
}
public void setRequirements(String[] requirements) {
public void setRequirements(String[] requirements, boolean isPasswordShort) {
mRequirements = requirements;
mIsTooShortError = isPasswordShort;
notifyDataSetChanged();
}
@@ -74,7 +76,12 @@ public class PasswordRequirementAdapter extends
final int fontSize = mContext.getResources().getDimensionPixelSize(
R.dimen.password_requirement_font_size);
holder.mDescriptionText.setText(mRequirements[position]);
holder.mDescriptionText.setTextAppearance(R.style.ScreenLockPasswordHintTextFontStyle);
if (mIsTooShortError) {
holder.mDescriptionText.setTextAppearance(R.style.ScreenLockPasswordHintTextFontStyle);
} else {
holder.mDescriptionText.
setTextAppearance(R.style.ScreenLockPasswordHintTextFontStyleError);
}
holder.mDescriptionText.setTextSize(fontSize / mContext.getResources()
.getDisplayMetrics().scaledDensity);
}

View File

@@ -48,8 +48,7 @@ public class SmartForwardingFragment extends PreferenceFragmentCompat
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.smart_forwarding_switch, rootKey);
String title = getResources().getString(R.string.smart_forwarding_title);
getActivity().getActionBar().setTitle(title);
getActivity().setTitle(R.string.smart_forwarding_title);
TwoStatePreference smartForwardingSwitch = findPreference(KEY_SMART_FORWARDING_SWITCH);
if (turnOffSwitch) {

View File

@@ -53,6 +53,36 @@ public class CircularIconSetTest {
when(mDrawableLoader.apply(anyInt())).thenReturn(new ColorDrawable(Color.BLACK));
}
@Test
public void equals_sameItems_true() {
CircularIconSet<Integer> items1 = new CircularIconSet<>(ImmutableList.of(1, 2),
num -> new ColorDrawable(Color.BLUE));
CircularIconSet<Integer> items2 = new CircularIconSet<>(ImmutableList.of(1, 2),
num -> new ColorDrawable(Color.GREEN));
assertThat(items1.hasSameItemsAs(items2)).isTrue();
}
@Test
public void equals_differentTypes_false() {
CircularIconSet<Integer> items1 = new CircularIconSet<>(ImmutableList.of(1, 2),
num -> new ColorDrawable(Color.BLUE));
CircularIconSet<String> items2 = new CircularIconSet<>(ImmutableList.of("a", "b"),
str -> new ColorDrawable(Color.GREEN));
assertThat(items1.hasSameItemsAs(items2)).isFalse();
}
@Test
public void equals_differentItems_false() {
CircularIconSet<String> items1 = new CircularIconSet<>(ImmutableList.of("a", "b"),
str -> new ColorDrawable(Color.GREEN));
CircularIconSet<String> items2 = new CircularIconSet<>(ImmutableList.of("a", "b", "c"),
str -> new ColorDrawable(Color.GREEN));
assertThat(items1.hasSameItemsAs(items2)).isFalse();
}
@Test
public void getIcons_loadsAllIcons() {
CircularIconSet<Integer> set = new CircularIconSet<>(ImmutableList.of(1, 2, 3),

View File

@@ -19,16 +19,16 @@ package com.android.settings.notification.modes;
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import androidx.preference.PreferenceViewHolder;
@@ -41,11 +41,11 @@ import com.google.common.util.concurrent.MoreExecutors;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.List;
import java.util.stream.IntStream;
@RunWith(RobolectricTestRunner.class)
@@ -68,20 +68,30 @@ public class CircularIconsPreferenceTest {
// Tests should call bindAndMeasureViewHolder() so that icons can be added.
Resources res = mContext.getResources();
mOneIconWidth = res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_size)
mOneIconWidth = res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_diameter)
+ res.getDimensionPixelSize(R.dimen.zen_mode_circular_icon_margin_between);
}
private void bindAndMeasureViewHolder(int viewWidth) {
bindViewHolder();
measureViewHolder(viewWidth);
}
private void bindViewHolder() {
View preferenceView = LayoutInflater.from(mContext).inflate(mPreference.getLayoutResource(),
null);
mIconContainer = checkNotNull(preferenceView.findViewById(R.id.circles_container));
mIconContainer.measure(makeMeasureSpec(viewWidth, View.MeasureSpec.EXACTLY),
makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(preferenceView);
mPreference.onBindViewHolder(holder);
}
private void measureViewHolder(int viewWidth) {
checkState(mIconContainer != null, "Call bindViewHolder() first!");
mIconContainer.measure(makeMeasureSpec(viewWidth, View.MeasureSpec.EXACTLY),
makeMeasureSpec(1000, View.MeasureSpec.EXACTLY));
mIconContainer.getViewTreeObserver().dispatchOnGlobalLayout();
}
@Test
public void displayIcons_loadsIcons() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2),
@@ -90,13 +100,10 @@ public class CircularIconsPreferenceTest {
bindAndMeasureViewHolder(VIEW_WIDTH);
mPreference.displayIcons(iconSet);
assertThat(mPreference.getIconViews()).hasSize(2);
assertThat(mPreference.getIconViews().get(0).getDrawable())
.isInstanceOf(ColorDrawable.class);
assertThat(((ColorDrawable) mPreference.getIconViews().get(0).getDrawable()).getColor())
.isEqualTo(1);
assertThat(((ColorDrawable) mPreference.getIconViews().get(1).getDrawable()).getColor())
.isEqualTo(2);
assertThat(mPreference.getIcons()).hasSize(2);
assertThat(((ColorDrawable) mPreference.getIcons().get(0)).getColor()).isEqualTo(1);
assertThat(((ColorDrawable) mPreference.getIcons().get(1)).getColor()).isEqualTo(2);
assertThat(mPreference.getPlusText()).isNull();
assertThat(mIconContainer.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -111,74 +118,81 @@ public class CircularIconsPreferenceTest {
assertThat(mIconContainer.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void displayIcons_exactlyMaxIcons_loadsAllIcons() throws Exception {
int width = 300;
int fittingIcons = width / mOneIconWidth;
int fittingCircles = width / mOneIconWidth;
CircularIconSet<Integer> iconSet = new CircularIconSet<>(
IntStream.range(0, fittingIcons).boxed().toList(),
IntStream.range(0, fittingCircles).boxed().toList(),
ColorDrawable::new);
bindAndMeasureViewHolder(width);
mPreference.displayIcons(iconSet);
List<Drawable> displayedDrawables = mPreference.getIconViews().stream()
.map(ImageView::getDrawable).toList();
assertThat(displayedDrawables).hasSize(fittingIcons);
assertThat(displayedDrawables).containsExactlyElementsIn(
assertThat(mPreference.getIcons()).hasSize(fittingCircles);
assertThat(mPreference.getIcons()).containsExactlyElementsIn(
Futures.allAsList(iconSet.getIcons()).get()).inOrder();
assertThat(mPreference.getPlusText()).isNull();
}
@Test
public void displayIcons_tooManyIcons_loadsFirstNAndPlusIcon() throws Exception {
int width = 300;
int fittingIcons = width / mOneIconWidth;
int fittingCircles = width / mOneIconWidth;
CircularIconSet<Integer> iconSet = new CircularIconSet<>(
IntStream.range(0, fittingIcons + 5).boxed().toList(),
IntStream.range(0, fittingCircles + 5).boxed().toList(),
ColorDrawable::new);
bindAndMeasureViewHolder(width);
mPreference.displayIcons(iconSet);
List<Drawable> displayedDrawables = mPreference.getIconViews().stream()
.map(ImageView::getDrawable).toList();
assertThat(displayedDrawables).hasSize(fittingIcons);
// N-1 are actual icons, Nth icon is (+xx).
assertThat(displayedDrawables.stream().limit(fittingIcons - 1).toList())
.containsExactlyElementsIn(
Futures.allAsList(iconSet.getIcons(fittingIcons - 1)).get())
// N-1 icons, plus (+6) text.
assertThat(mPreference.getIcons()).hasSize(fittingCircles - 1);
assertThat(mPreference.getIcons()).containsExactlyElementsIn(
Futures.allAsList(iconSet.getIcons(fittingCircles - 1)).get())
.inOrder();
// TODO: b/346551087 - Correctly verify the plus-6 icon, once we generate it properly.
assertThat(((ColorDrawable) displayedDrawables.get(
displayedDrawables.size() - 1)).getColor()).isEqualTo(Color.BLUE);
assertThat(mPreference.getPlusText()).isEqualTo("+6");
}
@Test
public void displayIcons_teenyTinySpace_showsPlusIcon_noCrash() {
int width = 1;
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2),
ColorDrawable::new);
bindAndMeasureViewHolder(width);
bindAndMeasureViewHolder(1);
mPreference.displayIcons(iconSet);
assertThat(mPreference.getIconViews()).hasSize(1);
// TODO: b/346551087 - Correctly verify the plus-2 icon, once we generate it properly.
assertThat(((ColorDrawable) mPreference.getIconViews().get(0).getDrawable()).getColor())
.isEqualTo(Color.BLUE);
assertThat(mPreference.getIcons()).isEmpty();
assertThat(mPreference.getPlusText()).isEqualTo("+2");
}
@Test
public void displayIcons_beforeBind_loadsIconsOnBind() {
public void displayIcons_beforeBind_loadsIconsOnBindAndMeasure() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
ColorDrawable::new);
mPreference.displayIcons(iconSet);
assertThat(mPreference.getIconViews()).isEmpty();
assertThat(mPreference.getIcons()).isEmpty(); // Hold...
bindAndMeasureViewHolder(VIEW_WIDTH);
assertThat(mPreference.getIconViews()).hasSize(3);
bindViewHolder();
assertThat(mPreference.getIcons()).isEmpty(); // Hooooold...
measureViewHolder(VIEW_WIDTH);
assertThat(mPreference.getIcons()).hasSize(3);
}
@Test
public void displayIcons_beforeMeasure_loadsIconsOnMeasure() {
CircularIconSet<Integer> iconSet = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
ColorDrawable::new);
bindViewHolder();
mPreference.displayIcons(iconSet);
assertThat(mPreference.getIcons()).isEmpty();
measureViewHolder(VIEW_WIDTH);
assertThat(mPreference.getIcons()).hasSize(3);
}
@Test
@@ -192,10 +206,24 @@ public class CircularIconsPreferenceTest {
bindAndMeasureViewHolder(VIEW_WIDTH);
mPreference.displayIcons(threeIcons);
assertThat(mPreference.getIconViews()).hasSize(3);
assertThat(mPreference.getIcons()).hasSize(3);
mPreference.displayIcons(twoIcons);
assertThat(mPreference.getIconViews()).hasSize(2);
assertThat(mPreference.getIcons()).hasSize(2);
mPreference.displayIcons(fourIcons);
assertThat(mPreference.getIconViews()).hasSize(4);
assertThat(mPreference.getIcons()).hasSize(4);
}
@Test
public void displayIcons_sameSet_doesNotReloadIcons() {
CircularIconSet<Integer> one = new CircularIconSet<>(ImmutableList.of(1, 2, 3),
ColorDrawable::new);
CircularIconSet<Integer> same = Mockito.spy(new CircularIconSet<>(ImmutableList.of(1, 2, 3),
ColorDrawable::new));
when(same.getIcons()).thenThrow(new RuntimeException("Shouldn't be called!"));
bindAndMeasureViewHolder(VIEW_WIDTH);
mPreference.displayIcons(one);
mPreference.displayIcons(same); // if no exception, wasn't called.
}
}

View File

@@ -259,7 +259,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
appEntries.add(createAppEntry("test2", mContext.getUserId()));
mController.mAppSessionCallbacks.onRebuildComplete(appEntries);
assertThat(mPreference.getIconViews()).hasSize(2);
assertThat(mPreference.getIcons()).hasSize(2);
}
@Test

View File

@@ -17,7 +17,7 @@
package com.android.settings.notification.modes;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -25,8 +25,10 @@ import android.app.Flags;
import android.content.Context;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenPolicy;
import com.android.settingslib.notification.modes.TestModeBuilder;
import com.android.settingslib.notification.modes.ZenMode;
import org.junit.Before;
import org.junit.Rule;
@@ -60,13 +62,40 @@ public final class ZenModeOtherLinkPreferenceControllerTest {
}
@Test
@EnableFlags(Flags.FLAG_MODES_UI)
public void testHasSummary() {
public void updateState_loadsSummary() {
CircularIconsPreference pref = mock(CircularIconsPreference.class);
mController.updateZenMode(pref, TestModeBuilder.EXAMPLE);
verify(pref).setSummary(any());
verify(pref).displayIcons(eq(CircularIconSet.EMPTY));
}
@Test
public void updateState_loadsIcons() {
CircularIconsPreference pref = mock(CircularIconsPreference.class);
ZenMode mode = new TestModeBuilder()
.setZenPolicy(new ZenPolicy.Builder()
.disallowAllSounds()
.allowMedia(true)
.allowSystem(true)
.allowReminders(true)
.build())
.build();
mController.updateState(pref, mode);
verify(pref).displayIcons(argThat(iconSet -> iconSet.size() == 3));
}
@Test
public void updateState_loadsAllIcons() {
CircularIconsPreference pref = mock(CircularIconsPreference.class);
ZenMode mode = new TestModeBuilder()
.setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
.build();
mController.updateState(pref, mode);
verify(pref).displayIcons(argThat(iconSet ->
iconSet.size() == ZenModeSummaryHelper.OTHER_SOUND_CATEGORIES.size()));
}
}

View File

@@ -36,19 +36,25 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.robolectric.RuntimeEnvironment.application;
import static org.robolectric.Shadows.shadowOf;
import android.annotation.ColorInt;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.PasswordMetrics;
import android.app.admin.PasswordPolicy;
import android.content.Intent;
import android.os.Looper;
import android.os.UserHandle;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.widget.LockscreenCredential;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.password.ChooseLockPassword.ChooseLockPasswordFragment;
import com.android.settings.password.ChooseLockPassword.IntentBuilder;
import com.android.settings.testutils.shadow.SettingsShadowResources;
@@ -515,6 +521,52 @@ public class ChooseLockPasswordTest {
assertThat(pinAutoConfirmOption.isChecked()).isFalse();
}
@Test
public void defaultMessage_shouldBeInTextColorPrimary() {
final ChooseLockPassword passwordActivity = setupActivityWithPinTypeAndDefaultPolicy();
final ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(passwordActivity);
final ScrollToParentEditText passwordEntry = passwordActivity.findViewById(R.id.password_entry);
final RecyclerView view = (RecyclerView) fragment.getPasswordRequirementsView();
@ColorInt final int textColorPrimary = Utils.getColorAttrDefaultColor(passwordActivity,
android.R.attr.textColorPrimary);
passwordEntry.setText("");
fragment.updateUi();
shadowOf(Looper.getMainLooper()).idle();
TextView textView = (TextView)view.getLayoutManager().findViewByPosition(0);
assertThat(textView.getCurrentTextColor()).isEqualTo(textColorPrimary);
}
@Test
public void errorMessage_shouldBeColorError() {
final ChooseLockPassword passwordActivity = setupActivityWithPinTypeAndDefaultPolicy();
final ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(passwordActivity);
final ScrollToParentEditText passwordEntry = passwordActivity.findViewById(R.id.password_entry);
final RecyclerView view = (RecyclerView) fragment.getPasswordRequirementsView();
@ColorInt final int textColorPrimary = Utils.getColorAttrDefaultColor(passwordActivity,
android.R.attr.textColorPrimary);
@ColorInt final int colorError = Utils.getColorAttrDefaultColor(passwordActivity,
android.R.attr.colorError);
passwordEntry.setText("");
fragment.updateUi();
shadowOf(Looper.getMainLooper()).idle();
TextView textView = (TextView)view.getLayoutManager().findViewByPosition(0);
assertThat(textView.getCurrentTextColor()).isEqualTo(textColorPrimary);
// Password must be fewer than 17 digits, so this should give an error.
passwordEntry.setText("a".repeat(17));
fragment.updateUi();
shadowOf(Looper.getMainLooper()).idle();
textView = (TextView)view.getLayoutManager().findViewByPosition(0);
assertThat(textView.getCurrentTextColor()).isEqualTo(colorError);
}
private ChooseLockPassword setupActivityWithPinTypeAndDefaultPolicy() {
PasswordPolicy policy = new PasswordPolicy();
policy.quality = PASSWORD_QUALITY_UNSPECIFIED;
@@ -543,7 +595,7 @@ public class ChooseLockPasswordTest {
.setForFingerprint(addFingerprintExtra)
.build());
ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(passwordActivity);
return Shadows.shadowOf(((GlifLayout) fragment.getView()).getIcon());
return shadowOf(((GlifLayout) fragment.getView()).getIcon());
}
private void assertPasswordValidationResult(PasswordMetrics minMetrics,