1 /* 2 * Copyright (C) 2020 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.quickstep.views; 18 19 import android.content.Context; 20 import android.content.res.Configuration; 21 import android.graphics.Rect; 22 import android.util.AttributeSet; 23 import android.view.View; 24 import android.view.View.OnClickListener; 25 import android.view.ViewGroup; 26 import android.widget.Button; 27 import android.widget.FrameLayout; 28 import android.widget.LinearLayout; 29 30 import androidx.annotation.IntDef; 31 import androidx.annotation.Nullable; 32 33 import com.android.launcher3.DeviceProfile; 34 import com.android.launcher3.Insettable; 35 import com.android.launcher3.R; 36 import com.android.launcher3.util.MultiValueAlpha; 37 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; 38 import com.android.quickstep.SysUINavigationMode; 39 import com.android.quickstep.SysUINavigationMode.Mode; 40 import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks; 41 import com.android.quickstep.util.LayoutUtils; 42 43 import java.lang.annotation.Retention; 44 import java.lang.annotation.RetentionPolicy; 45 46 /** 47 * View for showing action buttons in Overview 48 */ 49 public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayout 50 implements OnClickListener, Insettable { 51 52 private final Rect mInsets = new Rect(); 53 54 @IntDef(flag = true, value = { 55 HIDDEN_NON_ZERO_ROTATION, 56 HIDDEN_NO_TASKS, 57 HIDDEN_NO_RECENTS, 58 HIDDEN_FOCUSED_SCROLL, 59 HIDDEN_SPLIT_SCREEN}) 60 @Retention(RetentionPolicy.SOURCE) 61 public @interface ActionsHiddenFlags { } 62 63 public static final int HIDDEN_NON_ZERO_ROTATION = 1 << 0; 64 public static final int HIDDEN_NO_TASKS = 1 << 1; 65 public static final int HIDDEN_NO_RECENTS = 1 << 2; 66 public static final int HIDDEN_FOCUSED_SCROLL = 1 << 3; 67 public static final int HIDDEN_SPLIT_SCREEN = 1 << 4; 68 69 @IntDef(flag = true, value = { 70 DISABLED_SCROLLING, 71 DISABLED_ROTATED, 72 DISABLED_NO_THUMBNAIL}) 73 @Retention(RetentionPolicy.SOURCE) 74 public @interface ActionsDisabledFlags { } 75 76 public static final int DISABLED_SCROLLING = 1 << 0; 77 public static final int DISABLED_ROTATED = 1 << 1; 78 public static final int DISABLED_NO_THUMBNAIL = 1 << 2; 79 80 private static final int INDEX_CONTENT_ALPHA = 0; 81 private static final int INDEX_VISIBILITY_ALPHA = 1; 82 private static final int INDEX_FULLSCREEN_ALPHA = 2; 83 private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3; 84 85 private final MultiValueAlpha mMultiValueAlpha; 86 private Button mSplitButton; 87 88 @ActionsHiddenFlags 89 private int mHiddenFlags; 90 91 @ActionsDisabledFlags 92 protected int mDisabledFlags; 93 94 @Nullable 95 protected T mCallbacks; 96 97 @Nullable 98 protected DeviceProfile mDp; 99 OverviewActionsView(Context context)100 public OverviewActionsView(Context context) { 101 this(context, null); 102 } 103 OverviewActionsView(Context context, @Nullable AttributeSet attrs)104 public OverviewActionsView(Context context, @Nullable AttributeSet attrs) { 105 this(context, attrs, 0); 106 } 107 OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr)108 public OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 109 super(context, attrs, defStyleAttr, 0); 110 mMultiValueAlpha = new MultiValueAlpha(this, 5); 111 mMultiValueAlpha.setUpdateVisibility(true); 112 } 113 114 @Override onFinishInflate()115 protected void onFinishInflate() { 116 super.onFinishInflate(); 117 findViewById(R.id.action_screenshot).setOnClickListener(this); 118 119 mSplitButton = findViewById(R.id.action_split); 120 mSplitButton.setOnClickListener(this); 121 } 122 123 /** 124 * Set listener for callbacks on action button taps. 125 * 126 * @param callbacks for callbacks, or {@code null} to clear the listener. 127 */ setCallbacks(T callbacks)128 public void setCallbacks(T callbacks) { 129 mCallbacks = callbacks; 130 } 131 132 @Override onClick(View view)133 public void onClick(View view) { 134 if (mCallbacks == null) { 135 return; 136 } 137 int id = view.getId(); 138 if (id == R.id.action_screenshot) { 139 mCallbacks.onScreenshot(); 140 } else if (id == R.id.action_split) { 141 mCallbacks.onSplit(); 142 } 143 } 144 145 @Override onConfigurationChanged(Configuration newConfig)146 protected void onConfigurationChanged(Configuration newConfig) { 147 super.onConfigurationChanged(newConfig); 148 updateVerticalMargin(SysUINavigationMode.getMode(getContext())); 149 } 150 151 @Override setInsets(Rect insets)152 public void setInsets(Rect insets) { 153 mInsets.set(insets); 154 updateVerticalMargin(SysUINavigationMode.getMode(getContext())); 155 updateHorizontalPadding(); 156 } 157 updateHiddenFlags(@ctionsHiddenFlags int visibilityFlags, boolean enable)158 public void updateHiddenFlags(@ActionsHiddenFlags int visibilityFlags, boolean enable) { 159 if (enable) { 160 mHiddenFlags |= visibilityFlags; 161 } else { 162 mHiddenFlags &= ~visibilityFlags; 163 } 164 boolean isHidden = mHiddenFlags != 0; 165 mMultiValueAlpha.getProperty(INDEX_HIDDEN_FLAGS_ALPHA).setValue(isHidden ? 0 : 1); 166 } 167 168 /** 169 * Updates the proper disabled flag to indicate whether OverviewActionsView should be enabled. 170 * Ignores DISABLED_ROTATED flag for determining enabled. Flag is used to enable/disable 171 * buttons individually, currently done for select button in subclass. 172 * 173 * @param disabledFlags The flag to update. 174 * @param enable Whether to enable the disable flag: True will cause view to be disabled. 175 */ updateDisabledFlags(@ctionsDisabledFlags int disabledFlags, boolean enable)176 public void updateDisabledFlags(@ActionsDisabledFlags int disabledFlags, boolean enable) { 177 if (enable) { 178 mDisabledFlags |= disabledFlags; 179 } else { 180 mDisabledFlags &= ~disabledFlags; 181 } 182 boolean isEnabled = (mDisabledFlags & ~DISABLED_ROTATED) == 0; 183 LayoutUtils.setViewEnabled(this, isEnabled); 184 } 185 getContentAlpha()186 public AlphaProperty getContentAlpha() { 187 return mMultiValueAlpha.getProperty(INDEX_CONTENT_ALPHA); 188 } 189 getVisibilityAlpha()190 public AlphaProperty getVisibilityAlpha() { 191 return mMultiValueAlpha.getProperty(INDEX_VISIBILITY_ALPHA); 192 } 193 getFullscreenAlpha()194 public AlphaProperty getFullscreenAlpha() { 195 return mMultiValueAlpha.getProperty(INDEX_FULLSCREEN_ALPHA); 196 } 197 updateHorizontalPadding()198 private void updateHorizontalPadding() { 199 setPadding(mInsets.left, 0, mInsets.right, 0); 200 } 201 202 /** Updates vertical margins for different navigation mode or configuration changes. */ updateVerticalMargin(Mode mode)203 public void updateVerticalMargin(Mode mode) { 204 if (mDp == null) { 205 return; 206 } 207 LayoutParams actionParams = (LayoutParams) findViewById( 208 R.id.action_buttons).getLayoutParams(); 209 actionParams.setMargins( 210 actionParams.leftMargin, getOverviewActionsTopMarginPx(mode, mDp), 211 actionParams.rightMargin, getOverviewActionsBottomMarginPx(mode, mDp)); 212 } 213 214 /** 215 * Set the device profile for this view to draw with. 216 */ setDp(DeviceProfile dp)217 public void setDp(DeviceProfile dp) { 218 mDp = dp; 219 updateVerticalMargin(SysUINavigationMode.getMode(getContext())); 220 221 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( 222 dp.isVerticalBarLayout() ? 0 : dp.overviewActionsButtonSpacing, 223 ViewGroup.LayoutParams.MATCH_PARENT); 224 params.weight = dp.isVerticalBarLayout() ? 1 : 0; 225 findViewById(R.id.action_split_space).setLayoutParams(params); 226 227 requestLayout(); 228 229 mSplitButton.setCompoundDrawablesWithIntrinsicBounds( 230 (dp.isLandscape ? R.drawable.ic_split_horizontal : R.drawable.ic_split_vertical), 231 0, 0, 0); 232 } 233 setSplitButtonVisible(boolean visible)234 public void setSplitButtonVisible(boolean visible) { 235 if (mSplitButton == null) { 236 return; 237 } 238 239 mSplitButton.setVisibility(visible ? VISIBLE : GONE); 240 findViewById(R.id.action_split_space).setVisibility(visible ? VISIBLE : GONE); 241 } 242 243 /** Get the top margin associated with the action buttons in Overview. */ getOverviewActionsTopMarginPx( SysUINavigationMode.Mode mode, DeviceProfile dp)244 public static int getOverviewActionsTopMarginPx( 245 SysUINavigationMode.Mode mode, DeviceProfile dp) { 246 // In vertical bar, use the smaller task margin for the top regardless of mode 247 if (dp.isVerticalBarLayout()) { 248 return dp.overviewTaskMarginPx; 249 } 250 251 if (mode == SysUINavigationMode.Mode.THREE_BUTTONS) { 252 return dp.overviewActionsMarginThreeButtonPx; 253 } 254 255 return dp.overviewActionsTopMarginGesturePx; 256 } 257 258 /** Get the bottom margin associated with the action buttons in Overview. */ getOverviewActionsBottomMarginPx( SysUINavigationMode.Mode mode, DeviceProfile dp)259 public static int getOverviewActionsBottomMarginPx( 260 SysUINavigationMode.Mode mode, DeviceProfile dp) { 261 int inset = dp.getInsets().bottom; 262 263 if (dp.isVerticalBarLayout()) { 264 return inset; 265 } 266 267 if (mode == SysUINavigationMode.Mode.THREE_BUTTONS) { 268 return dp.overviewActionsMarginThreeButtonPx + inset; 269 } 270 271 return dp.overviewActionsBottomMarginGesturePx + inset; 272 } 273 } 274