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.SharedPreferences;
22 import android.content.res.Configuration;
23 import android.content.res.Resources;
24 import android.graphics.Rect;
25 import android.provider.Settings;
26 import android.util.DisplayMetrics;
27 import android.util.TypedValue;
28 import android.view.animation.AnimationUtils;
29 import android.view.animation.Interpolator;
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     /** Animations */
54     public float animationPxMovementPerSecond;
55 
56     /** Interpolators */
57     public Interpolator fastOutSlowInInterpolator;
58     public Interpolator fastOutLinearInInterpolator;
59     public Interpolator linearOutSlowInInterpolator;
60     public Interpolator quintOutInterpolator;
61 
62     /** Filtering */
63     public int filteringCurrentViewsAnimDuration;
64     public int filteringNewViewsAnimDuration;
65 
66     /** Insets */
67     public Rect systemInsets = new Rect();
68     public Rect displayRect = new Rect();
69 
70     /** Layout */
71     boolean isLandscape;
72     boolean hasTransposedSearchBar;
73     boolean hasTransposedNavBar;
74 
75     /** Loading */
76     public int maxNumTasksToLoad;
77 
78     /** Search bar */
79     int searchBarAppWidgetId = -1;
80     public int searchBarSpaceHeightPx;
81 
82     /** Task stack */
83     public int taskStackScrollDuration;
84     public int taskStackMaxDim;
85     public int taskStackTopPaddingPx;
86     public float taskStackWidthPaddingPct;
87     public float taskStackOverscrollPct;
88 
89     /** Transitions */
90     public int transitionEnterFromAppDelay;
91     public int transitionEnterFromHomeDelay;
92 
93     /** Task view animation and styles */
94     public int taskViewEnterFromAppDuration;
95     public int taskViewEnterFromHomeDuration;
96     public int taskViewEnterFromHomeStaggerDelay;
97     public int taskViewExitToAppDuration;
98     public int taskViewExitToHomeDuration;
99     public int taskViewRemoveAnimDuration;
100     public int taskViewRemoveAnimTranslationXPx;
101     public int taskViewTranslationZMinPx;
102     public int taskViewTranslationZMaxPx;
103     public int taskViewRoundedCornerRadiusPx;
104     public int taskViewHighlightPx;
105     public int taskViewAffiliateGroupEnterOffsetPx;
106     public float taskViewThumbnailAlpha;
107 
108     /** Task bar colors */
109     public int taskBarViewDefaultBackgroundColor;
110     public int taskBarViewLightTextColor;
111     public int taskBarViewDarkTextColor;
112     public int taskBarViewHighlightColor;
113     public float taskBarViewAffiliationColorMinAlpha;
114 
115     /** Task bar size & animations */
116     public int taskBarHeight;
117     public int taskBarDismissDozeDelaySeconds;
118 
119     /** Nav bar scrim */
120     public int navBarScrimEnterDuration;
121 
122     /** Launch states */
123     public boolean launchedWithAltTab;
124     public boolean launchedWithNoRecentTasks;
125     public boolean launchedFromAppWithThumbnail;
126     public boolean launchedFromHome;
127     public boolean launchedFromSearchHome;
128     public boolean launchedReuseTaskStackViews;
129     public boolean launchedHasConfigurationChanged;
130     public int launchedToTaskId;
131     public int launchedNumVisibleTasks;
132     public int launchedNumVisibleThumbnails;
133 
134     /** Misc **/
135     public boolean useHardwareLayers;
136     public int altTabKeyDelay;
137     public boolean fakeShadows;
138 
139     /** Dev options and global settings */
140     public boolean lockToAppEnabled;
141     public boolean developerOptionsEnabled;
142     public boolean debugModeEnabled;
143     public int svelteLevel;
144 
145     /** Private constructor */
RecentsConfiguration(Context context)146     private RecentsConfiguration(Context context) {
147         // Properties that don't have to be reloaded with each configuration change can be loaded
148         // here.
149 
150         // Interpolators
151         fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
152                 com.android.internal.R.interpolator.fast_out_slow_in);
153         fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
154                 com.android.internal.R.interpolator.fast_out_linear_in);
155         linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
156                 com.android.internal.R.interpolator.linear_out_slow_in);
157         quintOutInterpolator = AnimationUtils.loadInterpolator(context,
158                 com.android.internal.R.interpolator.decelerate_quint);
159     }
160 
161     /** Updates the configuration to the current context */
reinitialize(Context context, SystemServicesProxy ssp)162     public static RecentsConfiguration reinitialize(Context context, SystemServicesProxy ssp) {
163         if (sInstance == null) {
164             sInstance = new RecentsConfiguration(context);
165         }
166         int configHashCode = context.getResources().getConfiguration().hashCode();
167         if (sPrevConfigurationHashCode != configHashCode) {
168             sInstance.update(context);
169             sPrevConfigurationHashCode = configHashCode;
170         }
171         sInstance.updateOnReinitialize(context, ssp);
172         return sInstance;
173     }
174 
175     /** Returns the current recents configuration */
getInstance()176     public static RecentsConfiguration getInstance() {
177         return sInstance;
178     }
179 
180     /** Updates the state, given the specified context */
update(Context context)181     void update(Context context) {
182         SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
183         Resources res = context.getResources();
184         DisplayMetrics dm = res.getDisplayMetrics();
185 
186         // Debug mode
187         debugModeEnabled = settings.getBoolean(Constants.Values.App.Key_DebugModeEnabled, false);
188         if (debugModeEnabled) {
189             Console.Enabled = true;
190         }
191 
192         // Layout
193         isLandscape = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
194         hasTransposedSearchBar = res.getBoolean(R.bool.recents_has_transposed_search_bar);
195         hasTransposedNavBar = res.getBoolean(R.bool.recents_has_transposed_nav_bar);
196 
197         // Insets
198         displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
199 
200         // Animations
201         animationPxMovementPerSecond =
202                 res.getDimensionPixelSize(R.dimen.recents_animation_movement_in_dps_per_second);
203 
204         // Filtering
205         filteringCurrentViewsAnimDuration =
206                 res.getInteger(R.integer.recents_filter_animate_current_views_duration);
207         filteringNewViewsAnimDuration =
208                 res.getInteger(R.integer.recents_filter_animate_new_views_duration);
209 
210         // Loading
211         maxNumTasksToLoad = ActivityManager.getMaxRecentTasksStatic();
212 
213         // Search Bar
214         searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
215         searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1);
216 
217         // Task stack
218         taskStackScrollDuration =
219                 res.getInteger(R.integer.recents_animate_task_stack_scroll_duration);
220         TypedValue widthPaddingPctValue = new TypedValue();
221         res.getValue(R.dimen.recents_stack_width_padding_percentage, widthPaddingPctValue, true);
222         taskStackWidthPaddingPct = widthPaddingPctValue.getFloat();
223         TypedValue stackOverscrollPctValue = new TypedValue();
224         res.getValue(R.dimen.recents_stack_overscroll_percentage, stackOverscrollPctValue, true);
225         taskStackOverscrollPct = stackOverscrollPctValue.getFloat();
226         taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
227         taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding);
228 
229         // Transition
230         transitionEnterFromAppDelay =
231                 res.getInteger(R.integer.recents_enter_from_app_transition_duration);
232         transitionEnterFromHomeDelay =
233                 res.getInteger(R.integer.recents_enter_from_home_transition_duration);
234 
235         // Task view animation and styles
236         taskViewEnterFromAppDuration =
237                 res.getInteger(R.integer.recents_task_enter_from_app_duration);
238         taskViewEnterFromHomeDuration =
239                 res.getInteger(R.integer.recents_task_enter_from_home_duration);
240         taskViewEnterFromHomeStaggerDelay =
241                 res.getInteger(R.integer.recents_task_enter_from_home_stagger_delay);
242         taskViewExitToAppDuration =
243                 res.getInteger(R.integer.recents_task_exit_to_app_duration);
244         taskViewExitToHomeDuration =
245                 res.getInteger(R.integer.recents_task_exit_to_home_duration);
246         taskViewRemoveAnimDuration =
247                 res.getInteger(R.integer.recents_animate_task_view_remove_duration);
248         taskViewRemoveAnimTranslationXPx =
249                 res.getDimensionPixelSize(R.dimen.recents_task_view_remove_anim_translation_x);
250         taskViewRoundedCornerRadiusPx =
251                 res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
252         taskViewHighlightPx = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
253         taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
254         taskViewTranslationZMaxPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
255         taskViewAffiliateGroupEnterOffsetPx =
256                 res.getDimensionPixelSize(R.dimen.recents_task_view_affiliate_group_enter_offset);
257         TypedValue thumbnailAlphaValue = new TypedValue();
258         res.getValue(R.dimen.recents_task_view_thumbnail_alpha, thumbnailAlphaValue, true);
259         taskViewThumbnailAlpha = thumbnailAlphaValue.getFloat();
260 
261         // Task bar colors
262         taskBarViewDefaultBackgroundColor =
263                 res.getColor(R.color.recents_task_bar_default_background_color);
264         taskBarViewLightTextColor =
265                 res.getColor(R.color.recents_task_bar_light_text_color);
266         taskBarViewDarkTextColor =
267                 res.getColor(R.color.recents_task_bar_dark_text_color);
268         taskBarViewHighlightColor =
269                 res.getColor(R.color.recents_task_bar_highlight_color);
270         TypedValue affMinAlphaPctValue = new TypedValue();
271         res.getValue(R.dimen.recents_task_affiliation_color_min_alpha_percentage, affMinAlphaPctValue, true);
272         taskBarViewAffiliationColorMinAlpha = affMinAlphaPctValue.getFloat();
273 
274         // Task bar size & animations
275         taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
276         taskBarDismissDozeDelaySeconds =
277                 res.getInteger(R.integer.recents_task_bar_dismiss_delay_seconds);
278 
279         // Nav bar scrim
280         navBarScrimEnterDuration =
281                 res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
282 
283         // Misc
284         useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers);
285         altTabKeyDelay = res.getInteger(R.integer.recents_alt_tab_key_delay);
286         fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
287         svelteLevel = res.getInteger(R.integer.recents_svelte_level);
288     }
289 
290     /** Updates the system insets */
updateSystemInsets(Rect insets)291     public void updateSystemInsets(Rect insets) {
292         systemInsets.set(insets);
293     }
294 
295     /** Updates the search bar app widget */
updateSearchBarAppWidgetId(Context context, int appWidgetId)296     public void updateSearchBarAppWidgetId(Context context, int appWidgetId) {
297         searchBarAppWidgetId = appWidgetId;
298         SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
299         settings.edit().putInt(Constants.Values.App.Key_SearchAppWidgetId,
300                 appWidgetId).apply();
301     }
302 
303     /** Updates the states that need to be re-read whenever we re-initialize. */
updateOnReinitialize(Context context, SystemServicesProxy ssp)304     void updateOnReinitialize(Context context, SystemServicesProxy ssp) {
305         // Check if the developer options are enabled
306         developerOptionsEnabled = ssp.getGlobalSetting(context,
307                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED) != 0;
308         lockToAppEnabled = ssp.getSystemSetting(context,
309                 Settings.System.LOCK_TO_APP_ENABLED) != 0;
310     }
311 
312     /** Called when the configuration has changed, and we want to reset any configuration specific
313      * members. */
updateOnConfigurationChange()314     public void updateOnConfigurationChange() {
315         // Reset this flag on configuration change to ensure that we recreate new task views
316         launchedReuseTaskStackViews = false;
317         // Set this flag to indicate that the configuration has changed since Recents last launched
318         launchedHasConfigurationChanged = true;
319     }
320 
321     /** Returns whether the search bar app widget exists. */
hasSearchBarAppWidget()322     public boolean hasSearchBarAppWidget() {
323         return searchBarAppWidgetId >= 0;
324     }
325 
326     /** Returns whether the status bar scrim should be animated when shown for the first time. */
shouldAnimateStatusBarScrim()327     public boolean shouldAnimateStatusBarScrim() {
328         return launchedFromHome;
329     }
330 
331     /** Returns whether the status bar scrim should be visible. */
hasStatusBarScrim()332     public boolean hasStatusBarScrim() {
333         return !launchedWithNoRecentTasks;
334     }
335 
336     /** Returns whether the nav bar scrim should be animated when shown for the first time. */
shouldAnimateNavBarScrim()337     public boolean shouldAnimateNavBarScrim() {
338         return true;
339     }
340 
341     /** Returns whether the nav bar scrim should be visible. */
hasNavBarScrim()342     public boolean hasNavBarScrim() {
343         // Only show the scrim if we have recent tasks, and if the nav bar is not transposed
344         return !launchedWithNoRecentTasks && (!hasTransposedNavBar || !isLandscape);
345     }
346 
347     /** Returns whether the current layout is horizontal. */
hasHorizontalLayout()348     public boolean hasHorizontalLayout() {
349         return isLandscape && hasTransposedSearchBar;
350     }
351 
352     /**
353      * Returns the task stack bounds in the current orientation. These bounds do not account for
354      * the system insets.
355      */
getTaskStackBounds(int windowWidth, int windowHeight, int topInset, int rightInset, Rect taskStackBounds)356     public void getTaskStackBounds(int windowWidth, int windowHeight, int topInset, int rightInset,
357                                    Rect taskStackBounds) {
358         Rect searchBarBounds = new Rect();
359         getSearchBarBounds(windowWidth, windowHeight, topInset, searchBarBounds);
360         if (isLandscape && hasTransposedSearchBar) {
361             // In landscape, the search bar appears on the left, but we overlay it on top
362             taskStackBounds.set(0, topInset, windowWidth - rightInset, windowHeight);
363         } else {
364             // In portrait, the search bar appears on the top (which already has the inset)
365             taskStackBounds.set(0, searchBarBounds.bottom, windowWidth, windowHeight);
366         }
367     }
368 
369     /**
370      * Returns the search bar bounds in the current orientation.  These bounds do not account for
371      * the system insets.
372      */
getSearchBarBounds(int windowWidth, int windowHeight, int topInset, Rect searchBarSpaceBounds)373     public void getSearchBarBounds(int windowWidth, int windowHeight, int topInset,
374                                    Rect searchBarSpaceBounds) {
375         // Return empty rects if search is not enabled
376         int searchBarSize = searchBarSpaceHeightPx;
377         if (!Constants.DebugFlags.App.EnableSearchLayout || !hasSearchBarAppWidget()) {
378             searchBarSize = 0;
379         }
380 
381         if (isLandscape && hasTransposedSearchBar) {
382             // In landscape, the search bar appears on the left
383             searchBarSpaceBounds.set(0, topInset, searchBarSize, windowHeight);
384         } else {
385             // In portrait, the search bar appears on the top
386             searchBarSpaceBounds.set(0, topInset, windowWidth, topInset + searchBarSize);
387         }
388     }
389 }
390