1 /*
2  * Copyright (C) 2014 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.systemui.recents;
18 
19 import android.app.ActivityManager;
20 import android.content.Context;
21 import android.content.res.Configuration;
22 import android.content.res.Resources;
23 import android.graphics.Rect;
24 import android.provider.Settings;
25 import android.util.DisplayMetrics;
26 import android.view.animation.AnimationUtils;
27 import android.view.animation.Interpolator;
28 
29 import com.android.systemui.Prefs;
30 import com.android.systemui.R;
31 import com.android.systemui.recents.misc.Console;
32 import com.android.systemui.recents.misc.SystemServicesProxy;
33 
34 
35 /** A static Recents configuration for the current context
36  * NOTE: We should not hold any references to a Context from a static instance */
37 public class RecentsConfiguration {
38     static RecentsConfiguration sInstance;
39     static int sPrevConfigurationHashCode;
40 
41     /** Levels of svelte in increasing severity/austerity. */
42     // No svelting.
43     public static final int SVELTE_NONE = 0;
44     // Limit thumbnail cache to number of visible thumbnails when Recents was loaded, disable
45     // caching thumbnails as you scroll.
46     public static final int SVELTE_LIMIT_CACHE = 1;
47     // Disable the thumbnail cache, load thumbnails asynchronously when the activity loads and
48     // evict all thumbnails when hidden.
49     public static final int SVELTE_DISABLE_CACHE = 2;
50     // Disable all thumbnail loading.
51     public static final int SVELTE_DISABLE_LOADING = 3;
52 
53     /** Interpolators */
54     public Interpolator fastOutSlowInInterpolator;
55     public Interpolator fastOutLinearInInterpolator;
56     public Interpolator linearOutSlowInInterpolator;
57     public Interpolator quintOutInterpolator;
58 
59     /** Filtering */
60     public int filteringCurrentViewsAnimDuration;
61     public int filteringNewViewsAnimDuration;
62 
63     /** Insets */
64     public Rect systemInsets = new Rect();
65     public Rect displayRect = new Rect();
66 
67     /** Layout */
68     boolean isLandscape;
69     boolean hasTransposedSearchBar;
70     boolean hasTransposedNavBar;
71 
72     /** Loading */
73     public int maxNumTasksToLoad;
74 
75     /** Search bar */
76     public int searchBarSpaceHeightPx;
77 
78     /** Task stack */
79     public int taskStackScrollDuration;
80     public int taskStackMaxDim;
81     public int taskStackTopPaddingPx;
82     public int dismissAllButtonSizePx;
83     public float taskStackWidthPaddingPct;
84     public float taskStackOverscrollPct;
85 
86     /** Transitions */
87     public int transitionEnterFromAppDelay;
88     public int transitionEnterFromHomeDelay;
89 
90     /** Task view animation and styles */
91     public int taskViewEnterFromAppDuration;
92     public int taskViewEnterFromHomeDuration;
93     public int taskViewEnterFromHomeStaggerDelay;
94     public int taskViewExitToAppDuration;
95     public int taskViewExitToHomeDuration;
96     public int taskViewRemoveAnimDuration;
97     public int taskViewRemoveAnimTranslationXPx;
98     public int taskViewTranslationZMinPx;
99     public int taskViewTranslationZMaxPx;
100     public int taskViewRoundedCornerRadiusPx;
101     public int taskViewHighlightPx;
102     public int taskViewAffiliateGroupEnterOffsetPx;
103     public float taskViewThumbnailAlpha;
104 
105     /** Task bar colors */
106     public int taskBarViewDefaultBackgroundColor;
107     public int taskBarViewLightTextColor;
108     public int taskBarViewDarkTextColor;
109     public int taskBarViewHighlightColor;
110     public float taskBarViewAffiliationColorMinAlpha;
111 
112     /** Task bar size & animations */
113     public int taskBarHeight;
114     public int taskBarDismissDozeDelaySeconds;
115 
116     /** Nav bar scrim */
117     public int navBarScrimEnterDuration;
118 
119     /** Launch states */
120     public boolean launchedWithAltTab;
121     public boolean launchedWithNoRecentTasks;
122     public boolean launchedFromAppWithThumbnail;
123     public boolean launchedFromHome;
124     public boolean launchedFromSearchHome;
125     public boolean launchedReuseTaskStackViews;
126     public boolean launchedHasConfigurationChanged;
127     public int launchedToTaskId;
128     public int launchedNumVisibleTasks;
129     public int launchedNumVisibleThumbnails;
130 
131     /** Misc **/
132     public boolean useHardwareLayers;
133     public int altTabKeyDelay;
134     public boolean fakeShadows;
135 
136     /** Dev options and global settings */
137     public boolean multiStackEnabled;
138     public boolean lockToAppEnabled;
139     public boolean developerOptionsEnabled;
140     public boolean debugModeEnabled;
141     public int svelteLevel;
142 
143     /** Private constructor */
RecentsConfiguration(Context context)144     private RecentsConfiguration(Context context) {
145         // Properties that don't have to be reloaded with each configuration change can be loaded
146         // here.
147 
148         // Interpolators
149         fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
150                 com.android.internal.R.interpolator.fast_out_slow_in);
151         fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
152                 com.android.internal.R.interpolator.fast_out_linear_in);
153         linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
154                 com.android.internal.R.interpolator.linear_out_slow_in);
155         quintOutInterpolator = AnimationUtils.loadInterpolator(context,
156                 com.android.internal.R.interpolator.decelerate_quint);
157     }
158 
159     /** Updates the configuration to the current context */
reinitialize(Context context, SystemServicesProxy ssp)160     public static RecentsConfiguration reinitialize(Context context, SystemServicesProxy ssp) {
161         if (sInstance == null) {
162             sInstance = new RecentsConfiguration(context);
163         }
164         int configHashCode = context.getResources().getConfiguration().hashCode();
165         if (sPrevConfigurationHashCode != configHashCode) {
166             sInstance.update(context);
167             sPrevConfigurationHashCode = configHashCode;
168         }
169         sInstance.updateOnReinitialize(context, ssp);
170         return sInstance;
171     }
172 
173     /** Returns the current recents configuration */
getInstance()174     public static RecentsConfiguration getInstance() {
175         return sInstance;
176     }
177 
178     /** Updates the state, given the specified context */
update(Context context)179     void update(Context context) {
180         Resources res = context.getResources();
181         DisplayMetrics dm = res.getDisplayMetrics();
182 
183         // Debug mode
184         debugModeEnabled = Prefs.getBoolean(context, Prefs.Key.DEBUG_MODE_ENABLED,
185                 false /* defaultValue */);
186         if (debugModeEnabled) {
187             Console.Enabled = true;
188         }
189 
190         // Layout
191         isLandscape = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
192         hasTransposedSearchBar = res.getBoolean(R.bool.recents_has_transposed_search_bar);
193         hasTransposedNavBar = res.getBoolean(R.bool.recents_has_transposed_nav_bar);
194 
195         // Insets
196         displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
197 
198         // Filtering
199         filteringCurrentViewsAnimDuration =
200                 res.getInteger(R.integer.recents_filter_animate_current_views_duration);
201         filteringNewViewsAnimDuration =
202                 res.getInteger(R.integer.recents_filter_animate_new_views_duration);
203 
204         // Loading
205         maxNumTasksToLoad = ActivityManager.getMaxRecentTasksStatic();
206 
207         // Search Bar
208         searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
209 
210         // Task stack
211         taskStackScrollDuration =
212                 res.getInteger(R.integer.recents_animate_task_stack_scroll_duration);
213         taskStackWidthPaddingPct = res.getFloat(R.dimen.recents_stack_width_padding_percentage);
214         taskStackOverscrollPct = res.getFloat(R.dimen.recents_stack_overscroll_percentage);
215         taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
216         taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding);
217         dismissAllButtonSizePx = res.getDimensionPixelSize(R.dimen.recents_dismiss_all_button_size);
218 
219         // Transition
220         transitionEnterFromAppDelay =
221                 res.getInteger(R.integer.recents_enter_from_app_transition_duration);
222         transitionEnterFromHomeDelay =
223                 res.getInteger(R.integer.recents_enter_from_home_transition_duration);
224 
225         // Task view animation and styles
226         taskViewEnterFromAppDuration =
227                 res.getInteger(R.integer.recents_task_enter_from_app_duration);
228         taskViewEnterFromHomeDuration =
229                 res.getInteger(R.integer.recents_task_enter_from_home_duration);
230         taskViewEnterFromHomeStaggerDelay =
231                 res.getInteger(R.integer.recents_task_enter_from_home_stagger_delay);
232         taskViewExitToAppDuration =
233                 res.getInteger(R.integer.recents_task_exit_to_app_duration);
234         taskViewExitToHomeDuration =
235                 res.getInteger(R.integer.recents_task_exit_to_home_duration);
236         taskViewRemoveAnimDuration =
237                 res.getInteger(R.integer.recents_animate_task_view_remove_duration);
238         taskViewRemoveAnimTranslationXPx =
239                 res.getDimensionPixelSize(R.dimen.recents_task_view_remove_anim_translation_x);
240         taskViewRoundedCornerRadiusPx =
241                 res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
242         taskViewHighlightPx = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
243         taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
244         taskViewTranslationZMaxPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
245         taskViewAffiliateGroupEnterOffsetPx =
246                 res.getDimensionPixelSize(R.dimen.recents_task_view_affiliate_group_enter_offset);
247         taskViewThumbnailAlpha = res.getFloat(R.dimen.recents_task_view_thumbnail_alpha);
248 
249         // Task bar colors
250         taskBarViewDefaultBackgroundColor = context.getColor(
251                 R.color.recents_task_bar_default_background_color);
252         taskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color);
253         taskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color);
254         taskBarViewHighlightColor = context.getColor(R.color.recents_task_bar_highlight_color);
255         taskBarViewAffiliationColorMinAlpha = res.getFloat(
256                 R.dimen.recents_task_affiliation_color_min_alpha_percentage);
257 
258         // Task bar size & animations
259         taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
260         taskBarDismissDozeDelaySeconds =
261                 res.getInteger(R.integer.recents_task_bar_dismiss_delay_seconds);
262 
263         // Nav bar scrim
264         navBarScrimEnterDuration =
265                 res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
266 
267         // Misc
268         useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers);
269         altTabKeyDelay = res.getInteger(R.integer.recents_alt_tab_key_delay);
270         fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
271         svelteLevel = res.getInteger(R.integer.recents_svelte_level);
272     }
273 
274     /** Updates the system insets */
updateSystemInsets(Rect insets)275     public void updateSystemInsets(Rect insets) {
276         systemInsets.set(insets);
277     }
278 
279     /** Updates the states that need to be re-read whenever we re-initialize. */
updateOnReinitialize(Context context, SystemServicesProxy ssp)280     void updateOnReinitialize(Context context, SystemServicesProxy ssp) {
281         // Check if the developer options are enabled
282         developerOptionsEnabled = ssp.getGlobalSetting(context,
283                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED) != 0;
284         lockToAppEnabled = ssp.getSystemSetting(context,
285                 Settings.System.LOCK_TO_APP_ENABLED) != 0;
286         multiStackEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window"));
287     }
288 
289     /** Called when the configuration has changed, and we want to reset any configuration specific
290      * members. */
updateOnConfigurationChange()291     public void updateOnConfigurationChange() {
292         // Reset this flag on configuration change to ensure that we recreate new task views
293         launchedReuseTaskStackViews = false;
294         // Set this flag to indicate that the configuration has changed since Recents last launched
295         launchedHasConfigurationChanged = true;
296     }
297 
298     /** Returns whether the status bar scrim should be animated when shown for the first time. */
shouldAnimateStatusBarScrim()299     public boolean shouldAnimateStatusBarScrim() {
300         return launchedFromHome;
301     }
302 
303     /** Returns whether the status bar scrim should be visible. */
hasStatusBarScrim()304     public boolean hasStatusBarScrim() {
305         return !launchedWithNoRecentTasks;
306     }
307 
308     /** Returns whether the nav bar scrim should be animated when shown for the first time. */
shouldAnimateNavBarScrim()309     public boolean shouldAnimateNavBarScrim() {
310         return true;
311     }
312 
313     /** Returns whether the nav bar scrim should be visible. */
hasNavBarScrim()314     public boolean hasNavBarScrim() {
315         // Only show the scrim if we have recent tasks, and if the nav bar is not transposed
316         return !launchedWithNoRecentTasks && (!hasTransposedNavBar || !isLandscape);
317     }
318 
319     /**
320      * Returns the task stack bounds in the current orientation. These bounds do not account for
321      * the system insets.
322      */
getAvailableTaskStackBounds(int windowWidth, int windowHeight, int topInset, int rightInset, Rect searchBarBounds, Rect taskStackBounds)323     public void getAvailableTaskStackBounds(int windowWidth, int windowHeight, int topInset,
324             int rightInset, Rect searchBarBounds, Rect taskStackBounds) {
325         if (isLandscape && hasTransposedSearchBar) {
326             // In landscape, the search bar appears on the left, but we overlay it on top
327             taskStackBounds.set(0, topInset, windowWidth - rightInset, windowHeight);
328         } else {
329             // In portrait, the search bar appears on the top (which already has the inset)
330             taskStackBounds.set(0, searchBarBounds.bottom, windowWidth, windowHeight);
331         }
332     }
333 
334     /**
335      * Returns the search bar bounds in the current orientation.  These bounds do not account for
336      * the system insets.
337      */
getSearchBarBounds(int windowWidth, int windowHeight, int topInset, Rect searchBarSpaceBounds)338     public void getSearchBarBounds(int windowWidth, int windowHeight, int topInset,
339             Rect searchBarSpaceBounds) {
340         // Return empty rects if search is not enabled
341         int searchBarSize = searchBarSpaceHeightPx;
342         if (isLandscape && hasTransposedSearchBar) {
343             // In landscape, the search bar appears on the left
344             searchBarSpaceBounds.set(0, topInset, searchBarSize, windowHeight);
345         } else {
346             // In portrait, the search bar appears on the top
347             searchBarSpaceBounds.set(0, topInset, windowWidth, topInset + searchBarSize);
348         }
349     }
350 }
351