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.View;
26 import android.view.ViewGroup;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 
30 /**
31  * Grid-based implementation of the button layout created by the global actions dialog.
32  */
33 public class GlobalActionsGridLayout extends GlobalActionsLayout {
GlobalActionsGridLayout(Context context, AttributeSet attrs)34     public GlobalActionsGridLayout(Context context, AttributeSet attrs) {
35         super(context, attrs);
36     }
37 
38     @VisibleForTesting
setupListView()39     protected void setupListView() {
40         ListGridLayout listView = getListView();
41         listView.setExpectedCount(mAdapter.countListItems());
42         listView.setReverseSublists(shouldReverseSublists());
43         listView.setReverseItems(shouldReverseListItems());
44         listView.setSwapRowsAndColumns(shouldSwapRowsAndColumns());
45     }
46 
47     @Override
onUpdateList()48     public void onUpdateList() {
49         setupListView();
50         super.onUpdateList();
51         updateSeparatedItemSize();
52     }
53 
54     /**
55      * If the separated view contains only one item, expand the bounds of that item to take up the
56      * entire view, so that the whole thing is touch-able.
57      */
58     @VisibleForTesting
updateSeparatedItemSize()59     protected void updateSeparatedItemSize() {
60         ViewGroup separated = getSeparatedView();
61         if (separated.getChildCount() == 0) {
62             return;
63         }
64         View firstChild = separated.getChildAt(0);
65         ViewGroup.LayoutParams childParams = firstChild.getLayoutParams();
66 
67         if (separated.getChildCount() == 1) {
68             childParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
69             childParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
70         } else {
71             childParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
72             childParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
73         }
74     }
75 
76     @Override
getListView()77     protected ListGridLayout getListView() {
78         return (ListGridLayout) super.getListView();
79     }
80 
81     @Override
removeAllListViews()82     protected void removeAllListViews() {
83         ListGridLayout list = getListView();
84         if (list != null) {
85             list.removeAllItems();
86         }
87     }
88 
89     @Override
addToListView(View v, boolean reverse)90     protected void addToListView(View v, boolean reverse) {
91         ListGridLayout list = getListView();
92         if (list != null) {
93             list.addItem(v);
94         }
95     }
96 
97     @Override
removeAllItems()98     public void removeAllItems() {
99         ViewGroup separatedList = getSeparatedView();
100         ListGridLayout list = getListView();
101         if (separatedList != null) {
102             separatedList.removeAllViews();
103         }
104         if (list != null) {
105             list.removeAllItems();
106         }
107     }
108 
109     /**
110      * Determines whether the ListGridLayout should fill sublists in the reverse order.
111      * Used to account for sublist ordering changing between landscape and seascape views.
112      */
113     @VisibleForTesting
shouldReverseSublists()114     protected boolean shouldReverseSublists() {
115         if (getCurrentRotation() == ROTATION_SEASCAPE) {
116             return true;
117         }
118         return false;
119     }
120 
121     /**
122      * Determines whether the ListGridLayout should fill rows first instead of columns.
123      * Used to account for vertical/horizontal changes due to landscape or seascape rotations.
124      */
125     @VisibleForTesting
shouldSwapRowsAndColumns()126     protected boolean shouldSwapRowsAndColumns() {
127         if (getCurrentRotation() == ROTATION_NONE) {
128             return false;
129         }
130         return true;
131     }
132 
133     @Override
shouldReverseListItems()134     protected boolean shouldReverseListItems() {
135         int rotation = getCurrentRotation();
136         boolean reverse = false; // should we add items to parents in the reverse order?
137         if (rotation == ROTATION_NONE
138                 || rotation == ROTATION_SEASCAPE) {
139             reverse = !reverse; // if we're in portrait or seascape, reverse items
140         }
141         if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
142             reverse = !reverse; // if we're in an RTL language, reverse items (again)
143         }
144         return reverse;
145     }
146 
147     @VisibleForTesting
getAnimationDistance()148     protected float getAnimationDistance() {
149         int rows = getListView().getRowCount();
150         float gridItemSize = getContext().getResources().getDimension(
151                 com.android.systemui.R.dimen.global_actions_grid_item_height);
152         return rows * gridItemSize / 2;
153     }
154 
155     @Override
getAnimationOffsetX()156     public float getAnimationOffsetX() {
157         switch (getCurrentRotation()) {
158             case ROTATION_LANDSCAPE:
159                 return getAnimationDistance();
160             case ROTATION_SEASCAPE:
161                 return -getAnimationDistance();
162             default: // Portrait
163                 return 0;
164         }
165     }
166 
167     @Override
getAnimationOffsetY()168     public float getAnimationOffsetY() {
169         if (getCurrentRotation() == ROTATION_NONE) {
170             return getAnimationDistance();
171         }
172         return 0;
173     }
174 }
175