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 
Hotseat(Context context)52     public Hotseat(Context context) {
53         this(context, null);
54     }
55 
Hotseat(Context context, AttributeSet attrs)56     public Hotseat(Context context, AttributeSet attrs) {
57         this(context, attrs, 0);
58     }
59 
Hotseat(Context context, AttributeSet attrs, int defStyle)60     public Hotseat(Context context, AttributeSet attrs, int defStyle) {
61         super(context, attrs, defStyle);
62 
63         mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
64         addView(mQsb);
65 
66         mQsbHeight = getResources().getDimensionPixelSize(R.dimen.qsb_widget_height);
67     }
68 
69     /**
70      * Returns orientation specific cell X given invariant order in the hotseat
71      */
getCellXFromOrder(int rank)72     public int getCellXFromOrder(int rank) {
73         return mHasVerticalHotseat ? 0 : rank;
74     }
75 
76     /**
77      * Returns orientation specific cell Y given invariant order in the hotseat
78      */
getCellYFromOrder(int rank)79     public int getCellYFromOrder(int rank) {
80         return mHasVerticalHotseat ? (getCountY() - (rank + 1)) : 0;
81     }
82 
resetLayout(boolean hasVerticalHotseat)83     public void resetLayout(boolean hasVerticalHotseat) {
84         removeAllViewsInLayout();
85         mHasVerticalHotseat = hasVerticalHotseat;
86         DeviceProfile dp = mActivity.getDeviceProfile();
87         if (hasVerticalHotseat) {
88             setGridSize(1, dp.numShownHotseatIcons);
89         } else {
90             setGridSize(dp.numShownHotseatIcons, 1);
91         }
92     }
93 
94     @Override
setInsets(Rect insets)95     public void setInsets(Rect insets) {
96         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
97         DeviceProfile grid = mActivity.getDeviceProfile();
98 
99         if (grid.isVerticalBarLayout()) {
100             mQsb.setVisibility(View.GONE);
101             lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
102             if (grid.isSeascape()) {
103                 lp.gravity = Gravity.LEFT;
104                 lp.width = grid.hotseatBarSizePx + insets.left;
105             } else {
106                 lp.gravity = Gravity.RIGHT;
107                 lp.width = grid.hotseatBarSizePx + insets.right;
108             }
109         } else {
110             mQsb.setVisibility(View.VISIBLE);
111             lp.gravity = Gravity.BOTTOM;
112             lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
113             lp.height = (grid.isTaskbarPresent
114                     ? grid.workspacePadding.bottom
115                         : grid.hotseatBarSizePx)
116                     + (grid.isTaskbarPresent ? grid.taskbarSize : insets.bottom);
117         }
118 
119         Rect padding = grid.getHotseatLayoutPadding(getContext());
120         setPadding(padding.left, padding.top, padding.right, padding.bottom);
121         setLayoutParams(lp);
122         InsettableFrameLayout.dispatchInsets(this, insets);
123     }
124 
setWorkspace(Workspace w)125     public void setWorkspace(Workspace w) {
126         mWorkspace = w;
127     }
128 
129     @Override
onInterceptTouchEvent(MotionEvent ev)130     public boolean onInterceptTouchEvent(MotionEvent ev) {
131         // We allow horizontal workspace scrolling from within the Hotseat. We do this by delegating
132         // touch intercept the Workspace, and if it intercepts, delegating touch to the Workspace
133         // for the remainder of the this input stream.
134         int yThreshold = getMeasuredHeight() - getPaddingBottom();
135         if (mWorkspace != null && ev.getY() <= yThreshold) {
136             mSendTouchToWorkspace = mWorkspace.onInterceptTouchEvent(ev);
137             return mSendTouchToWorkspace;
138         }
139         return false;
140     }
141 
142     @Override
onTouchEvent(MotionEvent event)143     public boolean onTouchEvent(MotionEvent event) {
144         // See comment in #onInterceptTouchEvent
145         if (mSendTouchToWorkspace) {
146             final int action = event.getAction();
147             switch (action & MotionEvent.ACTION_MASK) {
148                 case MotionEvent.ACTION_UP:
149                 case MotionEvent.ACTION_CANCEL:
150                     mSendTouchToWorkspace = false;
151             }
152             return mWorkspace.onTouchEvent(event);
153         }
154         // Always let touch follow through to Workspace.
155         return false;
156     }
157 
158     @Override
onVisibilityAggregated(boolean isVisible)159     public void onVisibilityAggregated(boolean isVisible) {
160         super.onVisibilityAggregated(isVisible);
161 
162         if (mOnVisibilityAggregatedCallback != null) {
163             mOnVisibilityAggregatedCallback.accept(isVisible);
164         }
165     }
166 
167     /** Sets a callback to be called onVisibilityAggregated */
setOnVisibilityAggregatedCallback(@ullable Consumer<Boolean> callback)168     public void setOnVisibilityAggregatedCallback(@Nullable Consumer<Boolean> callback) {
169         mOnVisibilityAggregatedCallback = callback;
170     }
171 
172     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)173     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
174         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
175 
176         int width = getShortcutsAndWidgets().getMeasuredWidth();
177         mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
178                 MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
179     }
180 
181     @Override
onLayout(boolean changed, int l, int t, int r, int b)182     protected void onLayout(boolean changed, int l, int t, int r, int b) {
183         super.onLayout(changed, l, t, r, b);
184 
185         int qsbWidth = mQsb.getMeasuredWidth();
186         int left = (r - l - qsbWidth) / 2;
187         int right = left + qsbWidth;
188 
189         int bottom = b - t - mActivity.getDeviceProfile().getQsbOffsetY();
190         int top = bottom - mQsbHeight;
191         mQsb.layout(left, top, right, bottom);
192     }
193 
194     /**
195      * Sets the alpha value of just our ShortcutAndWidgetContainer.
196      */
setIconsAlpha(float alpha)197     public void setIconsAlpha(float alpha) {
198         getShortcutsAndWidgets().setAlpha(alpha);
199     }
200 
getIconsAlpha()201     public float getIconsAlpha() {
202         return getShortcutsAndWidgets().getAlpha();
203     }
204 
205     /**
206      * Returns the QSB inside hotseat
207      */
getQsb()208     public View getQsb() {
209         return mQsb;
210     }
211 
212 }
213