• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.launcher3;
18 
19 import android.content.Context;
20 import android.graphics.Rect;
21 import android.util.AttributeSet;
22 import android.view.Gravity;
23 import android.view.LayoutInflater;
24 import android.view.MotionEvent;
25 import android.view.View;
26 import android.view.ViewDebug;
27 import android.view.ViewGroup;
28 import android.widget.FrameLayout;
29 
30 import androidx.annotation.Nullable;
31 
32 import java.util.function.Consumer;
33 
34 /**
35  * View class that represents the bottom row of the home screen.
36  */
37 public class Hotseat extends CellLayout implements Insettable {
38 
39     // Ratio of empty space, qsb should take up to appear visually centered.
40     public static final float QSB_CENTER_FACTOR = .325f;
41 
42     @ViewDebug.ExportedProperty(category = "launcher")
43     private boolean mHasVerticalHotseat;
44     private Workspace mWorkspace;
45     private boolean mSendTouchToWorkspace;
46     @Nullable
47     private Consumer<Boolean> mOnVisibilityAggregatedCallback;
48 
49     private final View mQsb;
50     private final int mQsbHeight;
51 
52     private final int mTaskbarViewHeight;
53 
Hotseat(Context context)54     public Hotseat(Context context) {
55         this(context, null);
56     }
57 
Hotseat(Context context, AttributeSet attrs)58     public Hotseat(Context context, AttributeSet attrs) {
59         this(context, attrs, 0);
60     }
61 
Hotseat(Context context, AttributeSet attrs, int defStyle)62     public Hotseat(Context context, AttributeSet attrs, int defStyle) {
63         super(context, attrs, defStyle);
64 
65         mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
66         mQsbHeight = mQsb.getLayoutParams().height;
67         addView(mQsb);
68 
69         mTaskbarViewHeight = context.getResources().getDimensionPixelSize(R.dimen.taskbar_size);
70     }
71 
72     /**
73      * Returns orientation specific cell X given invariant order in the hotseat
74      */
getCellXFromOrder(int rank)75     public int getCellXFromOrder(int rank) {
76         return mHasVerticalHotseat ? 0 : rank;
77     }
78 
79     /**
80      * Returns orientation specific cell Y given invariant order in the hotseat
81      */
getCellYFromOrder(int rank)82     public int getCellYFromOrder(int rank) {
83         return mHasVerticalHotseat ? (getCountY() - (rank + 1)) : 0;
84     }
85 
resetLayout(boolean hasVerticalHotseat)86     public void resetLayout(boolean hasVerticalHotseat) {
87         removeAllViewsInLayout();
88         mHasVerticalHotseat = hasVerticalHotseat;
89         DeviceProfile dp = mActivity.getDeviceProfile();
90         if (hasVerticalHotseat) {
91             setGridSize(1, dp.numShownHotseatIcons);
92         } else {
93             setGridSize(dp.numShownHotseatIcons, 1);
94         }
95     }
96 
97     @Override
setInsets(Rect insets)98     public void setInsets(Rect insets) {
99         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
100         DeviceProfile grid = mActivity.getDeviceProfile();
101 
102         if (grid.isVerticalBarLayout()) {
103             mQsb.setVisibility(View.GONE);
104             lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
105             if (grid.isSeascape()) {
106                 lp.gravity = Gravity.LEFT;
107                 lp.width = grid.hotseatBarSizePx + insets.left;
108             } else {
109                 lp.gravity = Gravity.RIGHT;
110                 lp.width = grid.hotseatBarSizePx + insets.right;
111             }
112         } else {
113             mQsb.setVisibility(View.VISIBLE);
114             lp.gravity = Gravity.BOTTOM;
115             lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
116             lp.height = (grid.isTaskbarPresent
117                         ? grid.workspacePadding.bottom
118                         : grid.hotseatBarSizePx)
119                     + (grid.isTaskbarPresent ? grid.taskbarSize : insets.bottom);
120         }
121 
122         if (!grid.isTaskbarPresent) {
123             // When taskbar is present, we set the padding separately to ensure a seamless visual
124             // handoff between taskbar and hotseat during drag and drop.
125             Rect padding = grid.getHotseatLayoutPadding();
126             setPadding(padding.left, padding.top, padding.right, padding.bottom);
127         }
128 
129         setLayoutParams(lp);
130         InsettableFrameLayout.dispatchInsets(this, insets);
131     }
132 
setWorkspace(Workspace w)133     public void setWorkspace(Workspace w) {
134         mWorkspace = w;
135     }
136 
137     @Override
onInterceptTouchEvent(MotionEvent ev)138     public boolean onInterceptTouchEvent(MotionEvent ev) {
139         // We allow horizontal workspace scrolling from within the Hotseat. We do this by delegating
140         // touch intercept the Workspace, and if it intercepts, delegating touch to the Workspace
141         // for the remainder of the this input stream.
142         int yThreshold = getMeasuredHeight() - getPaddingBottom();
143         if (mWorkspace != null && ev.getY() <= yThreshold) {
144             mSendTouchToWorkspace = mWorkspace.onInterceptTouchEvent(ev);
145             return mSendTouchToWorkspace;
146         }
147         return false;
148     }
149 
150     @Override
onTouchEvent(MotionEvent event)151     public boolean onTouchEvent(MotionEvent event) {
152         // See comment in #onInterceptTouchEvent
153         if (mSendTouchToWorkspace) {
154             final int action = event.getAction();
155             switch (action & MotionEvent.ACTION_MASK) {
156                 case MotionEvent.ACTION_UP:
157                 case MotionEvent.ACTION_CANCEL:
158                     mSendTouchToWorkspace = false;
159             }
160             return mWorkspace.onTouchEvent(event);
161         }
162         return event.getY() > getCellHeight();
163     }
164 
165     @Override
onVisibilityAggregated(boolean isVisible)166     public void onVisibilityAggregated(boolean isVisible) {
167         super.onVisibilityAggregated(isVisible);
168 
169         if (mOnVisibilityAggregatedCallback != null) {
170             mOnVisibilityAggregatedCallback.accept(isVisible);
171         }
172     }
173 
174     /** Sets a callback to be called onVisibilityAggregated */
setOnVisibilityAggregatedCallback(@ullable Consumer<Boolean> callback)175     public void setOnVisibilityAggregatedCallback(@Nullable Consumer<Boolean> callback) {
176         mOnVisibilityAggregatedCallback = callback;
177     }
178 
179     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)180     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
181         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
182 
183         int width = getShortcutsAndWidgets().getMeasuredWidth();
184         mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
185                 MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
186     }
187 
188     @Override
onLayout(boolean changed, int l, int t, int r, int b)189     protected void onLayout(boolean changed, int l, int t, int r, int b) {
190         super.onLayout(changed, l, t, r, b);
191 
192         int qsbWidth = mQsb.getMeasuredWidth();
193         int left = (r - l - qsbWidth) / 2;
194         int right = left + qsbWidth;
195 
196         int bottom = b - t - getQsbOffsetY();
197         int top = bottom - mQsbHeight;
198         mQsb.layout(left, top, right, bottom);
199     }
200 
201     /**
202      * Returns the number of pixels the QSB is translated from the bottom of the screen.
203      */
getQsbOffsetY()204     private int getQsbOffsetY() {
205         DeviceProfile dp = mActivity.getDeviceProfile();
206         int freeSpace = dp.isTaskbarPresent
207                 ? dp.workspacePadding.bottom
208                 : dp.hotseatBarSizePx - dp.hotseatCellHeightPx - mQsbHeight;
209 
210         if (dp.isScalableGrid && dp.qsbBottomMarginPx > dp.getInsets().bottom) {
211             return Math.min(dp.qsbBottomMarginPx, freeSpace);
212         } else {
213             return (int) (freeSpace * QSB_CENTER_FACTOR) + (dp.isTaskbarPresent
214                     ? dp.taskbarSize
215                     : dp.getInsets().bottom);
216         }
217     }
218 
219     /**
220      * Returns the number of pixels the taskbar is translated from the bottom of the screen.
221      */
getTaskbarOffsetY()222     public int getTaskbarOffsetY() {
223         return (getQsbOffsetY() - mTaskbarViewHeight) / 2;
224     }
225 
226     /**
227      * Sets the alpha value of just our ShortcutAndWidgetContainer.
228      */
setIconsAlpha(float alpha)229     public void setIconsAlpha(float alpha) {
230         getShortcutsAndWidgets().setAlpha(alpha);
231     }
232 
233     /**
234      * Returns the QSB inside hotseat
235      */
getQsb()236     public View getQsb() {
237         return mQsb;
238     }
239 
240 }
241