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 android.support.v7.app; 18 19 import android.app.Activity; 20 import android.app.Dialog; 21 import android.content.Context; 22 import android.content.res.Configuration; 23 import android.os.Build; 24 import android.os.Bundle; 25 import android.support.annotation.IdRes; 26 import android.support.annotation.IntDef; 27 import android.support.annotation.LayoutRes; 28 import android.support.annotation.NonNull; 29 import android.support.annotation.Nullable; 30 import android.support.v4.app.FragmentActivity; 31 import android.support.v4.os.BuildCompat; 32 import android.support.v4.view.WindowCompat; 33 import android.support.v7.appcompat.R; 34 import android.support.v7.view.ActionMode; 35 import android.support.v7.widget.Toolbar; 36 import android.util.AttributeSet; 37 import android.util.Log; 38 import android.view.MenuInflater; 39 import android.view.View; 40 import android.view.ViewGroup; 41 import android.view.Window; 42 43 import java.lang.annotation.Retention; 44 import java.lang.annotation.RetentionPolicy; 45 46 /** 47 * This class represents a delegate which you can use to extend AppCompat's support to any 48 * {@link android.app.Activity}. 49 * <p> 50 * When using an {@link AppCompatDelegate}, you should any methods exposed in it rather than the 51 * {@link android.app.Activity} method of the same name. This applies to: 52 * <ul> 53 * <li>{@link #addContentView(android.view.View, android.view.ViewGroup.LayoutParams)}</li> 54 * <li>{@link #setContentView(int)}</li> 55 * <li>{@link #setContentView(android.view.View)}</li> 56 * <li>{@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}</li> 57 * <li>{@link #requestWindowFeature(int)}</li> 58 * <li>{@link #invalidateOptionsMenu()}</li> 59 * <li>{@link #startSupportActionMode(android.support.v7.view.ActionMode.Callback)}</li> 60 * <li>{@link #setSupportActionBar(android.support.v7.widget.Toolbar)}</li> 61 * <li>{@link #getSupportActionBar()}</li> 62 * <li>{@link #getMenuInflater()}</li> 63 * </ul> 64 * There also some Activity lifecycle methods which should be proxied to the delegate: 65 * <ul> 66 * <li>{@link #onCreate(android.os.Bundle)}</li> 67 * <li>{@link #onPostCreate(android.os.Bundle)}</li> 68 * <li>{@link #onConfigurationChanged(android.content.res.Configuration)}</li> 69 * <li>{@link #setTitle(CharSequence)}</li> 70 * <li>{@link #onStop()}</li> 71 * <li>{@link #onDestroy()}</li> 72 * </ul> 73 * <p> 74 * An {@link Activity} can only be linked with one {@link AppCompatDelegate} instance, 75 * so the instance returned from {@link #create(Activity, AppCompatCallback)} should be kept 76 * until the Activity is destroyed. 77 */ 78 public abstract class AppCompatDelegate { 79 80 static final String TAG = "AppCompatDelegate"; 81 82 /** 83 * Mode which means to not use night mode, and therefore prefer {@code notnight} qualified 84 * resources where available, regardless of the time. 85 * 86 * @see #setLocalNightMode(int) 87 */ 88 public static final int MODE_NIGHT_NO = 1; 89 90 /** 91 * Mode which means to always use night mode, and therefore prefer {@code night} qualified 92 * resources where available, regardless of the time. 93 * 94 * @see #setLocalNightMode(int) 95 */ 96 public static final int MODE_NIGHT_YES = 2; 97 98 /** 99 * Mode which means to use night mode when it is determined that it is night or not. 100 * 101 * <p>The calculation used to determine whether it is night or not makes use of the location 102 * APIs (if this app has the necessary permissions). This allows us to generate accurate 103 * sunrise and sunset times. If this app does not have permission to access the location APIs 104 * then we use hardcoded times which will be less accurate.</p> 105 * 106 * @see #setLocalNightMode(int) 107 */ 108 public static final int MODE_NIGHT_AUTO = 0; 109 110 /** 111 * Mode which uses the system's night mode setting to determine if it is night or not. 112 * 113 * @see #setLocalNightMode(int) 114 */ 115 public static final int MODE_NIGHT_FOLLOW_SYSTEM = -1; 116 117 static final int MODE_NIGHT_UNSPECIFIED = -100; 118 119 @NightMode 120 private static int sDefaultNightMode = MODE_NIGHT_FOLLOW_SYSTEM; 121 122 private static boolean sCompatVectorFromResourcesEnabled = false; 123 124 /** @hide */ 125 @IntDef({MODE_NIGHT_NO, MODE_NIGHT_YES, MODE_NIGHT_AUTO, MODE_NIGHT_FOLLOW_SYSTEM, 126 MODE_NIGHT_UNSPECIFIED}) 127 @Retention(RetentionPolicy.SOURCE) 128 public @interface NightMode {} 129 130 @IntDef({MODE_NIGHT_NO, MODE_NIGHT_YES, MODE_NIGHT_FOLLOW_SYSTEM}) 131 @Retention(RetentionPolicy.SOURCE) 132 @interface ApplyableNightMode {} 133 134 /** 135 * Flag for enabling the support Action Bar. 136 * 137 * <p>This is enabled by default for some devices. The Action Bar replaces the title bar and 138 * provides an alternate location for an on-screen menu button on some devices. 139 */ 140 public static final int FEATURE_SUPPORT_ACTION_BAR = 100 + WindowCompat.FEATURE_ACTION_BAR; 141 142 /** 143 * Flag for requesting an support Action Bar that overlays window content. 144 * Normally an Action Bar will sit in the space above window content, but if this 145 * feature is requested along with {@link #FEATURE_SUPPORT_ACTION_BAR} it will be layered over 146 * the window content itself. This is useful if you would like your app to have more control 147 * over how the Action Bar is displayed, such as letting application content scroll beneath 148 * an Action Bar with a transparent background or otherwise displaying a transparent/translucent 149 * Action Bar over application content. 150 * 151 * <p>This mode is especially useful with {@code View.SYSTEM_UI_FLAG_FULLSCREEN}, which allows 152 * you to seamlessly hide the action bar in conjunction with other screen decorations. 153 * When an ActionBar is in this mode it will adjust the insets provided to 154 * {@link View#fitSystemWindows(android.graphics.Rect) View.fitSystemWindows(Rect)} 155 * to include the content covered by the action bar, so you can do layout within 156 * that space. 157 */ 158 public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 159 100 + WindowCompat.FEATURE_ACTION_BAR_OVERLAY; 160 161 /** 162 * Flag for specifying the behavior of action modes when an Action Bar is not present. 163 * If overlay is enabled, the action mode UI will be allowed to cover existing window content. 164 */ 165 public static final int FEATURE_ACTION_MODE_OVERLAY = WindowCompat.FEATURE_ACTION_MODE_OVERLAY; 166 167 /** 168 * Create a {@link android.support.v7.app.AppCompatDelegate} to use with {@code activity}. 169 * 170 * @param callback An optional callback for AppCompat specific events 171 */ create(Activity activity, AppCompatCallback callback)172 public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) { 173 return create(activity, activity.getWindow(), callback); 174 } 175 176 /** 177 * Create a {@link android.support.v7.app.AppCompatDelegate} to use with {@code dialog}. 178 * 179 * @param callback An optional callback for AppCompat specific events 180 */ create(Dialog dialog, AppCompatCallback callback)181 public static AppCompatDelegate create(Dialog dialog, AppCompatCallback callback) { 182 return create(dialog.getContext(), dialog.getWindow(), callback); 183 } 184 create(Context context, Window window, AppCompatCallback callback)185 private static AppCompatDelegate create(Context context, Window window, 186 AppCompatCallback callback) { 187 final int sdk = Build.VERSION.SDK_INT; 188 if (BuildCompat.isAtLeastN()) { 189 return new AppCompatDelegateImplN(context, window, callback); 190 } else if (sdk >= 23) { 191 return new AppCompatDelegateImplV23(context, window, callback); 192 } else if (sdk >= 14) { 193 return new AppCompatDelegateImplV14(context, window, callback); 194 } else if (sdk >= 11) { 195 return new AppCompatDelegateImplV11(context, window, callback); 196 } else { 197 return new AppCompatDelegateImplV7(context, window, callback); 198 } 199 } 200 201 /** 202 * Private constructor 203 */ AppCompatDelegate()204 AppCompatDelegate() {} 205 206 /** 207 * Support library version of {@link Activity#getActionBar}. 208 * 209 * @return AppCompat's action bar, or null if it does not have one. 210 */ 211 @Nullable getSupportActionBar()212 public abstract ActionBar getSupportActionBar(); 213 214 /** 215 * Set a {@link Toolbar} to act as the {@link ActionBar} for this delegate. 216 * 217 * <p>When set to a non-null value the {@link #getSupportActionBar()} ()} method will return 218 * an {@link ActionBar} object that can be used to control the given toolbar as if it were 219 * a traditional window decor action bar. The toolbar's menu will be populated with the 220 * Activity's options menu and the navigation button will be wired through the standard 221 * {@link android.R.id#home home} menu select action.</p> 222 * 223 * <p>In order to use a Toolbar within the Activity's window content the application 224 * must not request the window feature 225 * {@link AppCompatDelegate#FEATURE_SUPPORT_ACTION_BAR FEATURE_SUPPORT_ACTION_BAR}.</p> 226 * 227 * @param toolbar Toolbar to set as the Activity's action bar, or {@code null} to clear it 228 */ setSupportActionBar(@ullable Toolbar toolbar)229 public abstract void setSupportActionBar(@Nullable Toolbar toolbar); 230 231 /** 232 * Return the value of this call from your {@link Activity#getMenuInflater()} 233 */ getMenuInflater()234 public abstract MenuInflater getMenuInflater(); 235 236 /** 237 * Should be called from {@link Activity#onCreate Activity.onCreate()}. 238 * 239 * <p>This should be called before {@code super.onCreate()} as so:</p> 240 * <pre class="prettyprint"> 241 * protected void onCreate(Bundle savedInstanceState) { 242 * getDelegate().onCreate(savedInstanceState); 243 * super.onCreate(savedInstanceState); 244 * // ... 245 * } 246 * </pre> 247 */ onCreate(Bundle savedInstanceState)248 public abstract void onCreate(Bundle savedInstanceState); 249 250 /** 251 * Should be called from {@link Activity#onPostCreate(android.os.Bundle)} 252 */ onPostCreate(Bundle savedInstanceState)253 public abstract void onPostCreate(Bundle savedInstanceState); 254 255 /** 256 * Should be called from 257 * {@link Activity#onConfigurationChanged} 258 */ onConfigurationChanged(Configuration newConfig)259 public abstract void onConfigurationChanged(Configuration newConfig); 260 261 /** 262 * Should be called from {@link Activity#onStop Activity.onStop()} 263 */ onStop()264 public abstract void onStop(); 265 266 /** 267 * Should be called from {@link Activity#onPostResume()} 268 */ onPostResume()269 public abstract void onPostResume(); 270 271 /** 272 * Finds a view that was identified by the id attribute from the XML that 273 * was processed in {@link #onCreate}. 274 * 275 * @return The view if found or null otherwise. 276 */ 277 @Nullable findViewById(@dRes int id)278 public abstract View findViewById(@IdRes int id); 279 280 /** 281 * Should be called instead of {@link Activity#setContentView(android.view.View)}} 282 */ setContentView(View v)283 public abstract void setContentView(View v); 284 285 /** 286 * Should be called instead of {@link Activity#setContentView(int)}} 287 */ setContentView(@ayoutRes int resId)288 public abstract void setContentView(@LayoutRes int resId); 289 290 /** 291 * Should be called instead of 292 * {@link Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}} 293 */ setContentView(View v, ViewGroup.LayoutParams lp)294 public abstract void setContentView(View v, ViewGroup.LayoutParams lp); 295 296 /** 297 * Should be called instead of 298 * {@link Activity#addContentView(android.view.View, android.view.ViewGroup.LayoutParams)}} 299 */ addContentView(View v, ViewGroup.LayoutParams lp)300 public abstract void addContentView(View v, ViewGroup.LayoutParams lp); 301 302 /** 303 * Should be called from {@link Activity#onTitleChanged(CharSequence, int)}} 304 */ setTitle(@ullable CharSequence title)305 public abstract void setTitle(@Nullable CharSequence title); 306 307 /** 308 * Should be called from {@link Activity#invalidateOptionsMenu()}} or 309 * {@link FragmentActivity#supportInvalidateOptionsMenu()}. 310 */ invalidateOptionsMenu()311 public abstract void invalidateOptionsMenu(); 312 313 /** 314 * Should be called from {@link Activity#onDestroy()} 315 */ onDestroy()316 public abstract void onDestroy(); 317 318 /** 319 * Returns an {@link ActionBarDrawerToggle.Delegate} which can be returned from your Activity 320 * if it implements {@link ActionBarDrawerToggle.DelegateProvider}. 321 */ 322 @Nullable getDrawerToggleDelegate()323 public abstract ActionBarDrawerToggle.Delegate getDrawerToggleDelegate(); 324 325 /** 326 * Enable extended window features. This should be called instead of 327 * {@link android.app.Activity#requestWindowFeature(int)} or 328 * {@link android.view.Window#requestFeature getWindow().requestFeature()}. 329 * 330 * @param featureId The desired feature as defined in {@link android.view.Window}. 331 * @return Returns true if the requested feature is supported and now 332 * enabled. 333 */ requestWindowFeature(int featureId)334 public abstract boolean requestWindowFeature(int featureId); 335 336 /** 337 * Query for the availability of a certain feature. 338 * 339 * <p>This should be called instead of {@link android.view.Window#hasFeature(int)}.</p> 340 * 341 * @param featureId The feature ID to check 342 * @return true if the feature is enabled, false otherwise. 343 */ hasWindowFeature(int featureId)344 public abstract boolean hasWindowFeature(int featureId); 345 346 /** 347 * Start an action mode. 348 * 349 * @param callback Callback that will manage lifecycle events for this context mode 350 * @return The ContextMode that was started, or null if it was canceled 351 */ 352 @Nullable startSupportActionMode(@onNull ActionMode.Callback callback)353 public abstract ActionMode startSupportActionMode(@NonNull ActionMode.Callback callback); 354 355 /** 356 * Installs AppCompat's {@link android.view.LayoutInflater} Factory so that it can replace 357 * the framework widgets with compatible tinted versions. This should be called before 358 * {@code super.onCreate()} as so: 359 * <pre class="prettyprint"> 360 * protected void onCreate(Bundle savedInstanceState) { 361 * getDelegate().installViewFactory(); 362 * getDelegate().onCreate(savedInstanceState); 363 * super.onCreate(savedInstanceState); 364 * 365 * // ... 366 * } 367 * </pre> 368 * If you are using your own {@link android.view.LayoutInflater.Factory Factory} or 369 * {@link android.view.LayoutInflater.Factory2 Factory2} then you can omit this call, and instead call 370 * {@link #createView(android.view.View, String, android.content.Context, android.util.AttributeSet)} 371 * from your factory to return any compatible widgets. 372 */ installViewFactory()373 public abstract void installViewFactory(); 374 375 /** 376 * This should be called from a 377 * {@link android.support.v4.view.LayoutInflaterFactory LayoutInflaterFactory} in order 378 * to return tint-aware widgets. 379 * <p> 380 * This is only needed if you are using your own 381 * {@link android.view.LayoutInflater LayoutInflater} factory, and have therefore not 382 * installed the default factory via {@link #installViewFactory()}. 383 */ createView(@ullable View parent, String name, @NonNull Context context, @NonNull AttributeSet attrs)384 public abstract View createView(@Nullable View parent, String name, @NonNull Context context, 385 @NonNull AttributeSet attrs); 386 387 /** 388 * Whether AppCompat handles any native action modes itself. 389 * <p>This methods only takes effect on 390 * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and above. 391 * 392 * @param enabled whether AppCompat should handle native action modes. 393 */ setHandleNativeActionModesEnabled(boolean enabled)394 public abstract void setHandleNativeActionModesEnabled(boolean enabled); 395 396 /** 397 * Returns whether AppCompat handles any native action modes itself. 398 * 399 * @return true if AppCompat should handle native action modes. 400 */ isHandleNativeActionModesEnabled()401 public abstract boolean isHandleNativeActionModesEnabled(); 402 403 /** 404 * Allows AppCompat to save instance state. 405 */ onSaveInstanceState(Bundle outState)406 public abstract void onSaveInstanceState(Bundle outState); 407 408 /** 409 * Allow AppCompat to apply the {@code night} and {@code notnight} resource qualifiers. 410 * 411 * <p>Doing this enables the 412 * {@link R.style#Theme_AppCompat_DayNight Theme.AppCompat.DayNight} 413 * family of themes to work, using the computed twilight to automatically select a dark or 414 * light theme.</p> 415 * 416 * <p>You can override the night mode using {@link #setLocalNightMode(int)}.</p> 417 * 418 * <p>This only works on devices running 419 * {@link Build.VERSION_CODES#ICE_CREAM_SANDWICH ICE_CREAM_SANDWICH} and above.</p> 420 * 421 * @see #setDefaultNightMode(int) 422 * @see #setLocalNightMode(int) 423 * 424 * @return true if the night mode was applied, false if not 425 */ applyDayNight()426 public abstract boolean applyDayNight(); 427 428 /** 429 * Override the night mode used for this delegate's host component. This method only takes 430 * effect for those situtations where {@link #applyDayNight()} works. 431 * 432 * <p>Depending on when this is called, this may not take effect until the next time that 433 * the host component is created. You may use {@link Activity#recreate()} to force a 434 * recreation.</p> 435 * 436 * @see #applyDayNight() 437 */ setLocalNightMode(@ightMode int mode)438 public abstract void setLocalNightMode(@NightMode int mode); 439 440 /** 441 * Sets the default night mode. This is used across all activities/dialogs but can be overriden 442 * locally via {@link #setLocalNightMode(int)}. 443 * 444 * <p>This method only takes effect for those situtations where {@link #applyDayNight()} works. 445 * Defaults to {@link #MODE_NIGHT_NO}.</p> 446 * 447 * @see #setLocalNightMode(int) 448 * @see #getDefaultNightMode() 449 */ setDefaultNightMode(@ightMode int mode)450 public static void setDefaultNightMode(@NightMode int mode) { 451 switch (mode) { 452 case MODE_NIGHT_AUTO: 453 case MODE_NIGHT_NO: 454 case MODE_NIGHT_YES: 455 case MODE_NIGHT_FOLLOW_SYSTEM: 456 sDefaultNightMode = mode; 457 break; 458 default: 459 Log.d(TAG, "setDefaultNightMode() called with an unknown mode"); 460 break; 461 } 462 } 463 464 /** 465 * Returns the default night mode. 466 * 467 * @see #setDefaultNightMode(int) 468 */ 469 @NightMode getDefaultNightMode()470 public static int getDefaultNightMode() { 471 return sDefaultNightMode; 472 } 473 474 /** 475 * Sets whether vector drawables on older platforms (< API 21) can be used within 476 * {@link android.graphics.drawable.DrawableContainer} resources. 477 * 478 * <p>When enabled, AppCompat can intercept some drawable inflation from the framework, which 479 * enables implicit inflation of vector drawables within 480 * {@link android.graphics.drawable.DrawableContainer} resources. You can then use those 481 * drawables in places such as {@code android:src} on {@link android.widget.ImageView}, 482 * or {@code android:drawableLeft} on {@link android.widget.TextView}. Example usage:</p> 483 * 484 * <pre> 485 * <selector xmlns:android="..."> 486 * <item android:state_checked="true" 487 * android:drawable="@drawable/vector_checked_icon" /> 488 * <item android:drawable="@drawable/vector_icon" /> 489 * </selector> 490 * 491 * <TextView 492 * ... 493 * android:drawableLeft="@drawable/vector_state_list_icon" /> 494 * </pre> 495 * 496 * <p>This feature defaults to disabled, since enabling it can cause issues with memory usage, 497 * and problems updating {@link Configuration} instances. If you update the configuration 498 * manually, then you probably do not want to enable this. You have been warned.</p> 499 * 500 * <p>Even with this disabled, you can still use vector resources through 501 * {@link android.support.v7.widget.AppCompatImageView#setImageResource(int)} and it's 502 * {@code app:srcCompat} attribute. They can also be used in anything which AppComapt inflates 503 * for you, such as menu resources.</p> 504 * 505 * <p>Please note: this only takes effect in Activities created after this call.</p> 506 */ setCompatVectorFromResourcesEnabled(boolean enabled)507 public static void setCompatVectorFromResourcesEnabled(boolean enabled) { 508 sCompatVectorFromResourcesEnabled = enabled; 509 } 510 511 /** 512 * Returns whether vector drawables on older platforms (< API 21) can be accessed from within 513 * resources. 514 * 515 * @see #setCompatVectorFromResourcesEnabled(boolean) 516 */ isCompatVectorFromResourcesEnabled()517 public static boolean isCompatVectorFromResourcesEnabled() { 518 return sCompatVectorFromResourcesEnabled; 519 } 520 } 521