Add overflow menu to GlobalActionsDialog.

Test: atest GlobalActionsDialogTest
Test: Power menu items beyond the 3rd will appear in a dropdown menu instead of being truncated when controls are available. If controls are disabled (ex. 'adb shell settings put secure systemui.controls_available 0'), all items should still display in older versions of GlobalActions. Items in power overflow menu are both short- and long-pressable.

Fixes: 152625023

Change-Id: Icdbf8eb7e79a61d490d484f207eeedc47c4882c5
This commit is contained in:
Aran Ink
2020-03-31 17:48:37 -04:00
parent 01f473ab78
commit cf03827a81
6 changed files with 400 additions and 81 deletions

View File

@@ -12,34 +12,46 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:theme="@style/qs_theme"
android:gravity="top"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_marginTop="@dimen/global_actions_top_margin"
android:layout_marginLeft="@dimen/global_actions_side_margin"
android:layout_marginRight="@dimen/global_actions_side_margin"
>
<LinearLayout
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/global_actions_side_margin"
android:layout_marginRight="@dimen/global_actions_side_margin"
android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
android:paddingTop="@dimen/global_actions_grid_vertical_padding"
android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
android:orientation="horizontal"
android:gravity="left"
android:gravity="left | center_vertical"
android:translationZ="@dimen/global_actions_translate"
/>
>
<RelativeLayout
android:id="@+id/global_actions_overflow_button"
android:layout_width="48dp"
android:layout_height="48dp"
>
<ImageView
android:src="@drawable/ic_more_vert"
android:layout_centerInParent="true"
android:layout_width="24dp"
android:layout_height="24dp"
android:tint="@color/control_more_vert"
/>
</RelativeLayout>
</LinearLayout>
</com.android.systemui.globalactions.GlobalActionsFlatLayout>
<com.android.systemui.globalactions.MinHeightScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset"
android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset"
android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset"
android:orientation="vertical"
>
<LinearLayout
android:id="@+id/global_actions_grid_root"

View File

@@ -34,4 +34,7 @@
<!-- Max number of columns for quick controls area -->
<integer name="controls_max_columns">4</integer>
<!-- Max number of columns for power menu -->
<integer name="power_menu_max_columns">4</integer>
</resources>

View File

@@ -536,6 +536,10 @@
<!-- Max number of columns for quick controls area -->
<integer name="controls_max_columns">2</integer>
<!-- Max number of columns for power menu -->
<integer name="power_menu_max_columns">3</integer>
<!-- If the dp width of the available space is <= this value, potentially adjust the number
of columns-->
<integer name="controls_max_columns_adjust_below_width_dp">320</integer>

View File

@@ -75,9 +75,12 @@ import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.ListPopupWindow;
import android.widget.ListView;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -151,19 +154,20 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
/* Valid settings for global actions keys.
* see config.xml config_globalActionList */
private static final String GLOBAL_ACTION_KEY_POWER = "power";
private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
private static final String GLOBAL_ACTION_KEY_USERS = "users";
private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
private static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
private static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
@VisibleForTesting
protected static final String GLOBAL_ACTION_KEY_POWER = "power";
protected static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
protected static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
protected static final String GLOBAL_ACTION_KEY_SILENT = "silent";
protected static final String GLOBAL_ACTION_KEY_USERS = "users";
protected static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
protected static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
protected static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
protected static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
protected static final String GLOBAL_ACTION_KEY_RESTART = "restart";
protected static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
protected static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
protected static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
private static final String PREFS_CONTROLS_SEEDING_COMPLETED = "ControlsSeedingCompleted";
private static final String PREFS_CONTROLS_FILE = "controls_prefs";
@@ -191,13 +195,18 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
// Used for RingerModeTracker
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
private ArrayList<Action> mItems;
@VisibleForTesting
protected ArrayList<Action> mItems;
@VisibleForTesting
protected ArrayList<Action> mOverflowItems;
private ActionsDialog mDialog;
private Action mSilentModeAction;
private ToggleAction mAirplaneModeOn;
private MyAdapter mAdapter;
private MyOverflowAdapter mOverflowAdapter;
private boolean mKeyguardShowing = false;
private boolean mDeviceProvisioned = false;
@@ -459,12 +468,51 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
@VisibleForTesting
protected boolean shouldShowAction(Action action) {
if (mKeyguardShowing && !action.showDuringKeyguard()) {
return false;
}
if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
return false;
}
return true;
}
/**
* Create the global actions dialog.
*
* @return A new dialog.
* Returns the maximum number of power menu items to show based on which GlobalActions
* layout is being used.
*/
private ActionsDialog createDialog() {
@VisibleForTesting
protected int getMaxShownPowerItems() {
if (shouldShowControls()) {
return mResources.getInteger(com.android.systemui.R.integer.power_menu_max_columns);
} else {
return Integer.MAX_VALUE;
}
}
/**
* Add a power menu action item for to either the main or overflow items lists, depending on
* whether controls are enabled and whether the max number of shown items has been reached.
*/
private void addActionItem(Action action) {
if (mItems != null && shouldShowAction(action)) {
if (mItems.size() < getMaxShownPowerItems()) {
mItems.add(action);
} else if (mOverflowItems != null) {
mOverflowItems.add(action);
}
}
}
@VisibleForTesting
protected String[] getDefaultActions() {
return mResources.getStringArray(R.array.config_globalActionsList);
}
@VisibleForTesting
protected void createActionItems() {
// Simple toggle style if there's no vibrator, otherwise use a tri-state
if (!mHasVibrator) {
mSilentModeAction = new SilentModeToggleAction();
@@ -475,7 +523,13 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
onAirplaneModeChanged();
mItems = new ArrayList<Action>();
String[] defaultActions = mResources.getStringArray(R.array.config_globalActionsList);
mOverflowItems = new ArrayList<Action>();
String[] defaultActions = getDefaultActions();
// make sure emergency affordance action is first, if needed
if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
addActionItem(new EmergencyAffordanceAction());
}
ArraySet<String> addedKeys = new ArraySet<String>();
for (int i = 0; i < defaultActions.length; i++) {
@@ -485,46 +539,46 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
continue;
}
if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
mItems.add(new PowerAction());
addActionItem(new PowerAction());
} else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
mItems.add(mAirplaneModeOn);
addActionItem(mAirplaneModeOn);
} else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
if (Settings.Global.getInt(mContentResolver,
Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
mItems.add(new BugReportAction());
addActionItem(new BugReportAction());
}
} else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
if (mShowSilentToggle) {
mItems.add(mSilentModeAction);
addActionItem(mSilentModeAction);
}
} else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
addUsersToMenu(mItems);
addUsersToMenu();
}
} else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
mItems.add(getSettingsAction());
addActionItem(getSettingsAction());
} else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
if (Settings.Secure.getIntForUser(mContentResolver,
Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, getCurrentUser().id) != 0
&& shouldDisplayLockdown()) {
mItems.add(getLockdownAction());
addActionItem(getLockdownAction());
}
} else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
mItems.add(getVoiceAssistAction());
addActionItem(getVoiceAssistAction());
} else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
mItems.add(getAssistAction());
addActionItem(getAssistAction());
} else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
mItems.add(new RestartAction());
addActionItem(new RestartAction());
} else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
mItems.add(new ScreenshotAction());
addActionItem(new ScreenshotAction());
} else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
if (mDevicePolicyManager.isLogoutEnabled()
&& getCurrentUser().id != UserHandle.USER_SYSTEM) {
mItems.add(new LogoutAction());
addActionItem(new LogoutAction());
}
} else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey)) {
if (!mEmergencyAffordanceManager.needsEmergencyAffordance()) {
mItems.add(new EmergencyDialerAction());
addActionItem(new EmergencyDialerAction());
}
} else {
Log.e(TAG, "Invalid global action key " + actionKey);
@@ -532,17 +586,23 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
// Add here so we don't add more than one.
addedKeys.add(actionKey);
}
}
if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
mItems.add(new EmergencyAffordanceAction());
}
/**
* Create the global actions dialog.
*
* @return A new dialog.
*/
private ActionsDialog createDialog() {
createActionItems();
mAdapter = new MyAdapter();
mOverflowAdapter = new MyOverflowAdapter();
mDepthController.setShowingHomeControls(shouldShowControls());
ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, getWalletPanelViewController(),
mDepthController, mSysuiColorExtractor, mStatusBarService,
mNotificationShadeWindowController,
ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, mOverflowAdapter,
getWalletPanelViewController(), mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
shouldShowControls() ? mControlsUiController : null, mBlurUtils);
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
dialog.setKeyguardShowing(mKeyguardShowing);
@@ -1020,7 +1080,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return currentUser == null || currentUser.isPrimary();
}
private void addUsersToMenu(ArrayList<Action> items) {
private void addUsersToMenu() {
if (mUserManager.isUserSwitcherEnabled()) {
List<UserInfo> users = mUserManager.getUsers();
UserInfo currentUser = getCurrentUser();
@@ -1050,7 +1110,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return false;
}
};
items.add(switchToUser);
addActionItem(switchToUser);
}
}
}
@@ -1099,11 +1159,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
/**
* The adapter used for the list within the global actions dialog, taking into account whether
* the keyguard is showing via
* {@link com.android.systemui.globalactions.GlobalActionsDialog#mKeyguardShowing}
* and whether the device is provisioned via
* {@link com.android.systemui.globalactions.GlobalActionsDialog#mDeviceProvisioned}.
* The adapter used for power menu items shown in the global actions dialog.
*/
public class MyAdapter extends MultiListAdapter {
private int countItems(boolean separated) {
@@ -1111,23 +1167,13 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
for (int i = 0; i < mItems.size(); i++) {
final Action action = mItems.get(i);
if (shouldBeShown(action) && action.shouldBeSeparated() == separated) {
if (action.shouldBeSeparated() == separated) {
count++;
}
}
return count;
}
private boolean shouldBeShown(Action action) {
if (mKeyguardShowing && !action.showDuringKeyguard()) {
return false;
}
if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
return false;
}
return true;
}
@Override
public int countSeparatedItems() {
return countItems(true);
@@ -1158,7 +1204,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
int filteredPos = 0;
for (int i = 0; i < mItems.size(); i++) {
final Action action = mItems.get(i);
if (!shouldBeShown(action)) {
if (!shouldShowAction(action)) {
continue;
}
if (filteredPos == position) {
@@ -1223,6 +1269,79 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
/**
* The adapter used for items in the overflow menu.
*/
public class MyOverflowAdapter extends BaseAdapter {
@Override
public int getCount() {
return mOverflowItems != null ? mOverflowItems.size() : 0;
}
@Override
public Action getItem(int position) {
return mOverflowItems != null ? mOverflowItems.get(position) : null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Action action = getItem(position);
if (action == null) {
Log.w(TAG, "No overflow action found at position: " + position);
return null;
}
int viewLayoutResource = com.android.systemui.R.layout.controls_more_item;
View view = convertView != null ? convertView
: LayoutInflater.from(mContext).inflate(viewLayoutResource, parent, false);
TextView textView = (TextView) view;
textView.setOnClickListener(v -> onClickItem(position));
if (action.getMessageResId() != 0) {
textView.setText(action.getMessageResId());
} else {
textView.setText(action.getMessage());
}
if (action instanceof LongPressAction) {
textView.setOnLongClickListener(v -> onLongClickItem(position));
} else {
textView.setOnLongClickListener(null);
}
return textView;
}
private boolean onLongClickItem(int position) {
final Action action = getItem(position);
if (action instanceof LongPressAction) {
if (mDialog != null) {
mDialog.hidePowerOverflowMenu();
mDialog.dismiss();
} else {
Log.w(TAG, "Action long-clicked while mDialog is null.");
}
return ((LongPressAction) action).onLongPress();
}
return false;
}
private void onClickItem(int position) {
Action item = getItem(position);
if (!(item instanceof SilentModeTriStateAction)) {
if (mDialog != null) {
mDialog.hidePowerOverflowMenu();
mDialog.dismiss();
} else {
Log.w(TAG, "Action clicked while mDialog is null.");
}
item.onPress();
}
}
}
// note: the scheme below made more sense when we were planning on having
// 8 different things in the global actions dialog. seems overkill with
// only 3 items now, but may as well keep this flexible approach so it will
@@ -1248,8 +1367,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
boolean showDuringKeyguard();
/**
* @return whether this action should appear in the dialog before the device is
* provisioned.onlongpress
* @return whether this action should appear in the dialog before the
* device is provisioned.f
*/
boolean showBeforeProvisioning();
@@ -1258,6 +1377,18 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
default boolean shouldBeSeparated() {
return false;
}
/**
* Return the id of the message associated with this action, or 0 if it doesn't have one.
* @return
*/
int getMessageResId();
/**
* Return the message associated with this action, or null if it doesn't have one.
* @return
*/
CharSequence getMessage();
}
/**
@@ -1309,6 +1440,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
public int getMessageResId() {
return mMessageResId;
}
public CharSequence getMessage() {
return mMessage;
}
public View create(
Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
View v = inflater.inflate(getActionLayoutId(), parent, false /* attach */);
@@ -1396,6 +1536,23 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return context.getString(mMessageResId);
}
private boolean isOn() {
return mState == ToggleState.On || mState == ToggleState.TurningOn;
}
@Override
public CharSequence getMessage() {
return null;
}
@Override
public int getMessageResId() {
return isOn() ? mEnabledStatusMessageResId : mDisabledStatusMessageResId;
}
private int getIconResId() {
return isOn() ? mEnabledIconResId : mDisabledIconResid;
}
public View create(Context context, View convertView, ViewGroup parent,
LayoutInflater inflater) {
willCreate();
@@ -1405,17 +1562,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
ImageView icon = (ImageView) v.findViewById(R.id.icon);
TextView messageView = (TextView) v.findViewById(R.id.message);
final boolean enabled = isEnabled();
boolean on = ((mState == ToggleState.On) || (mState == ToggleState.TurningOn));
if (messageView != null) {
messageView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
messageView.setText(getMessageResId());
messageView.setEnabled(enabled);
messageView.setSelected(true); // necessary for marquee to work
}
if (icon != null) {
icon.setImageDrawable(context.getDrawable(
(on ? mEnabledIconResId : mDisabledIconResid)));
icon.setImageDrawable(context.getDrawable(getIconResId()));
icon.setEnabled(enabled);
}
@@ -1553,6 +1708,16 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return null;
}
@Override
public int getMessageResId() {
return 0;
}
@Override
public CharSequence getMessage() {
return null;
}
public View create(Context context, View convertView, ViewGroup parent,
LayoutInflater inflater) {
View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
@@ -1708,6 +1873,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final Context mContext;
private final MyAdapter mAdapter;
private final MyOverflowAdapter mOverflowAdapter;
private final IStatusBarService mStatusBarService;
private final IBinder mToken = new Binder();
private MultiListLayout mGlobalActionsLayout;
@@ -1722,11 +1888,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final NotificationShadeDepthController mDepthController;
private final BlurUtils mBlurUtils;
private ListPopupWindow mOverflowPopup;
private ControlsUiController mControlsUiController;
private ViewGroup mControlsView;
ActionsDialog(Context context, MyAdapter adapter,
ActionsDialog(Context context, MyAdapter adapter, MyOverflowAdapter overflowAdapter,
GlobalActionsPanelPlugin.PanelViewController plugin,
NotificationShadeDepthController depthController,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
@@ -1735,6 +1902,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
mContext = context;
mAdapter = adapter;
mOverflowAdapter = overflowAdapter;
mDepthController = depthController;
mColorExtractor = sysuiColorExtractor;
mStatusBarService = statusBarService;
@@ -1817,6 +1985,45 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
private ListPopupWindow createPowerOverflowPopup() {
ListPopupWindow popup = new ListPopupWindow(new ContextThemeWrapper(
mContext, com.android.systemui.R.style.Control_ListPopupWindow));
popup.setWindowLayoutType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
View overflowButton =
findViewById(com.android.systemui.R.id.global_actions_overflow_button);
popup.setAnchorView(overflowButton);
int parentWidth = mGlobalActionsLayout.getWidth();
// arbitrarily set the menu width to half of parent
// TODO: Logic for menu sizing based on contents.
int halfParentWidth = Math.round(parentWidth * 0.5f);
popup.setContentWidth(halfParentWidth);
popup.setAdapter(mOverflowAdapter);
popup.setModal(true);
return popup;
}
private void showPowerOverflowMenu() {
mOverflowPopup.show();
// Width is fixed to slightly more than half of the GlobalActionsLayout container.
// TODO: Resize the width of this dialog based on the sizes of the items in it.
int width = Math.round(mGlobalActionsLayout.getWidth() * 0.6f);
ListView listView = mOverflowPopup.getListView();
listView.setDividerHeight(mContext.getResources()
.getDimensionPixelSize(com.android.systemui.R.dimen.control_list_divider));
listView.setDivider(mContext.getResources().getDrawable(
com.android.systemui.R.drawable.controls_list_divider));
mOverflowPopup.setWidth(width);
mOverflowPopup.setHorizontalOffset(-width + mOverflowPopup.getAnchorView().getWidth());
mOverflowPopup.setVerticalOffset(mOverflowPopup.getAnchorView().getHeight());
mOverflowPopup.show();
}
private void hidePowerOverflowMenu() {
mOverflowPopup.dismiss();
}
private void initializeLayout() {
setContentView(getGlobalActionsLayoutId(mContext));
fixNavBarClipping();
@@ -1835,6 +2042,18 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mGlobalActionsLayout.setRotationListener(this::onRotate);
mGlobalActionsLayout.setAdapter(mAdapter);
mOverflowPopup = createPowerOverflowPopup();
View overflowButton = findViewById(
com.android.systemui.R.id.global_actions_overflow_button);
if (overflowButton != null) {
if (mOverflowAdapter.getCount() > 0) {
overflowButton.setOnClickListener((view) -> showPowerOverflowMenu());
} else {
overflowButton.setVisibility(View.GONE);
}
}
View globalActionsParent = (View) mGlobalActionsLayout.getParent();
globalActionsParent.setOnClickListener(v -> dismiss());
@@ -2090,9 +2309,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return isPanelDebugModeEnabled(context);
}
private boolean shouldShowControls() {
@VisibleForTesting
protected boolean shouldShowControls() {
return mKeyguardStateController.isUnlocked()
&& mControlsUiController.getAvailable()
&& !mControlsServiceInfos.isEmpty();
}
}
}

View File

@@ -32,7 +32,6 @@ import com.android.systemui.R;
* Flat, single-row implementation of the button layout created by the global actions dialog.
*/
public class GlobalActionsFlatLayout extends GlobalActionsLayout {
private static final int MAX_ITEMS = 4;
public GlobalActionsFlatLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -54,11 +53,28 @@ public class GlobalActionsFlatLayout extends GlobalActionsLayout {
return null;
}
private View getOverflowButton() {
return findViewById(com.android.systemui.R.id.global_actions_overflow_button);
}
@Override
protected void addToListView(View v, boolean reverse) {
// only add items to the list view if we haven't hit our max yet
if (getListView().getChildCount() < MAX_ITEMS) {
super.addToListView(v, reverse);
super.addToListView(v, reverse);
View overflowButton = getOverflowButton();
// if there's an overflow button, make sure it stays at the end
if (overflowButton != null) {
getListView().removeView(overflowButton);
super.addToListView(overflowButton, reverse);
}
}
@Override
protected void removeAllListViews() {
View overflowButton = getOverflowButton();
super.removeAllListViews();
// if there's an overflow button, add it back after clearing the list views
if (overflowButton != null) {
super.addToListView(overflowButton, false);
}
}

View File

@@ -16,6 +16,11 @@
package com.android.systemui.globalactions;
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -200,4 +205,63 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
verify(mUiEventLogger, times(1))
.log(event);
}
@Test
public void testCreateActionItems_maxThree() {
mGlobalActionsDialog = spy(mGlobalActionsDialog);
// allow 3 items to be shown
doReturn(3).when(mGlobalActionsDialog).getMaxShownPowerItems();
// ensure items are not blocked by keyguard or device provisioning
doReturn(true).when(mGlobalActionsDialog).shouldShowAction(any());
String[] actions = {
GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
GlobalActionsDialog.GLOBAL_ACTION_KEY_SCREENSHOT,
};
doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
mGlobalActionsDialog.createActionItems();
assertEquals(3, mGlobalActionsDialog.mItems.size());
assertEquals(1, mGlobalActionsDialog.mOverflowItems.size());
}
@Test
public void testCreateActionItems_maxAny() {
mGlobalActionsDialog = spy(mGlobalActionsDialog);
// allow any number of power menu items to be shown
doReturn(Integer.MAX_VALUE).when(mGlobalActionsDialog).getMaxShownPowerItems();
// ensure items are not blocked by keyguard or device provisioning
doReturn(true).when(mGlobalActionsDialog).shouldShowAction(any());
String[] actions = {
GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
GlobalActionsDialog.GLOBAL_ACTION_KEY_SCREENSHOT,
};
doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
mGlobalActionsDialog.createActionItems();
assertEquals(4, mGlobalActionsDialog.mItems.size());
assertEquals(0, mGlobalActionsDialog.mOverflowItems.size());
}
@Test
public void testCreateActionItems_maxThree_itemNotShown() {
mGlobalActionsDialog = spy(mGlobalActionsDialog);
// allow only 3 items to be shown
doReturn(3).when(mGlobalActionsDialog).getMaxShownPowerItems();
String[] actions = {
GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
// screenshot blocked because device not provisioned
GlobalActionsDialog.GLOBAL_ACTION_KEY_SCREENSHOT,
GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
};
doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
mGlobalActionsDialog.createActionItems();
assertEquals(3, mGlobalActionsDialog.mItems.size());
assertEquals(0, mGlobalActionsDialog.mOverflowItems.size());
}
}