Home | History | Annotate | Download | only in globalactions
      1 /*
      2  * Copyright (C) 2019 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.systemui.globalactions;
     18 
     19 import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
     20 import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
     21 import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
     22 
     23 import android.content.Context;
     24 import android.util.AttributeSet;
     25 import android.view.Gravity;
     26 import android.view.View;
     27 
     28 import com.android.internal.annotations.VisibleForTesting;
     29 import com.android.systemui.R;
     30 
     31 /**
     32  * Grid-based implementation of the button layout created by the global actions dialog.
     33  */
     34 public class GlobalActionsColumnLayout extends GlobalActionsLayout {
     35     private boolean mLastSnap;
     36 
     37     public GlobalActionsColumnLayout(Context context, AttributeSet attrs) {
     38         super(context, attrs);
     39     }
     40 
     41     @Override
     42     protected void onLayout(boolean changed, int l, int t, int r, int b) {
     43         super.onLayout(changed, l, t, r, b);
     44 
     45         post(() -> updateSnap());
     46     }
     47 
     48     @Override
     49     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     50         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     51     }
     52 
     53     @VisibleForTesting
     54     protected boolean shouldReverseListItems() {
     55         int rotation = getCurrentRotation();
     56         if (rotation == ROTATION_NONE) {
     57             return false;
     58         }
     59         if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
     60             return rotation == ROTATION_LANDSCAPE;
     61         }
     62         return rotation == ROTATION_SEASCAPE;
     63     }
     64 
     65     @Override
     66     public void onUpdateList() {
     67         super.onUpdateList();
     68         updateChildOrdering();
     69     }
     70 
     71     private void updateChildOrdering() {
     72         if (shouldReverseListItems()) {
     73             getListView().bringToFront();
     74         } else {
     75             getSeparatedView().bringToFront();
     76         }
     77     }
     78 
     79     /**
     80      *  Snap this layout to align with the power button.
     81      */
     82     @VisibleForTesting
     83     protected void snapToPowerButton() {
     84         int offset = getPowerButtonOffsetDistance();
     85         switch (getCurrentRotation()) {
     86             case (ROTATION_LANDSCAPE):
     87                 setPadding(offset, 0, 0, 0);
     88                 setGravity(Gravity.LEFT | Gravity.TOP);
     89                 break;
     90             case (ROTATION_SEASCAPE):
     91                 setPadding(0, 0, offset, 0);
     92                 setGravity(Gravity.RIGHT | Gravity.BOTTOM);
     93                 break;
     94             default:
     95                 setPadding(0, offset, 0, 0);
     96                 setGravity(Gravity.TOP | Gravity.RIGHT);
     97                 break;
     98         }
     99     }
    100 
    101     /**
    102      *  Detach this layout from snapping to the power button and instead center along that edge.
    103      */
    104     @VisibleForTesting
    105     protected void centerAlongEdge() {
    106         switch (getCurrentRotation()) {
    107             case (ROTATION_LANDSCAPE):
    108                 setPadding(0, 0, 0, 0);
    109                 setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
    110                 break;
    111             case (ROTATION_SEASCAPE):
    112                 setPadding(0, 0, 0, 0);
    113                 setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
    114                 break;
    115             default:
    116                 setPadding(0, 0, 0, 0);
    117                 setGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
    118                 break;
    119         }
    120     }
    121 
    122     /**
    123      * Determines the distance from the top of the screen to the power button.
    124      */
    125     @VisibleForTesting
    126     protected int getPowerButtonOffsetDistance() {
    127         return Math.round(getContext().getResources().getDimension(
    128                 R.dimen.global_actions_top_padding));
    129     }
    130 
    131     /**
    132      * Check whether there is enough extra space below the dialog such that we can offset the top
    133      * of the dialog from the top of the phone to line it up with the power button, then either
    134      * snap the dialog to the power button or center it along the edge with snapToPowerButton.
    135      */
    136     @VisibleForTesting
    137     protected boolean shouldSnapToPowerButton() {
    138         int offsetSize = getPowerButtonOffsetDistance();
    139         int dialogSize;
    140         int screenSize;
    141         View wrapper = getWrapper();
    142         int rotation = getCurrentRotation();
    143         if (rotation == ROTATION_NONE) {
    144             dialogSize = wrapper.getMeasuredHeight();
    145             screenSize = getMeasuredHeight();
    146         } else {
    147             dialogSize = wrapper.getMeasuredWidth();
    148             screenSize = getMeasuredWidth();
    149         }
    150         return dialogSize + offsetSize < screenSize;
    151     }
    152 
    153     @VisibleForTesting
    154     protected void updateSnap() {
    155         boolean snap = shouldSnapToPowerButton();
    156         if (snap != mLastSnap) {
    157             if (snap) {
    158                 snapToPowerButton();
    159             } else {
    160                 centerAlongEdge();
    161             }
    162         }
    163         mLastSnap = snap;
    164     }
    165 
    166     @VisibleForTesting
    167     protected float getGridItemSize() {
    168         return getContext().getResources().getDimension(R.dimen.global_actions_grid_item_height);
    169     }
    170 
    171     @VisibleForTesting
    172     protected float getAnimationDistance() {
    173         return getGridItemSize() / 2;
    174     }
    175 
    176     @Override
    177     public float getAnimationOffsetX() {
    178         if (getCurrentRotation() == ROTATION_NONE) {
    179             return getAnimationDistance();
    180         }
    181         return 0;
    182     }
    183 
    184     @Override
    185     public float getAnimationOffsetY() {
    186         switch (getCurrentRotation()) {
    187             case ROTATION_LANDSCAPE:
    188                 return -getAnimationDistance();
    189             case ROTATION_SEASCAPE:
    190                 return getAnimationDistance();
    191             default: // Portrait
    192                 return 0;
    193         }
    194     }
    195 }
    196