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