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