• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.util.Log;
24 import android.view.View;
25 import android.view.View.OnClickListener;
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.Flags;
35 import com.android.launcher3.Insettable;
36 import com.android.launcher3.R;
37 import com.android.launcher3.anim.AnimatedFloat;
38 import com.android.launcher3.util.DisplayController;
39 import com.android.launcher3.util.MultiValueAlpha;
40 import com.android.launcher3.util.NavigationMode;
41 import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
42 import com.android.quickstep.util.LayoutUtils;
43 import com.android.wm.shell.shared.TypefaceUtils;
44 import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
45 
46 import java.lang.annotation.Retention;
47 import java.lang.annotation.RetentionPolicy;
48 import java.util.Arrays;
49 
50 /**
51  * View for showing action buttons in Overview
52  */
53 public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayout
54         implements OnClickListener, Insettable {
55     public static final String TAG = "OverviewActionsView";
56     private final Rect mInsets = new Rect();
57 
58     @IntDef(flag = true, value = {
59             HIDDEN_NON_ZERO_ROTATION,
60             HIDDEN_NO_TASKS,
61             HIDDEN_NO_RECENTS,
62             HIDDEN_SPLIT_SCREEN,
63             HIDDEN_SPLIT_SELECT_ACTIVE,
64             HIDDEN_ACTIONS_IN_MENU,
65             HIDDEN_DESKTOP
66     })
67     @Retention(RetentionPolicy.SOURCE)
68     public @interface ActionsHiddenFlags { }
69 
70     public static final int HIDDEN_NON_ZERO_ROTATION = 1 << 0;
71     public static final int HIDDEN_NO_TASKS = 1 << 1;
72     public static final int HIDDEN_NO_RECENTS = 1 << 2;
73     public static final int HIDDEN_SPLIT_SCREEN = 1 << 3;
74     public static final int HIDDEN_SPLIT_SELECT_ACTIVE = 1 << 4;
75     public static final int HIDDEN_ACTIONS_IN_MENU = 1 << 5;
76     public static final int HIDDEN_DESKTOP = 1 << 6;
77 
78     @IntDef(flag = true, value = {
79             DISABLED_SCROLLING,
80             DISABLED_ROTATED,
81             DISABLED_NO_THUMBNAIL})
82     @Retention(RetentionPolicy.SOURCE)
83     public @interface ActionsDisabledFlags { }
84 
85     public static final int DISABLED_SCROLLING = 1 << 0;
86     public static final int DISABLED_ROTATED = 1 << 1;
87     public static final int DISABLED_NO_THUMBNAIL = 1 << 2;
88 
89     private static final int INDEX_CONTENT_ALPHA = 0;
90     private static final int INDEX_VISIBILITY_ALPHA = 1;
91     private static final int INDEX_FULLSCREEN_ALPHA = 2;
92     private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3;
93     private static final int INDEX_SHARE_TARGET_ALPHA = 4;
94     private static final int INDEX_SCROLL_ALPHA = 5;
95     private static final int INDEX_GROUPED_ALPHA = 6;
96     private static final int INDEX_3P_LAUNCHER = 7;
97     private static final int NUM_ALPHAS = 8;
98 
99     public @interface SplitButtonHiddenFlags { }
100     public static final int FLAG_SMALL_SCREEN_HIDE_SPLIT = 1 << 0;
101 
102     /**
103      * Holds an AnimatedFloat for each alpha property, used to set or animate alpha values in
104      * {@link #mMultiValueAlphas}.
105      */
106     private final AnimatedFloat[] mAlphaProperties = new AnimatedFloat[NUM_ALPHAS];
107 
108     /** Holds MultiValueAlpha values for all actions bars */
109     private final MultiValueAlpha[] mMultiValueAlphas = new MultiValueAlpha[2];
110     /** Index used for single-task actions in the mMultiValueAlphas array */
111     private static final int ACTIONS_ALPHAS = 0;
112     /** Index used for grouped-task actions in the mMultiValueAlphas array */
113     private static final int GROUP_ACTIONS_ALPHAS = 1;
114 
115     /** Container for the action buttons below a focused, non-split Overview tile. */
116     protected LinearLayout mActionButtons;
117     private Button mSplitButton;
118     /**
119      * The "save app pair" button. Currently this is the only button that is not contained in
120      * mActionButtons, since it is the sole button that appears for a grouped task.
121      */
122     private Button mSaveAppPairButton;
123 
124     @ActionsHiddenFlags
125     private int mHiddenFlags;
126 
127     @ActionsDisabledFlags
128     protected int mDisabledFlags;
129 
130     @SplitButtonHiddenFlags
131     private int mSplitButtonHiddenFlags;
132 
133     @Nullable
134     protected T mCallbacks;
135 
136     @Nullable
137     protected DeviceProfile mDp;
138     private final Rect mTaskSize = new Rect();
139     private boolean mIsGroupedTask = false;
140     private boolean mCanSaveAppPair = false;
141 
OverviewActionsView(Context context)142     public OverviewActionsView(Context context) {
143         this(context, null);
144     }
145 
OverviewActionsView(Context context, @Nullable AttributeSet attrs)146     public OverviewActionsView(Context context, @Nullable AttributeSet attrs) {
147         this(context, attrs, 0);
148     }
149 
OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr)150     public OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
151         super(context, attrs, defStyleAttr, 0);
152     }
153 
154     @Override
onFinishInflate()155     protected void onFinishInflate() {
156         super.onFinishInflate();
157         // Initialize 2 view containers: one for single tasks, one for grouped tasks.
158         // These will take up the same space on the screen and alternate visibility as needed.
159         // Currently, the only grouped task action is "save app pairs".
160         mActionButtons = findViewById(R.id.action_buttons);
161         mSaveAppPairButton = findViewById(R.id.action_save_app_pair);
162         TypefaceUtils.setTypeface(mSaveAppPairButton, FontFamily.GSF_LABEL_LARGE);
163         // Initialize a list to hold alphas for mActionButtons and any group action buttons.
164         mMultiValueAlphas[ACTIONS_ALPHAS] = new MultiValueAlpha(mActionButtons, NUM_ALPHAS);
165         mMultiValueAlphas[GROUP_ACTIONS_ALPHAS] =
166                 new MultiValueAlpha(mSaveAppPairButton, NUM_ALPHAS);
167         Arrays.stream(mMultiValueAlphas).forEach(a -> a.setUpdateVisibility(true));
168         // To control alpha simultaneously on mActionButtons and any group action buttons, we set up
169         // an AnimatedFloat for each alpha property.
170         for (int i = 0; i < NUM_ALPHAS; i++) {
171             final int index = i;
172             mAlphaProperties[index] = new AnimatedFloat(() -> {
173                 for (MultiValueAlpha multiValueAlpha : mMultiValueAlphas) {
174                     multiValueAlpha.get(index).setValue(mAlphaProperties[index].value);
175                 }
176             }, 1f /* initialValue */);
177         }
178 
179         // The screenshot button is implemented as a Button in launcher3 and NexusLauncher, but is
180         // an ImageButton in go launcher (does not share a common class with Button). Take care when
181         // casting this.
182         View screenshotButton = findViewById(R.id.action_screenshot);
183         screenshotButton.setOnClickListener(this);
184         mSplitButton = findViewById(R.id.action_split);
185         mSplitButton.setOnClickListener(this);
186         mSaveAppPairButton.setOnClickListener(this);
187     }
188 
189     /**
190      * Set listener for callbacks on action button taps.
191      *
192      * @param callbacks for callbacks, or {@code null} to clear the listener.
193      */
setCallbacks(T callbacks)194     public void setCallbacks(T callbacks) {
195         mCallbacks = callbacks;
196     }
197 
198     @Override
onClick(View view)199     public void onClick(View view) {
200         if (mCallbacks == null) {
201             return;
202         }
203         int id = view.getId();
204         if (id == R.id.action_screenshot) {
205             mCallbacks.onScreenshot();
206         } else if (id == R.id.action_split) {
207             mCallbacks.onSplit();
208         } else if (id == R.id.action_save_app_pair) {
209             mCallbacks.onSaveAppPair();
210         }
211     }
212 
213     @Override
onConfigurationChanged(Configuration newConfig)214     protected void onConfigurationChanged(Configuration newConfig) {
215         super.onConfigurationChanged(newConfig);
216         updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
217     }
218 
219     @Override
setInsets(Rect insets)220     public void setInsets(Rect insets) {
221         mInsets.set(insets);
222         updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
223         updatePadding();
224     }
225 
updateHiddenFlags(@ctionsHiddenFlags int visibilityFlags, boolean enable)226     public void updateHiddenFlags(@ActionsHiddenFlags int visibilityFlags, boolean enable) {
227         if (enable) {
228             mHiddenFlags |= visibilityFlags;
229         } else {
230             mHiddenFlags &= ~visibilityFlags;
231         }
232         boolean isHidden = mHiddenFlags != 0;
233         mAlphaProperties[INDEX_HIDDEN_FLAGS_ALPHA].updateValue(isHidden ? 0 : 1);
234     }
235 
236     /**
237      * Updates the proper disabled flag to indicate whether OverviewActionsView should be enabled.
238      * Ignores DISABLED_ROTATED flag for determining enabled. Flag is used to enable/disable
239      * buttons individually, currently done for select button in subclass.
240      *
241      * @param disabledFlags The flag to update.
242      * @param enable        Whether to enable the disable flag: True will cause view to be disabled.
243      */
updateDisabledFlags(@ctionsDisabledFlags int disabledFlags, boolean enable)244     public void updateDisabledFlags(@ActionsDisabledFlags int disabledFlags, boolean enable) {
245         if (enable) {
246             mDisabledFlags |= disabledFlags;
247         } else {
248             mDisabledFlags &= ~disabledFlags;
249         }
250         boolean isEnabled = (mDisabledFlags & ~DISABLED_ROTATED) == 0;
251         LayoutUtils.setViewEnabled(this, isEnabled);
252     }
253 
254     /**
255      * Updates a batch of flags to hide and show actions buttons when a grouped task (split screen)
256      * is focused.
257      * @param isGroupedTask True if the focused task is a grouped task.
258      * @param canSaveAppPair True if the focused task is a grouped task and can be saved as an app
259      *                      pair.
260      */
updateForGroupedTask(boolean isGroupedTask, boolean canSaveAppPair)261     public void updateForGroupedTask(boolean isGroupedTask, boolean canSaveAppPair) {
262         Log.d(TAG, "updateForGroupedTask() called with: isGroupedTask = [" + isGroupedTask
263                 + "], canSaveAppPair = [" + canSaveAppPair + "]");
264         mIsGroupedTask = isGroupedTask;
265         mCanSaveAppPair = canSaveAppPair;
266         updateActionButtonsVisibility();
267     }
268 
269     /**
270      * Updates a batch of flags to hide and show actions buttons for tablet/non tablet case.
271      */
updateForIsTablet()272     private void updateForIsTablet() {
273         assert mDp != null;
274         // Update flags to see if split button should be hidden.
275         updateSplitButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_SPLIT, !mDp.isTablet);
276         updateActionButtonsVisibility();
277     }
278 
updateActionButtonsVisibility()279     private void updateActionButtonsVisibility() {
280         if (mDp == null) {
281             return;
282         }
283         boolean showSingleTaskActions = !mIsGroupedTask;
284         boolean showGroupActions = mIsGroupedTask && mDp.isTablet && mCanSaveAppPair;
285         Log.d(TAG, "updateActionButtonsVisibility() called: showSingleTaskActions = ["
286                 + showSingleTaskActions + "], showGroupActions = [" + showGroupActions + "]");
287         getActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showSingleTaskActions ? 1 : 0);
288         getGroupActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showGroupActions ? 1 : 0);
289     }
290 
291     /**
292      * Updates flags to hide and show actions buttons for 1p/3p launchers.
293      */
updateFor3pLauncher(boolean is3pLauncher)294     public void updateFor3pLauncher(boolean is3pLauncher) {
295         getGroupActionsAlphas().get(INDEX_3P_LAUNCHER).setValue(is3pLauncher ? 0 : 1);
296     }
297 
getActionsAlphas()298     private MultiValueAlpha getActionsAlphas() {
299         return mMultiValueAlphas[ACTIONS_ALPHAS];
300     }
301 
getGroupActionsAlphas()302     private MultiValueAlpha getGroupActionsAlphas() {
303         return mMultiValueAlphas[GROUP_ACTIONS_ALPHAS];
304     }
305 
306     /**
307      * Updates the proper flags to indicate whether the "Split screen" button should be hidden.
308      *
309      * @param flag   The flag to update.
310      * @param enable Whether to enable the hidden flag: True will cause view to be hidden.
311      */
updateSplitButtonHiddenFlags(@plitButtonHiddenFlags int flag, boolean enable)312     void updateSplitButtonHiddenFlags(@SplitButtonHiddenFlags int flag,
313             boolean enable) {
314         if (mSplitButton == null) return;
315         if (enable) {
316             mSplitButtonHiddenFlags |= flag;
317         } else {
318             mSplitButtonHiddenFlags &= ~flag;
319         }
320         int desiredVisibility = mSplitButtonHiddenFlags == 0 ? VISIBLE : GONE;
321         if (mSplitButton.getVisibility() != desiredVisibility) {
322             mSplitButton.setVisibility(desiredVisibility);
323             mActionButtons.requestLayout();
324         }
325     }
326 
getContentAlpha()327     public AnimatedFloat getContentAlpha() {
328         return mAlphaProperties[INDEX_CONTENT_ALPHA];
329     }
330 
getVisibilityAlpha()331     public AnimatedFloat getVisibilityAlpha() {
332         return mAlphaProperties[INDEX_VISIBILITY_ALPHA];
333     }
334 
getFullscreenAlpha()335     public AnimatedFloat getFullscreenAlpha() {
336         return mAlphaProperties[INDEX_FULLSCREEN_ALPHA];
337     }
338 
getShareTargetAlpha()339     public AnimatedFloat getShareTargetAlpha() {
340         return mAlphaProperties[INDEX_SHARE_TARGET_ALPHA];
341     }
342 
getIndexScrollAlpha()343     public AnimatedFloat getIndexScrollAlpha() {
344         return mAlphaProperties[INDEX_SCROLL_ALPHA];
345     }
346 
347     /**
348      * Returns the visibility of the overview actions buttons.
349      */
areActionsButtonsVisible()350     public boolean areActionsButtonsVisible() {
351         return mActionButtons.getVisibility() == View.VISIBLE
352                 || mSaveAppPairButton.getVisibility() == View.VISIBLE;
353     }
354 
355     /**
356      * Offsets OverviewActionsView horizontal position based on 3 button nav container in taskbar.
357      */
updatePadding()358     private void updatePadding() {
359         // If taskbar is in overview, overview action has dedicated space above nav buttons
360         setPadding(mInsets.left, 0, mInsets.right, 0);
361     }
362 
363     /** Updates vertical margins for different navigation mode or configuration changes. */
updateVerticalMargin(NavigationMode mode)364     public void updateVerticalMargin(NavigationMode mode) {
365         updateActionBarPosition(mActionButtons);
366         updateActionBarPosition(mSaveAppPairButton);
367     }
368 
369     /** Positions actions buttons according to device settings and insets. */
updateActionBarPosition(View actionBar)370     private void updateActionBarPosition(View actionBar) {
371         if (mDp == null) {
372             return;
373         }
374 
375         LayoutParams actionParams = (LayoutParams) actionBar.getLayoutParams();
376         actionParams.setMargins(
377                 actionParams.leftMargin, mDp.overviewActionsTopMarginPx,
378                 actionParams.rightMargin, getBottomMargin());
379     }
380 
getBottomMargin()381     private int getBottomMargin() {
382         if (mDp == null) {
383             return 0;
384         }
385 
386         if (mDp.isTablet && Flags.enableGridOnlyOverview()) {
387             return mDp.stashedTaskbarHeight;
388         }
389 
390         // Align to bottom of task Rect.
391         return mDp.heightPx - mTaskSize.bottom - mDp.overviewActionsTopMarginPx
392                 - mDp.overviewActionsHeight;
393     }
394 
395     /**
396      * Updates device profile and task size for this view to draw with.
397      */
updateDimension(DeviceProfile dp, Rect taskSize)398     public void updateDimension(DeviceProfile dp, Rect taskSize) {
399         mDp = dp;
400         mTaskSize.set(taskSize);
401         updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
402         updateForIsTablet();
403 
404         requestLayout();
405 
406         int splitIconRes = dp.isLeftRightSplit
407                 ? R.drawable.ic_split_horizontal
408                 : R.drawable.ic_split_vertical;
409         mSplitButton.setCompoundDrawablesRelativeWithIntrinsicBounds(splitIconRes, 0, 0, 0);
410 
411         int appPairIconRes = dp.isLeftRightSplit
412                 ? R.drawable.ic_save_app_pair_left_right
413                 : R.drawable.ic_save_app_pair_up_down;
414         mSaveAppPairButton.setCompoundDrawablesRelativeWithIntrinsicBounds(
415                 appPairIconRes, 0, 0, 0);
416     }
417 }
418