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