Merge "Creates a new grid-based global actions menu on power button hold."

This commit is contained in:
Aaron Heuckroth
2019-01-25 15:19:07 +00:00
committed by Android (Google) Code Review
8 changed files with 391 additions and 6 deletions

View File

@@ -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<String, String> DEFAULT_FLAGS;
private static final Set<String> 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);

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<com.android.systemui.globalactions.GlobalActionsGridLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@id/global_actions_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:clipToPadding="false"
android:theme="@style/qs_theme"
android:gravity="bottom|center"
android:clipChildren="false"
>
<LinearLayout
android:layout_height="290dp"
android:layout_width="412dp"
android:gravity="bottom"
android:padding="0dp"
android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin"
>
<!-- For separated items-->
<LinearLayout
android:id="@+id/separated_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
android:layout_marginRight="@dimen/global_actions_grid_side_margin"
android:paddingTop="@dimen/global_actions_grid_top_padding"
android:paddingLeft="@dimen/global_actions_grid_left_padding"
android:paddingBottom="@dimen/global_actions_grid_bottom_padding"
android:paddingRight="@dimen/global_actions_grid_right_padding"
android:orientation="vertical"
android:background="?android:attr/colorBackgroundFloating"
android:translationZ="@dimen/global_actions_translate"
/>
<Space android:layout_width="match_parent" android:layout_height="2dp"
android:layout_weight="1" />
<!-- Grid of action items -->
<com.android.systemui.globalactions.ListGridLayout
android:id="@android:id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
android:orientation="horizontal"
android:layoutDirection="rtl"
android:layout_marginRight="@dimen/global_actions_grid_side_margin"
android:translationZ="@dimen/global_actions_translate"
android:paddingLeft="@dimen/global_actions_grid_left_padding"
android:paddingRight="@dimen/global_actions_grid_right_padding"
android:paddingTop="@dimen/global_actions_grid_top_padding"
android:paddingBottom="@dimen/global_actions_grid_bottom_padding"
android:background="?android:attr/colorBackgroundFloating"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:visibility="gone"
android:gravity="bottom"
android:orientation="vertical"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:visibility="gone"
android:gravity="bottom"
android:orientation="vertical"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:visibility="gone"
android:gravity="bottom"
android:orientation="vertical"
/>
</com.android.systemui.globalactions.ListGridLayout>
</LinearLayout>
</com.android.systemui.globalactions.GlobalActionsGridLayout>

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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.
-->
<!-- RelativeLayouts have an issue enforcing minimum heights, so just
work around this for now with LinearLayouts. -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="72dp"
android:layout_height="72dp"
android:gravity="center"
android:orientation="vertical"
android:layout_marginTop="@dimen/global_actions_grid_item_vertical_margin"
android:layout_marginBottom="@dimen/global_actions_grid_item_vertical_margin"
android:layout_marginLeft="@dimen/global_actions_grid_item_side_margin"
android:layout_marginRight="@dimen/global_actions_grid_item_side_margin"
android:paddingEnd="4dip"
android:paddingStart="4dip">
<ImageView
android:id="@*android:id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:scaleType="center"
android:alpha="?android:attr/primaryContentAlpha"
/>
<TextView
android:id="@*android:id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:paddingTop="10dp"
android:gravity="center"
android:textSize="12sp"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@*android:id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:gravity="center"
android:textColor="?android:attr/textColorTertiary"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
</LinearLayout>

View File

@@ -834,6 +834,18 @@
<dimen name="global_actions_panel_width">120dp</dimen>
<dimen name="global_actions_grid_container_bottom_margin">16dp</dimen>
<dimen name="global_actions_grid_side_margin">4dp</dimen>
<dimen name="global_actions_grid_separated_panel_width">104dp</dimen>
<dimen name="global_actions_grid_top_padding">8dp</dimen>
<dimen name="global_actions_grid_bottom_padding">8dp</dimen>
<dimen name="global_actions_grid_left_padding">4dp</dimen>
<dimen name="global_actions_grid_right_padding">4dp</dimen>
<dimen name="global_actions_grid_item_side_margin">12dp</dimen>
<dimen name="global_actions_grid_item_vertical_margin">8dp</dimen>
<dimen name="global_actions_top_padding">120dp</dimen>
<dimen name="global_actions_padding">12dp</dimen>

View File

@@ -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);

View File

@@ -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<Action> separatedActions =

View File

@@ -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) {
}
}

View File

@@ -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));
}
}