• 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.view.View;
24 import android.view.View.OnClickListener;
25 import android.widget.Button;
26 import android.widget.FrameLayout;
27 
28 import androidx.annotation.IntDef;
29 import androidx.annotation.Nullable;
30 
31 import com.android.launcher3.DeviceProfile;
32 import com.android.launcher3.Insettable;
33 import com.android.launcher3.R;
34 import com.android.launcher3.config.FeatureFlags;
35 import com.android.launcher3.util.DisplayController;
36 import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
37 import com.android.launcher3.util.MultiValueAlpha;
38 import com.android.launcher3.util.NavigationMode;
39 import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
40 import com.android.quickstep.util.LayoutUtils;
41 
42 import java.lang.annotation.Retention;
43 import java.lang.annotation.RetentionPolicy;
44 
45 /**
46  * View for showing action buttons in Overview
47  */
48 public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayout
49         implements OnClickListener, Insettable {
50 
51     private final Rect mInsets = new Rect();
52 
53     @IntDef(flag = true, value = {
54             HIDDEN_NON_ZERO_ROTATION,
55             HIDDEN_NO_TASKS,
56             HIDDEN_NO_RECENTS,
57             HIDDEN_SPLIT_SCREEN,
58             HIDDEN_SPLIT_SELECT_ACTIVE,
59             HIDDEN_ACTIONS_IN_MENU,
60             HIDDEN_DESKTOP
61     })
62     @Retention(RetentionPolicy.SOURCE)
63     public @interface ActionsHiddenFlags { }
64 
65     public static final int HIDDEN_NON_ZERO_ROTATION = 1 << 0;
66     public static final int HIDDEN_NO_TASKS = 1 << 1;
67     public static final int HIDDEN_NO_RECENTS = 1 << 2;
68     public static final int HIDDEN_SPLIT_SCREEN = 1 << 3;
69     public static final int HIDDEN_SPLIT_SELECT_ACTIVE = 1 << 4;
70     public static final int HIDDEN_ACTIONS_IN_MENU = 1 << 5;
71     public static final int HIDDEN_DESKTOP = 1 << 6;
72 
73     @IntDef(flag = true, value = {
74             DISABLED_SCROLLING,
75             DISABLED_ROTATED,
76             DISABLED_NO_THUMBNAIL})
77     @Retention(RetentionPolicy.SOURCE)
78     public @interface ActionsDisabledFlags { }
79 
80     public static final int DISABLED_SCROLLING = 1 << 0;
81     public static final int DISABLED_ROTATED = 1 << 1;
82     public static final int DISABLED_NO_THUMBNAIL = 1 << 2;
83 
84     private static final int INDEX_CONTENT_ALPHA = 0;
85     private static final int INDEX_VISIBILITY_ALPHA = 1;
86     private static final int INDEX_FULLSCREEN_ALPHA = 2;
87     private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3;
88     private static final int INDEX_SHARE_TARGET_ALPHA = 4;
89     private static final int INDEX_SCROLL_ALPHA = 5;
90     private static final int NUM_ALPHAS = 6;
91 
92     public @interface SplitButtonHiddenFlags { }
93     public static final int FLAG_IS_NOT_TABLET = 1 << 0;
94 
95     public @interface SplitButtonDisabledFlags { }
96     public static final int FLAG_SINGLE_TASK = 1 << 0;
97 
98     private MultiValueAlpha mMultiValueAlpha;
99     private Button mSplitButton;
100 
101     @ActionsHiddenFlags
102     private int mHiddenFlags;
103 
104     @ActionsDisabledFlags
105     protected int mDisabledFlags;
106 
107     @SplitButtonHiddenFlags
108     private int mSplitButtonHiddenFlags;
109 
110     @SplitButtonDisabledFlags
111     private int mSplitButtonDisabledFlags;
112 
113     @Nullable
114     protected T mCallbacks;
115 
116     @Nullable
117     protected DeviceProfile mDp;
118     private final Rect mTaskSize = new Rect();
119 
OverviewActionsView(Context context)120     public OverviewActionsView(Context context) {
121         this(context, null);
122     }
123 
OverviewActionsView(Context context, @Nullable AttributeSet attrs)124     public OverviewActionsView(Context context, @Nullable AttributeSet attrs) {
125         this(context, attrs, 0);
126     }
127 
OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr)128     public OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
129         super(context, attrs, defStyleAttr, 0);
130     }
131 
132     @Override
onFinishInflate()133     protected void onFinishInflate() {
134         super.onFinishInflate();
135         mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), NUM_ALPHAS);
136         mMultiValueAlpha.setUpdateVisibility(true);
137 
138         findViewById(R.id.action_screenshot).setOnClickListener(this);
139         mSplitButton = findViewById(R.id.action_split);
140         mSplitButton.setOnClickListener(this);
141     }
142 
143     /**
144      * Set listener for callbacks on action button taps.
145      *
146      * @param callbacks for callbacks, or {@code null} to clear the listener.
147      */
setCallbacks(T callbacks)148     public void setCallbacks(T callbacks) {
149         mCallbacks = callbacks;
150     }
151 
152     @Override
onClick(View view)153     public void onClick(View view) {
154         if (mCallbacks == null) {
155             return;
156         }
157         int id = view.getId();
158         if (id == R.id.action_screenshot) {
159             mCallbacks.onScreenshot();
160         } else if (id == R.id.action_split) {
161             mCallbacks.onSplit();
162         }
163     }
164 
165     @Override
onConfigurationChanged(Configuration newConfig)166     protected void onConfigurationChanged(Configuration newConfig) {
167         super.onConfigurationChanged(newConfig);
168         updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
169     }
170 
171     @Override
setInsets(Rect insets)172     public void setInsets(Rect insets) {
173         mInsets.set(insets);
174         updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
175         updatePadding();
176     }
177 
updateHiddenFlags(@ctionsHiddenFlags int visibilityFlags, boolean enable)178     public void updateHiddenFlags(@ActionsHiddenFlags int visibilityFlags, boolean enable) {
179         if (enable) {
180             mHiddenFlags |= visibilityFlags;
181         } else {
182             mHiddenFlags &= ~visibilityFlags;
183         }
184         boolean isHidden = mHiddenFlags != 0;
185         mMultiValueAlpha.get(INDEX_HIDDEN_FLAGS_ALPHA).setValue(isHidden ? 0 : 1);
186     }
187 
188     /**
189      * Updates the proper disabled flag to indicate whether OverviewActionsView should be enabled.
190      * Ignores DISABLED_ROTATED flag for determining enabled. Flag is used to enable/disable
191      * buttons individually, currently done for select button in subclass.
192      *
193      * @param disabledFlags The flag to update.
194      * @param enable        Whether to enable the disable flag: True will cause view to be disabled.
195      */
updateDisabledFlags(@ctionsDisabledFlags int disabledFlags, boolean enable)196     public void updateDisabledFlags(@ActionsDisabledFlags int disabledFlags, boolean enable) {
197         if (enable) {
198             mDisabledFlags |= disabledFlags;
199         } else {
200             mDisabledFlags &= ~disabledFlags;
201         }
202         boolean isEnabled = (mDisabledFlags & ~DISABLED_ROTATED) == 0;
203         LayoutUtils.setViewEnabled(this, isEnabled);
204         updateSplitButtonEnabledState();
205     }
206 
207     /**
208      * Updates the proper flags to indicate whether the "Split screen" button should be hidden.
209      *
210      * @param flag   The flag to update.
211      * @param enable Whether to enable the hidden flag: True will cause view to be hidden.
212      */
updateSplitButtonHiddenFlags(@plitButtonHiddenFlags int flag, boolean enable)213     public void updateSplitButtonHiddenFlags(@SplitButtonHiddenFlags int flag, boolean enable) {
214         if (enable) {
215             mSplitButtonHiddenFlags |= flag;
216         } else {
217             mSplitButtonHiddenFlags &= ~flag;
218         }
219         if (mSplitButton == null) return;
220         boolean shouldBeVisible = mSplitButtonHiddenFlags == 0;
221         mSplitButton.setVisibility(shouldBeVisible ? VISIBLE : GONE);
222         findViewById(R.id.action_split_space).setVisibility(shouldBeVisible ? VISIBLE : GONE);
223     }
224 
225     /**
226      * Updates the proper flags to indicate whether the "Split screen" button should be disabled.
227      *
228      * @param flag   The flag to update.
229      * @param enable Whether to enable the disable flag: True will cause view to be disabled.
230      */
updateSplitButtonDisabledFlags(@plitButtonDisabledFlags int flag, boolean enable)231     public void updateSplitButtonDisabledFlags(@SplitButtonDisabledFlags int flag, boolean enable) {
232         if (enable) {
233             mSplitButtonDisabledFlags |= flag;
234         } else {
235             mSplitButtonDisabledFlags &= ~flag;
236         }
237         updateSplitButtonEnabledState();
238     }
239 
getContentAlpha()240     public MultiProperty getContentAlpha() {
241         return mMultiValueAlpha.get(INDEX_CONTENT_ALPHA);
242     }
243 
getVisibilityAlpha()244     public MultiProperty getVisibilityAlpha() {
245         return mMultiValueAlpha.get(INDEX_VISIBILITY_ALPHA);
246     }
247 
getFullscreenAlpha()248     public MultiProperty getFullscreenAlpha() {
249         return mMultiValueAlpha.get(INDEX_FULLSCREEN_ALPHA);
250     }
251 
getShareTargetAlpha()252     public MultiProperty getShareTargetAlpha() {
253         return mMultiValueAlpha.get(INDEX_SHARE_TARGET_ALPHA);
254     }
255 
getIndexScrollAlpha()256     public MultiProperty getIndexScrollAlpha() {
257         return mMultiValueAlpha.get(INDEX_SCROLL_ALPHA);
258     }
259 
260     /**
261      * Offsets OverviewActionsView horizontal position based on 3 button nav container in taskbar.
262      */
updatePadding()263     private void updatePadding() {
264         // If taskbar is in overview, overview action has dedicated space above nav buttons
265         setPadding(mInsets.left, 0, mInsets.right, 0);
266     }
267 
268     /** Updates vertical margins for different navigation mode or configuration changes. */
updateVerticalMargin(NavigationMode mode)269     public void updateVerticalMargin(NavigationMode mode) {
270         if (mDp == null) {
271             return;
272         }
273         LayoutParams actionParams = (LayoutParams) findViewById(
274                 R.id.action_buttons).getLayoutParams();
275         actionParams.setMargins(
276                 actionParams.leftMargin, mDp.overviewActionsTopMarginPx,
277                 actionParams.rightMargin, getBottomMargin());
278     }
279 
getBottomMargin()280     private int getBottomMargin() {
281         if (mDp == null) {
282             return 0;
283         }
284 
285         if (mDp.isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()) {
286             return mDp.stashedTaskbarHeight;
287         }
288 
289         // Align to bottom of task Rect.
290         return mDp.heightPx - mTaskSize.bottom - mDp.overviewActionsTopMarginPx
291                 - mDp.overviewActionsHeight;
292     }
293 
294     /**
295      * Updates device profile and task size for this view to draw with.
296      */
updateDimension(DeviceProfile dp, Rect taskSize)297     public void updateDimension(DeviceProfile dp, Rect taskSize) {
298         mDp = dp;
299         mTaskSize.set(taskSize);
300         updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
301 
302         requestLayout();
303 
304         mSplitButton.setCompoundDrawablesRelativeWithIntrinsicBounds(
305                 (dp.isLandscape ? R.drawable.ic_split_horizontal : R.drawable.ic_split_vertical),
306                 0, 0, 0);
307     }
308 
309     /**
310      * Enables/disables the "Split" button based on the status of mSplitButtonDisabledFlags and
311      * mDisabledFlags.
312      */
updateSplitButtonEnabledState()313     private void updateSplitButtonEnabledState() {
314         if (mSplitButton == null) {
315             return;
316         }
317         boolean isParentEnabled = (mDisabledFlags & ~DISABLED_ROTATED) == 0;
318         boolean shouldBeEnabled = mSplitButtonDisabledFlags == 0 && isParentEnabled;
319         mSplitButton.setEnabled(shouldBeEnabled);
320     }
321 
322 }
323