Update top of modes page
* Show blurb based on mode type. * Make the icon bigger and highlight it when mode is active. * Increase spacing between elements. Also eliminate some code duplication between header of mode page and header of icon picker. Fixes: 355415875 Test: manual Flag: android.app.modes_ui Change-Id: I7e788b9b5920cedb791d1571b19b37e65ece6d0b
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.Flags;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
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.notification.modes.ZenIconLoader;
|
||||
import com.android.settingslib.notification.modes.ZenMode;
|
||||
import com.android.settingslib.widget.LayoutPreference;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
abstract class AbstractZenModeHeaderController extends AbstractZenModePreferenceController {
|
||||
|
||||
private final DashboardFragment mFragment;
|
||||
private EntityHeaderController mHeaderController;
|
||||
|
||||
AbstractZenModeHeaderController(
|
||||
@NonNull Context context,
|
||||
@NonNull String key,
|
||||
@NonNull DashboardFragment fragment) {
|
||||
super(context, key);
|
||||
mFragment = fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return Flags.modesApi() && Flags.modesUi();
|
||||
}
|
||||
|
||||
protected void updateIcon(Preference preference, @NonNull ZenMode zenMode, int iconSizePx,
|
||||
Function<Drawable, Drawable> modeIconStylist,
|
||||
@Nullable Consumer<ImageView> iconViewCustomizer) {
|
||||
if (mFragment == null) {
|
||||
return;
|
||||
}
|
||||
preference.setSelectable(false);
|
||||
|
||||
if (mHeaderController == null) {
|
||||
final LayoutPreference pref = (LayoutPreference) preference;
|
||||
mHeaderController = EntityHeaderController.newInstance(
|
||||
mFragment.getActivity(),
|
||||
mFragment,
|
||||
pref.findViewById(R.id.entity_header));
|
||||
}
|
||||
|
||||
ImageView iconView = ((LayoutPreference) preference).findViewById(R.id.entity_header_icon);
|
||||
if (iconView != null) {
|
||||
if (iconViewCustomizer != null) {
|
||||
iconViewCustomizer.accept(iconView);
|
||||
}
|
||||
ViewGroup.LayoutParams layoutParams = iconView.getLayoutParams();
|
||||
if (layoutParams.width != iconSizePx || layoutParams.height != iconSizePx) {
|
||||
layoutParams.width = iconSizePx;
|
||||
layoutParams.height = iconSizePx;
|
||||
iconView.setLayoutParams(layoutParams);
|
||||
}
|
||||
}
|
||||
|
||||
FutureUtil.whenDone(
|
||||
zenMode.getIcon(mContext, ZenIconLoader.getInstance()),
|
||||
icon -> mHeaderController
|
||||
.setIcon(modeIconStylist.apply(icon))
|
||||
.done(/* rebindActions= */ false),
|
||||
mContext.getMainExecutor());
|
||||
}
|
||||
}
|
||||
@@ -63,9 +63,27 @@ class IconUtil {
|
||||
return icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a variant of the supplied mode icon to be used as the header in the mode page. The
|
||||
* inner icon is 64x64 dp and it's contained in a 12-sided-cookie of 136dp diameter. It's
|
||||
* tinted with the "material secondary" color combination and the "selected" color variant
|
||||
* should be used for modes currently active.
|
||||
*/
|
||||
static Drawable makeModeHeader(@NonNull Context context, Drawable modeIcon) {
|
||||
return composeIcons(
|
||||
checkNotNull(context.getDrawable(R.drawable.ic_zen_mode_icon_cookie)),
|
||||
context.getColorStateList(R.color.modes_icon_selectable_background),
|
||||
context.getResources().getDimensionPixelSize(
|
||||
R.dimen.zen_mode_header_size),
|
||||
modeIcon,
|
||||
context.getColorStateList(R.color.modes_icon_selectable_icon),
|
||||
context.getResources().getDimensionPixelSize(
|
||||
R.dimen.zen_mode_header_inner_icon_size));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* The inner icon is 48x48dp and it's contained in a circle of diameter 90dp.
|
||||
*/
|
||||
static Drawable makeIconPickerHeader(@NonNull Context context, Drawable icon) {
|
||||
return composeIconCircle(
|
||||
@@ -82,16 +100,16 @@ class IconUtil {
|
||||
|
||||
/**
|
||||
* Returns a variant of the supplied {@code icon} to be used as an option in the icon picker.
|
||||
* The inner icon is 36x36dp and it's contained into a circle of diameter 54dp. It's also set up
|
||||
* The inner icon is 36x36dp and it's contained in a circle of diameter 54dp. It's also set up
|
||||
* so that selection and pressed states are represented in the color.
|
||||
*/
|
||||
static Drawable makeIconPickerItem(@NonNull Context context, @DrawableRes int iconResId) {
|
||||
return composeIconCircle(
|
||||
context.getColorStateList(R.color.modes_icon_picker_item_background),
|
||||
context.getColorStateList(R.color.modes_icon_selectable_background),
|
||||
context.getResources().getDimensionPixelSize(
|
||||
R.dimen.zen_mode_icon_list_item_circle_diameter),
|
||||
checkNotNull(context.getDrawable(iconResId)),
|
||||
context.getColorStateList(R.color.modes_icon_picker_item_icon),
|
||||
context.getColorStateList(R.color.modes_icon_selectable_icon),
|
||||
context.getResources().getDimensionPixelSize(
|
||||
R.dimen.zen_mode_icon_list_item_icon_size));
|
||||
}
|
||||
@@ -164,18 +182,24 @@ class IconUtil {
|
||||
|
||||
private static Drawable composeIconCircle(ColorStateList circleColor, @Px int circleDiameterPx,
|
||||
Drawable icon, ColorStateList iconColor, @Px int iconSizePx) {
|
||||
ShapeDrawable background = new ShapeDrawable(new OvalShape());
|
||||
background.setTintList(circleColor);
|
||||
return composeIcons(new ShapeDrawable(new OvalShape()), circleColor, circleDiameterPx, icon,
|
||||
iconColor, iconSizePx);
|
||||
}
|
||||
|
||||
private static Drawable composeIcons(Drawable outer, ColorStateList outerColor,
|
||||
@Px int outerSizePx, Drawable icon, ColorStateList iconColor, @Px int iconSizePx) {
|
||||
Drawable background = checkNotNull(outer.getConstantState()).newDrawable().mutate();
|
||||
background.setTintList(outerColor);
|
||||
Drawable foreground = checkNotNull(icon.getConstantState()).newDrawable().mutate();
|
||||
foreground.setTintList(iconColor);
|
||||
|
||||
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[] { background, foreground });
|
||||
|
||||
layerDrawable.setLayerSize(0, circleDiameterPx, circleDiameterPx);
|
||||
layerDrawable.setLayerSize(0, outerSizePx, outerSizePx);
|
||||
layerDrawable.setLayerGravity(1, Gravity.CENTER);
|
||||
layerDrawable.setLayerSize(1, iconSizePx, iconSizePx);
|
||||
|
||||
layerDrawable.setBounds(0, 0, circleDiameterPx, circleDiameterPx);
|
||||
layerDrawable.setBounds(0, 0, outerSizePx, outerSizePx);
|
||||
return layerDrawable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.widget.TopIntroPreference;
|
||||
|
||||
public class ZenModeBlurbPreference extends TopIntroPreference {
|
||||
|
||||
public ZenModeBlurbPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ZenModeBlurbPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
if (holder.findViewById(android.R.id.title) instanceof TextView textView) {
|
||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
|
||||
getContext().getResources().getDimensionPixelSize(
|
||||
R.dimen.zen_mode_blurb_text_size));
|
||||
|
||||
textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
|
||||
|
||||
ViewGroup.LayoutParams layoutParams = textView.getLayoutParams();
|
||||
if (layoutParams.width != MATCH_PARENT) {
|
||||
layoutParams.width = MATCH_PARENT;
|
||||
textView.setLayoutParams(layoutParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 android.app.AutomaticZenRule.TYPE_BEDTIME;
|
||||
import static android.app.AutomaticZenRule.TYPE_DRIVING;
|
||||
import static android.app.AutomaticZenRule.TYPE_IMMERSIVE;
|
||||
import static android.app.AutomaticZenRule.TYPE_MANAGED;
|
||||
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
|
||||
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
|
||||
import static android.app.AutomaticZenRule.TYPE_THEATER;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.notification.modes.ZenMode;
|
||||
|
||||
class ZenModeBlurbPreferenceController extends AbstractZenModePreferenceController {
|
||||
|
||||
ZenModeBlurbPreferenceController(@NonNull Context context, @NonNull String key) {
|
||||
super(context, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
preference.setTitle(getModeBlurb(zenMode));
|
||||
}
|
||||
|
||||
@StringRes
|
||||
@SuppressLint("SwitchIntDef")
|
||||
private static int getModeBlurb(ZenMode mode) {
|
||||
if (mode.isSystemOwned()) {
|
||||
return switch (mode.getType()) {
|
||||
case TYPE_SCHEDULE_TIME -> R.string.zen_mode_blurb_schedule_time;
|
||||
case TYPE_SCHEDULE_CALENDAR -> R.string.zen_mode_blurb_schedule_calendar;
|
||||
default -> R.string.zen_mode_blurb_generic; // Custom Manual
|
||||
};
|
||||
} else {
|
||||
return switch (mode.getType()) {
|
||||
case TYPE_BEDTIME -> R.string.zen_mode_blurb_bedtime;
|
||||
case TYPE_DRIVING -> R.string.zen_mode_blurb_driving;
|
||||
case TYPE_IMMERSIVE -> R.string.zen_mode_blurb_immersive;
|
||||
case TYPE_THEATER -> R.string.zen_mode_blurb_theater;
|
||||
case TYPE_MANAGED -> R.string.zen_mode_blurb_managed;
|
||||
default -> R.string.zen_mode_blurb_generic; // Including OTHER, UNKNOWN.
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,7 @@ public class ZenModeFragment extends ZenModeFragmentBase {
|
||||
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
||||
List<AbstractPreferenceController> prefControllers = new ArrayList<>();
|
||||
prefControllers.add(new ZenModeHeaderController(context, "header", this));
|
||||
prefControllers.add(new ZenModeBlurbPreferenceController(context, "mode_blurb"));
|
||||
prefControllers.add(
|
||||
new ZenModeButtonPreferenceController(context, "activate", this, mBackend));
|
||||
prefControllers.add(new ZenModePreferenceCategoryController(context, "modes_filters"));
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package com.android.settings.notification.modes;
|
||||
|
||||
import android.app.Flags;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -23,48 +22,22 @@ 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.notification.modes.ZenIconLoader;
|
||||
import com.android.settingslib.notification.modes.ZenMode;
|
||||
import com.android.settingslib.widget.LayoutPreference;
|
||||
|
||||
class ZenModeHeaderController extends AbstractZenModePreferenceController {
|
||||
|
||||
private final DashboardFragment mFragment;
|
||||
private EntityHeaderController mHeaderController;
|
||||
class ZenModeHeaderController extends AbstractZenModeHeaderController {
|
||||
|
||||
ZenModeHeaderController(
|
||||
@NonNull Context context,
|
||||
@NonNull String key,
|
||||
@NonNull DashboardFragment fragment) {
|
||||
super(context, key);
|
||||
mFragment = fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return Flags.modesApi();
|
||||
super(context, key, fragment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
||||
if (mFragment == null) {
|
||||
return;
|
||||
}
|
||||
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, ZenIconLoader.getInstance()),
|
||||
icon -> mHeaderController.setIcon(IconUtil.applyNormalTint(mContext, icon))
|
||||
.done(/* rebindActions= */ false),
|
||||
mContext.getMainExecutor());
|
||||
updateIcon(preference, zenMode,
|
||||
mContext.getResources().getDimensionPixelSize(R.dimen.zen_mode_header_size),
|
||||
icon -> IconUtil.makeModeHeader(mContext, icon),
|
||||
iconView -> iconView.setSelected(zenMode.isActive()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,55 +17,28 @@
|
||||
package com.android.settings.notification.modes;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
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.notification.modes.ZenIconLoader;
|
||||
import com.android.settingslib.notification.modes.ZenMode;
|
||||
import com.android.settingslib.widget.LayoutPreference;
|
||||
|
||||
/** Controller used for displaying the currently-chosen icon at the top of the icon picker. */
|
||||
class ZenModeIconPickerIconPreferenceController extends AbstractZenModePreferenceController {
|
||||
|
||||
private final DashboardFragment mFragment;
|
||||
private EntityHeaderController mHeaderController;
|
||||
class ZenModeIconPickerIconPreferenceController extends AbstractZenModeHeaderController {
|
||||
|
||||
ZenModeIconPickerIconPreferenceController(@NonNull Context context, @NonNull String key,
|
||||
@NonNull DashboardFragment fragment) {
|
||||
super(context, key);
|
||||
mFragment = fragment;
|
||||
super(context, key, 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));
|
||||
|
||||
ImageView iconView = pref.findViewById(R.id.entity_header_icon);
|
||||
ViewGroup.LayoutParams layoutParams = iconView.getLayoutParams();
|
||||
int imageSizePx = iconView.getContext().getResources().getDimensionPixelSize(
|
||||
R.dimen.zen_mode_icon_list_header_circle_diameter);
|
||||
layoutParams.width = imageSizePx;
|
||||
layoutParams.height = imageSizePx;
|
||||
iconView.setLayoutParams(layoutParams);
|
||||
}
|
||||
|
||||
FutureUtil.whenDone(
|
||||
zenMode.getIcon(mContext, ZenIconLoader.getInstance()),
|
||||
icon -> mHeaderController.setIcon(IconUtil.makeIconPickerHeader(mContext, icon))
|
||||
.done(/* rebindActions= */ false),
|
||||
mContext.getMainExecutor());
|
||||
updateIcon(preference, zenMode,
|
||||
mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.zen_mode_icon_list_header_circle_diameter),
|
||||
icon -> IconUtil.makeIconPickerHeader(mContext, icon),
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user