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