Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f708d47e authored by Aaron Heuckroth's avatar Aaron Heuckroth
Browse files

Extract GlobalActionsLayout to an abstract class.

Test: Automated tests pass.
Change-Id: I5898723737e8c4355ff2de2c6535f6c81df9136f
parent 8235d512
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<com.android.systemui.HardwareUiLayout
    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:layout_gravity="top|right"
+3 −0
Original line number Diff line number Diff line
@@ -114,5 +114,8 @@
    <item type="id" name="aod_mask_transition_progress_tag" />
    <item type="id" name="aod_mask_transition_progress_end_tag" />
    <item type="id" name="aod_mask_transition_progress_start_tag" />

    <!-- Global Actions Menu -->
    <item type="id" name="global_actions_view" />
</resources>
+44 −33
Original line number Diff line number Diff line
@@ -37,23 +37,25 @@ import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.leak.RotationUtils;

public class HardwareUiLayout extends LinearLayout implements Tunable {
/**
 * Layout for placing two containers at a specific physical position on the device, relative to the
 * device's hardware, regardless of screen rotation.
 */
public class HardwareUiLayout extends MultiListLayout implements Tunable {

    private static final String EDGE_BLEED = "sysui_hwui_edge_bleed";
    private static final String ROUNDED_DIVIDER = "sysui_hwui_rounded_divider";
    private final int[] mTmp2 = new int[2];
    private View mList;
    private View mSeparatedView;
    private ViewGroup mList;
    private ViewGroup mSeparatedView;
    private int mOldHeight;
    private boolean mAnimating;
    private AnimatorSet mAnimation;
    private View mDivision;
    private boolean mHasOutsideTouch;
    private HardwareBgDrawable mListBackground;
    private HardwareBgDrawable mSeparatedViewBackground;
    private Animator mAnimator;
    private boolean mCollapse;
    private boolean mHasSeparatedButton;
    private int mEndPoint;
    private boolean mEdgeBleed;
    private boolean mRoundedDivider;
@@ -66,6 +68,35 @@ public class HardwareUiLayout extends LinearLayout implements Tunable {
        updateSettings();
    }

    @Override
    protected ViewGroup getSeparatedView() {
        return findViewById(com.android.systemui.R.id.separated_button);
    }

    @Override
    protected ViewGroup getListView() {
        return findViewById(android.R.id.list);
    }

    @Override
    public void removeAllItems() {
        if (mList != null) {
            mList.removeAllViews();
        }
        if (mSeparatedView != null) {
            mSeparatedView.removeAllViews();
        }
    }

    @Override
    public ViewGroup getParentView(boolean separated, int index) {
        if (separated) {
            return getSeparatedView();
        } else {
            return getListView();
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
@@ -137,9 +168,9 @@ public class HardwareUiLayout extends LinearLayout implements Tunable {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mList == null) {
            if (getChildCount() != 0) {
                mList = getChildAt(0);
                mList = getListView();
                mList.setBackground(mListBackground);
                mSeparatedView = getChildAt(1);
                mSeparatedView = getSeparatedView();
                mSeparatedView.setBackground(mSeparatedViewBackground);
                updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
                mOldHeight = mList.getMeasuredHeight();
@@ -187,7 +218,7 @@ public class HardwareUiLayout extends LinearLayout implements Tunable {
        } else {
            rotateLeft();
        }
        if (mHasSeparatedButton) {
        if (mHasSeparatedView) {
            if (from == ROTATION_SEASCAPE || to == ROTATION_SEASCAPE) {
                // Separated view has top margin, so seascape separated view need special rotation,
                // not a full left or right rotation.
@@ -408,8 +439,8 @@ public class HardwareUiLayout extends LinearLayout implements Tunable {
        if (mList == null) return;
        // If got separated button, setRotatedBackground to false,
        // all items won't get white background.
        mListBackground.setRotatedBackground(mHasSeparatedButton);
        mSeparatedViewBackground.setRotatedBackground(mHasSeparatedButton);
        mListBackground.setRotatedBackground(mHasSeparatedView);
        mSeparatedViewBackground.setRotatedBackground(mHasSeparatedView);
        if (mDivision != null && mDivision.getVisibility() == VISIBLE) {
            int index = mRotatedBackground ? 0 : 1;
            mDivision.getLocationOnScreen(mTmp2);
@@ -460,21 +491,21 @@ public class HardwareUiLayout extends LinearLayout implements Tunable {
            case RotationUtils.ROTATION_LANDSCAPE:
                defaultTopPadding = getPaddingLeft();
                viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth();
                separatedViewTopMargin = mHasSeparatedButton ? params.leftMargin : 0;
                separatedViewTopMargin = mHasSeparatedView ? params.leftMargin : 0;
                screenHeight = getMeasuredWidth();
                targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.TOP;
                break;
            case RotationUtils.ROTATION_SEASCAPE:
                defaultTopPadding = getPaddingRight();
                viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth();
                separatedViewTopMargin = mHasSeparatedButton ? params.leftMargin : 0;
                separatedViewTopMargin = mHasSeparatedView ? params.leftMargin : 0;
                screenHeight = getMeasuredWidth();
                targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM;
                break;
            default: // Portrait
                defaultTopPadding = getPaddingTop();
                viewsTotalHeight = mList.getMeasuredHeight() + mSeparatedView.getMeasuredHeight();
                separatedViewTopMargin = mHasSeparatedButton ? params.topMargin : 0;
                separatedViewTopMargin = mHasSeparatedView ? params.topMargin : 0;
                screenHeight = getMeasuredHeight();
                targetGravity = Gravity.CENTER_VERTICAL|Gravity.RIGHT;
                break;
@@ -491,30 +522,10 @@ public class HardwareUiLayout extends LinearLayout implements Tunable {
        return super.getOutlineProvider();
    }

    public void setOutsideTouchListener(OnClickListener onClickListener) {
        mHasOutsideTouch = true;
        requestLayout();
        setOnClickListener(onClickListener);
        setClickable(true);
        setFocusable(true);
    }

    public void setCollapse() {
        mCollapse = true;
    }

    public void setHasSeparatedButton(boolean hasSeparatedButton) {
        mHasSeparatedButton = hasSeparatedButton;
    }

    public static HardwareUiLayout get(View v) {
        if (v instanceof HardwareUiLayout) return (HardwareUiLayout) v;
        if (v.getParent() instanceof View) {
            return get((View) v.getParent());
        }
        return null;
    }

    private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> {
        if (mHasOutsideTouch || (mList == null)) {
            inoutInfo.setTouchableInsets(
+124 −0
Original line number Diff line number Diff line
/*
 * 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;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
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;

    int mExpectedSeparatedItemCount;
    int mExpectedListItemCount;

    public MultiListLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    protected abstract ViewGroup getSeparatedView();

    protected abstract ViewGroup getListView();

    /**
     * Removes all child items from the separated and list views, if they exist.
     */
    public abstract void removeAllItems();

    /**
     * Get the parent view which will be used to contain the item at the specified index.
     * @param separated Whether or not this index refers to a position in the separated or list
     *                  container.
     * @param index The index of the item within the container.
     * @return The parent ViewGroup which will be used to contain the specified item
     * after it has been added to the layout.
     */
    public abstract ViewGroup getParentView(boolean separated, int index);

    /**
     * Sets the divided view, which may have a differently-colored background.
     */
    public abstract void setDivisionView(View v);

    /**
     * Set the view accessibility delegate for the list view container.
     */
    public void setListViewAccessibilityDelegate(View.AccessibilityDelegate delegate) {
        getListView().setAccessibilityDelegate(delegate);
    }

    protected void setSeparatedViewVisibility(boolean visible) {
        getSeparatedView().setVisibility(visible ? View.VISIBLE : View.GONE);
    }

    /**
     * Sets the number of items expected to be rendered in the separated container. This allows the
     * layout to correctly determine which parent containers will be used for items before they have
     * beenadded to the layout.
     * @param count The number of items expected.
     */
    public void setExpectedSeparatedItemCount(int count) {
        mExpectedSeparatedItemCount = count;
    }

    /**
     * Sets the number of items expected to be rendered in the list container. This allows the
     * layout to correctly determine which parent containers will be used for items before they have
     * beenadded to the layout.
     * @param count The number of items expected.
     */
    public void setExpectedListItemCount(int count) {
        mExpectedListItemCount = count;
    }

    /**
     * Sets whether the separated view should be shown, and handles updating visibility on
     * that view.
     */
    public void setHasSeparatedView(boolean hasSeparatedView) {
        mHasSeparatedView = hasSeparatedView;
        setSeparatedViewVisibility(hasSeparatedView);
    }

    /**
     * Sets this layout to respond to an outside touch listener.
     */
    public void setOutsideTouchListener(OnClickListener onClickListener) {
        mHasOutsideTouch = true;
        requestLayout();
        setOnClickListener(onClickListener);
        setClickable(true);
        setFocusable(true);
    }

    /**
     * Retrieve the MultiListLayout associated with the given view.
     */
    public static MultiListLayout get(View v) {
        if (v instanceof MultiListLayout) return (MultiListLayout) v;
        if (v.getParent() instanceof View) {
            return get((View) v.getParent());
        }
        return null;
    }
}
+70 −29
Original line number Diff line number Diff line
@@ -67,10 +67,8 @@ import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.android.internal.R;
@@ -86,8 +84,8 @@ import com.android.internal.util.ScreenRecordHelper;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
import com.android.systemui.HardwareUiLayout;
import com.android.systemui.Interpolators;
import com.android.systemui.MultiListLayout;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -490,6 +488,11 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
        public boolean showBeforeProvisioning() {
            return true;
        }

        @Override
        public boolean shouldBeSeparated() {
            return true;
        }
    }

    private final class RestartAction extends SinglePressAction implements LongPressAction {
@@ -926,6 +929,34 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
            return getItem(position).isEnabled();
        }

        public ArrayList<Action> getSeparatedActions(boolean shouldUseSeparatedView) {
            ArrayList<Action> separatedActions = new ArrayList<Action>();
            if (!shouldUseSeparatedView) {
                return separatedActions;
            }
            for (int i = 0; i < mItems.size(); i++) {
                final Action action = mItems.get(i);
                if (action.shouldBeSeparated()) {
                    separatedActions.add(action);
                }
            }
            return separatedActions;
        }

        public ArrayList<Action> getListActions(boolean shouldUseSeparatedView) {
            if (!shouldUseSeparatedView) {
                return new ArrayList<Action>(mItems);
            }
            ArrayList<Action> listActions = new ArrayList<Action>();
            for (int i = 0; i < mItems.size(); i++) {
                final Action action = mItems.get(i);
                if (!action.shouldBeSeparated()) {
                    listActions.add(action);
                }
            }
            return listActions;
        }

        @Override
        public boolean areAllItemsEnabled() {
            return false;
@@ -965,7 +996,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
            View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
            // Everything but screenshot, the last item, gets white background.
            if (position == getCount() - 1) {
                HardwareUiLayout.get(parent).setDivisionView(view);
                MultiListLayout.get(parent).setDivisionView(view);
            }
            return view;
        }
@@ -1004,6 +1035,10 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
        boolean showBeforeProvisioning();

        boolean isEnabled();

        default boolean shouldBeSeparated() {
            return false;
        }
    }

    /**
@@ -1423,9 +1458,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,

        private final Context mContext;
        private final MyAdapter mAdapter;
        private final LinearLayout mListView;
        private final FrameLayout mSeparatedView;
        private final HardwareUiLayout mHardwareLayout;
        private final MultiListLayout mGlobalActionsLayout;
        private final OnClickListener mClickListener;
        private final OnItemLongClickListener mLongClickListener;
        private final GradientDrawable mGradientDrawable;
@@ -1466,16 +1499,11 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
            window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);

            setContentView(com.android.systemui.R.layout.global_actions_wrapped);
            mListView = findViewById(android.R.id.list);
            mSeparatedView = findViewById(com.android.systemui.R.id.separated_button);
            if (!mShouldDisplaySeparatedButton) {
                mSeparatedView.setVisibility(View.GONE);
            }
            mHardwareLayout = HardwareUiLayout.get(mListView);
            mHardwareLayout.setOutsideTouchListener(view -> dismiss());
            mHardwareLayout.setHasSeparatedButton(mShouldDisplaySeparatedButton);
            setTitle(R.string.global_actions);
            mListView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
            mGlobalActionsLayout = (MultiListLayout)
                    findViewById(com.android.systemui.R.id.global_actions_view);
            mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss());
            mGlobalActionsLayout.setHasSeparatedView(mShouldDisplaySeparatedButton);
            mGlobalActionsLayout.setListViewAccessibilityDelegate(new View.AccessibilityDelegate() {
                @Override
                public boolean dispatchPopulateAccessibilityEvent(
                        View host, AccessibilityEvent event) {
@@ -1484,20 +1512,33 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
                    return true;
                }
            });
            setTitle(R.string.global_actions);
        }

        private void updateList() {
            mListView.removeAllViews();
            mSeparatedView.removeAllViews();
            mGlobalActionsLayout.removeAllItems();
            ArrayList<Action> separatedActions =
                    mAdapter.getSeparatedActions(mShouldDisplaySeparatedButton);
            ArrayList<Action> listActions = mAdapter.getListActions(mShouldDisplaySeparatedButton);
            mGlobalActionsLayout.setExpectedListItemCount(listActions.size());
            mGlobalActionsLayout.setExpectedSeparatedItemCount(separatedActions.size());

            for (int i = 0; i < mAdapter.getCount(); i++) {
                ViewGroup parentView = mShouldDisplaySeparatedButton && i == mAdapter.getCount() - 1
                        ? mSeparatedView : mListView;
                View v = mAdapter.getView(i, null, parentView);
                Action action = mAdapter.getItem(i);
                int separatedIndex = separatedActions.indexOf(action);
                ViewGroup parent;
                if (separatedIndex != -1) {
                    parent = mGlobalActionsLayout.getParentView(true, separatedIndex);
                } else {
                    int listIndex = listActions.indexOf(action);
                    parent = mGlobalActionsLayout.getParentView(false, listIndex);
                }
                View v = mAdapter.getView(i, null, parent);
                final int pos = i;
                v.setOnClickListener(view -> mClickListener.onClick(this, pos));
                v.setOnLongClickListener(view ->
                        mLongClickListener.onItemLongClick(null, v, pos, 0));
                parentView.addView(v);
                parent.addView(v);
            }
        }

@@ -1543,9 +1584,9 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
            super.show();
            mShowing = true;
            mGradientDrawable.setAlpha(0);
            mHardwareLayout.setTranslationX(getAnimTranslation());
            mHardwareLayout.setAlpha(0);
            mHardwareLayout.animate()
            mGlobalActionsLayout.setTranslationX(getAnimTranslation());
            mGlobalActionsLayout.setAlpha(0);
            mGlobalActionsLayout.animate()
                    .alpha(1)
                    .translationX(0)
                    .setDuration(300)
@@ -1564,9 +1605,9 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
                return;
            }
            mShowing = false;
            mHardwareLayout.setTranslationX(0);
            mHardwareLayout.setAlpha(1);
            mHardwareLayout.animate()
            mGlobalActionsLayout.setTranslationX(0);
            mGlobalActionsLayout.setAlpha(1);
            mGlobalActionsLayout.animate()
                    .alpha(0)
                    .translationX(getAnimTranslation())
                    .setDuration(300)