From 166392ff24aab5f170b0688d1fa981995ea9c1b0 Mon Sep 17 00:00:00 2001 From: Aaron Heuckroth Date: Thu, 17 Jan 2019 16:50:59 -0500 Subject: [PATCH] Creates a new grid-based global actions menu on power button hold. Test: Automated tests should pass. Menu should render at correct size, shape, and position for 0-9 action items. Bug: 121385065 Change-Id: I3448e7e333ea8fdaeb09a241249ce7521920169e --- core/java/android/util/FeatureFlagUtils.java | 2 + .../res/layout/global_actions_grid.xml | 83 +++++++++++++ .../res/layout/global_actions_grid_item.xml | 61 ++++++++++ packages/SystemUI/res/values/dimens.xml | 12 ++ .../com/android/systemui/MultiListLayout.java | 8 +- .../globalactions/GlobalActionsDialog.java | 19 ++- .../GlobalActionsGridLayout.java | 101 ++++++++++++++++ .../globalactions/ListGridLayout.java | 111 ++++++++++++++++++ 8 files changed, 391 insertions(+), 6 deletions(-) create mode 100644 packages/SystemUI/res/layout/global_actions_grid.xml create mode 100644 packages/SystemUI/res/layout/global_actions_grid_item.xml create mode 100644 packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java create mode 100644 packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 0edcb3d8eb6ae..3b7b1181ea8fd 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -40,6 +40,7 @@ public class FeatureFlagUtils { public static final String SAFETY_HUB = "settings_safety_hub"; public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press"; public static final String AOD_IMAGEWALLPAPER_ENABLED = "settings_aod_imagewallpaper_enabled"; + public static final String GLOBAL_ACTIONS_GRID_ENABLED = "settings_global_actions_grid_enabled"; private static final Map DEFAULT_FLAGS; private static final Set OBSERVABLE_FLAGS; @@ -60,6 +61,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SAFETY_HUB, "false"); DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false"); DEFAULT_FLAGS.put(AOD_IMAGEWALLPAPER_ENABLED, "false"); + DEFAULT_FLAGS.put(GLOBAL_ACTIONS_GRID_ENABLED, "false"); OBSERVABLE_FLAGS = new HashSet<>(); OBSERVABLE_FLAGS.add(AOD_IMAGEWALLPAPER_ENABLED); diff --git a/packages/SystemUI/res/layout/global_actions_grid.xml b/packages/SystemUI/res/layout/global_actions_grid.xml new file mode 100644 index 0000000000000..e6f2376ae76b8 --- /dev/null +++ b/packages/SystemUI/res/layout/global_actions_grid.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/SystemUI/res/layout/global_actions_grid_item.xml b/packages/SystemUI/res/layout/global_actions_grid_item.xml new file mode 100644 index 0000000000000..0c11cd9772567 --- /dev/null +++ b/packages/SystemUI/res/layout/global_actions_grid_item.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index ab0bbe10c37c3..4b73917e0c5b7 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -834,6 +834,18 @@ 120dp + 16dp + + 4dp + 104dp + 8dp + 8dp + 4dp + 4dp + + 12dp + 8dp + 120dp 12dp diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java index 0c7a9a9fffd26..85265f4583705 100644 --- a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java +++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java @@ -26,11 +26,11 @@ import android.widget.LinearLayout; * Layout class representing the Global Actions menu which appears when the power button is held. */ public abstract class MultiListLayout extends LinearLayout { - boolean mHasOutsideTouch; - boolean mHasSeparatedView; + protected boolean mHasOutsideTouch; + protected boolean mHasSeparatedView; - int mExpectedSeparatedItemCount; - int mExpectedListItemCount; + protected int mExpectedSeparatedItemCount; + protected int mExpectedListItemCount; public MultiListLayout(Context context, AttributeSet attrs) { super(context, attrs); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 7b18fad0e1057..f5ac0d39d61f6 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -1090,9 +1090,16 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, } } + protected int getActionLayoutId(Context context) { + if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED)) { + return com.android.systemui.R.layout.global_actions_grid_item; + } + return com.android.systemui.R.layout.global_actions_item; + } + public View create( Context context, View convertView, ViewGroup parent, LayoutInflater inflater) { - View v = inflater.inflate(com.android.systemui.R.layout.global_actions_item, parent, + View v = inflater.inflate(getActionLayoutId(context), parent, false); ImageView icon = (ImageView) v.findViewById(R.id.icon); @@ -1498,7 +1505,8 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, window.setBackgroundDrawable(mGradientDrawable); window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); - setContentView(com.android.systemui.R.layout.global_actions_wrapped); + + setContentView(getGlobalActionsLayoutId(context)); mGlobalActionsLayout = (MultiListLayout) findViewById(com.android.systemui.R.id.global_actions_view); mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss()); @@ -1515,6 +1523,13 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, setTitle(R.string.global_actions); } + private int getGlobalActionsLayoutId(Context context) { + if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED)) { + return com.android.systemui.R.layout.global_actions_grid; + } + return com.android.systemui.R.layout.global_actions_wrapped; + } + private void updateList() { mGlobalActionsLayout.removeAllItems(); ArrayList separatedActions = diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java new file mode 100644 index 0000000000000..0e49b5f3cd2a0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2019 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.systemui.globalactions; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import com.android.systemui.HardwareBgDrawable; +import com.android.systemui.MultiListLayout; + +/** + * Grid-based implementation of the button layout created by the global actions dialog. + */ +public class GlobalActionsGridLayout extends MultiListLayout { + + boolean mBackgroundsSet; + + public GlobalActionsGridLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + private void setBackgrounds() { + HardwareBgDrawable listBackground = new HardwareBgDrawable(true, true, getContext()); + HardwareBgDrawable separatedViewBackground = new HardwareBgDrawable(true, true, + getContext()); + getListView().setBackground(listBackground); + getSeparatedView().setBackground(separatedViewBackground); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + // backgrounds set only once, the first time onMeasure is called after inflation + if (getListView() != null && !mBackgroundsSet) { + setBackgrounds(); + mBackgroundsSet = true; + } + } + + @Override + public void setExpectedListItemCount(int count) { + mExpectedListItemCount = count; + getListView().setExpectedCount(count); + } + + @Override + protected ViewGroup getSeparatedView() { + return findViewById(com.android.systemui.R.id.separated_button); + } + + @Override + protected ListGridLayout getListView() { + return findViewById(android.R.id.list); + } + + @Override + public void removeAllItems() { + ViewGroup separatedList = getSeparatedView(); + ListGridLayout list = getListView(); + if (separatedList != null) { + separatedList.removeAllViews(); + } + if (list != null) { + list.removeAllItems(); + } + } + + @Override + public ViewGroup getParentView(boolean separated, int index) { + if (separated) { + return getSeparatedView(); + } else { + return getListView().getParentView(index); + } + } + + /** + * Not used in this implementation of the Global Actions Menu, but necessary for some others. + */ + @Override + public void setDivisionView(View v) { + + } +} diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java new file mode 100644 index 0000000000000..37755155751f9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2019 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.systemui.globalactions; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +/** + * Layout which uses nested LinearLayouts to create a grid with the following behavior: + * + * * Try to maintain a 'square' grid (equal number of columns and rows) based on the expected item + * count. + * * Display and hide sub-lists as needed, depending on the expected item count. + * * Favor bias toward having more rows or columns depending on the orientation of the device + * (TODO(123344999): Implement this, currently always favors adding more rows.) + * * Change the orientation (horizontal vs. vertical) of the container and sub-lists to act as rows + * or columns depending on the orientation of the device. + * (TODO(123344999): Implement this, currently always columns.) + * + * While we could implement this behavior with a GridLayout, it would take significantly more + * time and effort, and would require more substantial refactoring of the existing code in + * GlobalActionsDialog, since it would require manipulation of the child items themselves. + * + */ + +public class ListGridLayout extends LinearLayout { + private int mExpectedCount; + private int mRows; + private int mColumns; + + public ListGridLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + /** + * Remove all items from this grid. + */ + public void removeAllItems() { + for (int i = 0; i < getChildCount(); i++) { + ViewGroup subList = (ViewGroup) getChildAt(i); + if (subList != null) { + subList.removeAllViews(); + } + } + } + + /** + * Get the parent view associated with the item which should be placed at the given position. + */ + public ViewGroup getParentView(int index) { + ViewGroup firstParent = (ViewGroup) getChildAt(0); + if (mRows == 0) { + return firstParent; + } + int column = (int) Math.floor(index / mRows); + ViewGroup parent = (ViewGroup) getChildAt(column); + return parent != null ? parent : firstParent; + } + + /** + * Sets the expected number of items that this grid will be responsible for rendering. + */ + public void setExpectedCount(int count) { + mExpectedCount = count; + mRows = getRowCount(); + mColumns = getColumnCount(); + + for (int i = 0; i < getChildCount(); i++) { + if (i <= mColumns) { + setSublistVisibility(i, true); + } else { + setSublistVisibility(i, false); + } + } + + } + + private void setSublistVisibility(int index, boolean visible) { + View subList = getChildAt(index); + Log.d("ListGrid", "index: " + index + ", visibility: " + visible); + if (subList != null) { + subList.setVisibility(visible ? View.VISIBLE : View.GONE); + } + } + + private int getRowCount() { + return (int) Math.ceil(Math.sqrt(mExpectedCount)); + } + + private int getColumnCount() { + return (int) Math.round(Math.sqrt(mExpectedCount)); + } +}