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