• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.content.Intent;
20 import android.content.res.Configuration;
21 import android.content.res.Resources;
22 import android.os.Build;
23 import android.os.Bundle;
24 import android.support.annotation.CallSuper;
25 import android.support.annotation.IdRes;
26 import android.support.annotation.LayoutRes;
27 import android.support.annotation.NonNull;
28 import android.support.annotation.Nullable;
29 import android.support.annotation.StyleRes;
30 import android.support.v4.app.ActivityCompat;
31 import android.support.v4.app.FragmentActivity;
32 import android.support.v4.app.NavUtils;
33 import android.support.v4.app.TaskStackBuilder;
34 import android.support.v4.view.KeyEventCompat;
35 import android.support.v7.view.ActionMode;
36 import android.support.v7.widget.Toolbar;
37 import android.support.v7.widget.VectorEnabledTintResources;
38 import android.util.DisplayMetrics;
39 import android.view.KeyEvent;
40 import android.view.Menu;
41 import android.view.MenuInflater;
42 import android.view.View;
43 import android.view.ViewGroup;
44 
45 /**
46  * Base class for activities that use the
47  * <a href="{@docRoot}tools/extras/support-library.html">support library</a> action bar features.
48  *
49  * <p>You can add an {@link android.support.v7.app.ActionBar} to your activity when running on API level 7 or higher
50  * by extending this class for your activity and setting the activity theme to
51  * {@link android.support.v7.appcompat.R.style#Theme_AppCompat Theme.AppCompat} or a similar theme.
52  *
53  * <div class="special reference">
54  * <h3>Developer Guides</h3>
55  *
56  * <p>For information about how to use the action bar, including how to add action items, navigation
57  * modes and more, read the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action
58  * Bar</a> API guide.</p>
59  * </div>
60  */
61 public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
62         TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
63 
64     private AppCompatDelegate mDelegate;
65     private int mThemeId = 0;
66     private boolean mEatKeyUpEvent;
67     private Resources mResources;
68 
69     @Override
onCreate(@ullable Bundle savedInstanceState)70     protected void onCreate(@Nullable Bundle savedInstanceState) {
71         final AppCompatDelegate delegate = getDelegate();
72         delegate.installViewFactory();
73         delegate.onCreate(savedInstanceState);
74         if (delegate.applyDayNight() && mThemeId != 0) {
75             // If DayNight has been applied, we need to re-apply the theme for
76             // the changes to take effect. On API 23+, we should bypass
77             // setTheme(), which will no-op if the theme ID is identical to the
78             // current theme ID.
79             if (Build.VERSION.SDK_INT >= 23) {
80                 onApplyThemeResource(getTheme(), mThemeId, false);
81             } else {
82                 setTheme(mThemeId);
83             }
84         }
85         super.onCreate(savedInstanceState);
86     }
87 
88     @Override
setTheme(@tyleRes final int resid)89     public void setTheme(@StyleRes final int resid) {
90         super.setTheme(resid);
91         // Keep hold of the theme id so that we can re-set it later if needed
92         mThemeId = resid;
93     }
94 
95     @Override
onPostCreate(@ullable Bundle savedInstanceState)96     protected void onPostCreate(@Nullable Bundle savedInstanceState) {
97         super.onPostCreate(savedInstanceState);
98         getDelegate().onPostCreate(savedInstanceState);
99     }
100 
101     /**
102      * Support library version of {@link android.app.Activity#getActionBar}.
103      *
104      * <p>Retrieve a reference to this activity's ActionBar.
105      *
106      * @return The Activity's ActionBar, or null if it does not have one.
107      */
108     @Nullable
getSupportActionBar()109     public ActionBar getSupportActionBar() {
110         return getDelegate().getSupportActionBar();
111     }
112 
113     /**
114      * Set a {@link android.widget.Toolbar Toolbar} to act as the
115      * {@link android.support.v7.app.ActionBar} for this Activity window.
116      *
117      * <p>When set to a non-null value the {@link #getActionBar()} method will return
118      * an {@link android.support.v7.app.ActionBar} object that can be used to control the given
119      * toolbar as if it were a traditional window decor action bar. The toolbar's menu will be
120      * populated with the Activity's options menu and the navigation button will be wired through
121      * the standard {@link android.R.id#home home} menu select action.</p>
122      *
123      * <p>In order to use a Toolbar within the Activity's window content the application
124      * must not request the window feature
125      * {@link android.view.Window#FEATURE_ACTION_BAR FEATURE_SUPPORT_ACTION_BAR}.</p>
126      *
127      * @param toolbar Toolbar to set as the Activity's action bar, or {@code null} to clear it
128      */
setSupportActionBar(@ullable Toolbar toolbar)129     public void setSupportActionBar(@Nullable Toolbar toolbar) {
130         getDelegate().setSupportActionBar(toolbar);
131     }
132 
133     @Override
getMenuInflater()134     public MenuInflater getMenuInflater() {
135         return getDelegate().getMenuInflater();
136     }
137 
138     @Override
setContentView(@ayoutRes int layoutResID)139     public void setContentView(@LayoutRes int layoutResID) {
140         getDelegate().setContentView(layoutResID);
141     }
142 
143     @Override
setContentView(View view)144     public void setContentView(View view) {
145         getDelegate().setContentView(view);
146     }
147 
148     @Override
setContentView(View view, ViewGroup.LayoutParams params)149     public void setContentView(View view, ViewGroup.LayoutParams params) {
150         getDelegate().setContentView(view, params);
151     }
152 
153     @Override
addContentView(View view, ViewGroup.LayoutParams params)154     public void addContentView(View view, ViewGroup.LayoutParams params) {
155         getDelegate().addContentView(view, params);
156     }
157 
158     @Override
onConfigurationChanged(Configuration newConfig)159     public void onConfigurationChanged(Configuration newConfig) {
160         super.onConfigurationChanged(newConfig);
161         getDelegate().onConfigurationChanged(newConfig);
162         if (mResources != null) {
163             // The real (and thus managed) resources object was already updated
164             // by ResourcesManager, so pull the current metrics from there.
165             final DisplayMetrics newMetrics = super.getResources().getDisplayMetrics();
166             mResources.updateConfiguration(newConfig, newMetrics);
167         }
168     }
169 
170     @Override
onPostResume()171     protected void onPostResume() {
172         super.onPostResume();
173         getDelegate().onPostResume();
174     }
175 
176     @Override
onStart()177     protected void onStart() {
178         super.onStart();
179         getDelegate().onStart();
180     }
181 
182     @Override
onStop()183     protected void onStop() {
184         super.onStop();
185         getDelegate().onStop();
186     }
187 
188     @Override
findViewById(@dRes int id)189     public View findViewById(@IdRes int id) {
190         return getDelegate().findViewById(id);
191     }
192 
193     @Override
onMenuItemSelected(int featureId, android.view.MenuItem item)194     public final boolean onMenuItemSelected(int featureId, android.view.MenuItem item) {
195         if (super.onMenuItemSelected(featureId, item)) {
196             return true;
197         }
198 
199         final ActionBar ab = getSupportActionBar();
200         if (item.getItemId() == android.R.id.home && ab != null &&
201                 (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
202             return onSupportNavigateUp();
203         }
204         return false;
205     }
206 
207     @Override
onDestroy()208     protected void onDestroy() {
209         super.onDestroy();
210         getDelegate().onDestroy();
211     }
212 
213     @Override
onTitleChanged(CharSequence title, int color)214     protected void onTitleChanged(CharSequence title, int color) {
215         super.onTitleChanged(title, color);
216         getDelegate().setTitle(title);
217     }
218 
219     /**
220      * Enable extended support library window features.
221      * <p>
222      * This is a convenience for calling
223      * {@link android.view.Window#requestFeature getWindow().requestFeature()}.
224      * </p>
225      *
226      * @param featureId The desired feature as defined in
227      * {@link android.view.Window} or {@link android.support.v4.view.WindowCompat}.
228      * @return Returns true if the requested feature is supported and now enabled.
229      *
230      * @see android.app.Activity#requestWindowFeature
231      * @see android.view.Window#requestFeature
232      */
supportRequestWindowFeature(int featureId)233     public boolean supportRequestWindowFeature(int featureId) {
234         return getDelegate().requestWindowFeature(featureId);
235     }
236 
237     @Override
supportInvalidateOptionsMenu()238     public void supportInvalidateOptionsMenu() {
239         getDelegate().invalidateOptionsMenu();
240     }
241 
242     /**
243      * @hide
244      */
invalidateOptionsMenu()245     public void invalidateOptionsMenu() {
246         getDelegate().invalidateOptionsMenu();
247     }
248 
249     /**
250      * Notifies the Activity that a support action mode has been started.
251      * Activity subclasses overriding this method should call the superclass implementation.
252      *
253      * @param mode The new action mode.
254      */
255     @Override
256     @CallSuper
onSupportActionModeStarted(@onNull ActionMode mode)257     public void onSupportActionModeStarted(@NonNull ActionMode mode) {
258     }
259 
260     /**
261      * Notifies the activity that a support action mode has finished.
262      * Activity subclasses overriding this method should call the superclass implementation.
263      *
264      * @param mode The action mode that just finished.
265      */
266     @Override
267     @CallSuper
onSupportActionModeFinished(@onNull ActionMode mode)268     public void onSupportActionModeFinished(@NonNull ActionMode mode) {
269     }
270 
271     /**
272      * Called when a support action mode is being started for this window. Gives the
273      * callback an opportunity to handle the action mode in its own unique and
274      * beautiful way. If this method returns null the system can choose a way
275      * to present the mode or choose not to start the mode at all.
276      *
277      * @param callback Callback to control the lifecycle of this action mode
278      * @return The ActionMode that was started, or null if the system should present it
279      */
280     @Nullable
281     @Override
onWindowStartingSupportActionMode(@onNull ActionMode.Callback callback)282     public ActionMode onWindowStartingSupportActionMode(@NonNull ActionMode.Callback callback) {
283         return null;
284     }
285 
286     /**
287      * Start an action mode.
288      *
289      * @param callback Callback that will manage lifecycle events for this context mode
290      * @return The ContextMode that was started, or null if it was canceled
291      */
292     @Nullable
startSupportActionMode(@onNull ActionMode.Callback callback)293     public ActionMode startSupportActionMode(@NonNull ActionMode.Callback callback) {
294         return getDelegate().startSupportActionMode(callback);
295     }
296 
297     /**
298      * @deprecated Progress bars are no longer provided in AppCompat.
299      */
300     @Deprecated
setSupportProgressBarVisibility(boolean visible)301     public void setSupportProgressBarVisibility(boolean visible) {
302     }
303 
304     /**
305      * @deprecated Progress bars are no longer provided in AppCompat.
306      */
307     @Deprecated
setSupportProgressBarIndeterminateVisibility(boolean visible)308     public void setSupportProgressBarIndeterminateVisibility(boolean visible) {
309     }
310 
311     /**
312      * @deprecated Progress bars are no longer provided in AppCompat.
313      */
314     @Deprecated
setSupportProgressBarIndeterminate(boolean indeterminate)315     public void setSupportProgressBarIndeterminate(boolean indeterminate) {
316     }
317 
318     /**
319      * @deprecated Progress bars are no longer provided in AppCompat.
320      */
321     @Deprecated
setSupportProgress(int progress)322     public void setSupportProgress(int progress) {
323     }
324 
325     /**
326      * Support version of {@link #onCreateNavigateUpTaskStack(android.app.TaskStackBuilder)}.
327      * This method will be called on all platform versions.
328      *
329      * Define the synthetic task stack that will be generated during Up navigation from
330      * a different task.
331      *
332      * <p>The default implementation of this method adds the parent chain of this activity
333      * as specified in the manifest to the supplied {@link android.support.v4.app.TaskStackBuilder}. Applications
334      * may choose to override this method to construct the desired task stack in a different
335      * way.</p>
336      *
337      * <p>This method will be invoked by the default implementation of {@link #onNavigateUp()}
338      * if {@link #shouldUpRecreateTask(android.content.Intent)} returns true when supplied with the intent
339      * returned by {@link #getParentActivityIntent()}.</p>
340      *
341      * <p>Applications that wish to supply extra Intent parameters to the parent stack defined
342      * by the manifest should override
343      * {@link #onPrepareSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder)}.</p>
344      *
345      * @param builder An empty TaskStackBuilder - the application should add intents representing
346      *                the desired task stack
347      */
onCreateSupportNavigateUpTaskStack(@onNull TaskStackBuilder builder)348     public void onCreateSupportNavigateUpTaskStack(@NonNull TaskStackBuilder builder) {
349         builder.addParentStack(this);
350     }
351 
352     /**
353      * Support version of {@link #onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder)}.
354      * This method will be called on all platform versions.
355      *
356      * Prepare the synthetic task stack that will be generated during Up navigation
357      * from a different task.
358      *
359      * <p>This method receives the {@link android.support.v4.app.TaskStackBuilder} with the constructed series of
360      * Intents as generated by {@link #onCreateSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder)}.
361      * If any extra data should be added to these intents before launching the new task,
362      * the application should override this method and add that data here.</p>
363      *
364      * @param builder A TaskStackBuilder that has been populated with Intents by
365      *                onCreateNavigateUpTaskStack.
366      */
onPrepareSupportNavigateUpTaskStack(@onNull TaskStackBuilder builder)367     public void onPrepareSupportNavigateUpTaskStack(@NonNull TaskStackBuilder builder) {
368     }
369 
370     /**
371      * This method is called whenever the user chooses to navigate Up within your application's
372      * activity hierarchy from the action bar.
373      *
374      * <p>If a parent was specified in the manifest for this activity or an activity-alias to it,
375      * default Up navigation will be handled automatically. See
376      * {@link #getSupportParentActivityIntent()} for how to specify the parent. If any activity
377      * along the parent chain requires extra Intent arguments, the Activity subclass
378      * should override the method {@link #onPrepareSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder)}
379      * to supply those arguments.</p>
380      *
381      * <p>See <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and
382      * Back Stack</a> from the developer guide and
383      * <a href="{@docRoot}design/patterns/navigation.html">Navigation</a> from the design guide
384      * for more information about navigating within your app.</p>
385      *
386      * <p>See the {@link android.support.v4.app.TaskStackBuilder} class and the Activity methods
387      * {@link #getSupportParentActivityIntent()}, {@link #supportShouldUpRecreateTask(android.content.Intent)}, and
388      * {@link #supportNavigateUpTo(android.content.Intent)} for help implementing custom Up navigation.</p>
389      *
390      * @return true if Up navigation completed successfully and this Activity was finished,
391      *         false otherwise.
392      */
onSupportNavigateUp()393     public boolean onSupportNavigateUp() {
394         Intent upIntent = getSupportParentActivityIntent();
395 
396         if (upIntent != null) {
397             if (supportShouldUpRecreateTask(upIntent)) {
398                 TaskStackBuilder b = TaskStackBuilder.create(this);
399                 onCreateSupportNavigateUpTaskStack(b);
400                 onPrepareSupportNavigateUpTaskStack(b);
401                 b.startActivities();
402 
403                 try {
404                     ActivityCompat.finishAffinity(this);
405                 } catch (IllegalStateException e) {
406                     // This can only happen on 4.1+, when we don't have a parent or a result set.
407                     // In that case we should just finish().
408                     finish();
409                 }
410             } else {
411                 // This activity is part of the application's task, so simply
412                 // navigate up to the hierarchical parent activity.
413                 supportNavigateUpTo(upIntent);
414             }
415             return true;
416         }
417         return false;
418     }
419 
420     /**
421      * Obtain an {@link android.content.Intent} that will launch an explicit target activity
422      * specified by sourceActivity's {@link android.support.v4.app.NavUtils#PARENT_ACTIVITY} &lt;meta-data&gt;
423      * element in the application's manifest. If the device is running
424      * Jellybean or newer, the android:parentActivityName attribute will be preferred
425      * if it is present.
426      *
427      * @return a new Intent targeting the defined parent activity of sourceActivity
428      */
429     @Nullable
430     @Override
getSupportParentActivityIntent()431     public Intent getSupportParentActivityIntent() {
432         return NavUtils.getParentActivityIntent(this);
433     }
434 
435     /**
436      * Returns true if sourceActivity should recreate the task when navigating 'up'
437      * by using targetIntent.
438      *
439      * <p>If this method returns false the app can trivially call
440      * {@link #supportNavigateUpTo(android.content.Intent)} using the same parameters to correctly perform
441      * up navigation. If this method returns false, the app should synthesize a new task stack
442      * by using {@link android.support.v4.app.TaskStackBuilder} or another similar mechanism to perform up navigation.</p>
443      *
444      * @param targetIntent An intent representing the target destination for up navigation
445      * @return true if navigating up should recreate a new task stack, false if the same task
446      *         should be used for the destination
447      */
supportShouldUpRecreateTask(@onNull Intent targetIntent)448     public boolean supportShouldUpRecreateTask(@NonNull Intent targetIntent) {
449         return NavUtils.shouldUpRecreateTask(this, targetIntent);
450     }
451 
452     /**
453      * Navigate from sourceActivity to the activity specified by upIntent, finishing sourceActivity
454      * in the process. upIntent will have the flag {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP} set
455      * by this method, along with any others required for proper up navigation as outlined
456      * in the Android Design Guide.
457      *
458      * <p>This method should be used when performing up navigation from within the same task
459      * as the destination. If up navigation should cross tasks in some cases, see
460      * {@link #supportShouldUpRecreateTask(android.content.Intent)}.</p>
461      *
462      * @param upIntent An intent representing the target destination for up navigation
463      */
supportNavigateUpTo(@onNull Intent upIntent)464     public void supportNavigateUpTo(@NonNull Intent upIntent) {
465         NavUtils.navigateUpTo(this, upIntent);
466     }
467 
468     @Override
onContentChanged()469     public void onContentChanged() {
470         // Call onSupportContentChanged() for legacy reasons
471         onSupportContentChanged();
472     }
473 
474     /**
475      * @deprecated Use {@link #onContentChanged()} instead.
476      */
477     @Deprecated
onSupportContentChanged()478     public void onSupportContentChanged() {
479     }
480 
481     @Nullable
482     @Override
getDrawerToggleDelegate()483     public ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() {
484         return getDelegate().getDrawerToggleDelegate();
485     }
486 
487     /**
488      * {@inheritDoc}
489      *
490      * <p>Please note: AppCompat uses it's own feature id for the action bar:
491      * {@link AppCompatDelegate#FEATURE_SUPPORT_ACTION_BAR FEATURE_SUPPORT_ACTION_BAR}.</p>
492      */
493     @Override
onMenuOpened(int featureId, Menu menu)494     public boolean onMenuOpened(int featureId, Menu menu) {
495         return super.onMenuOpened(featureId, menu);
496     }
497 
498     /**
499      * {@inheritDoc}
500      *
501      * <p>Please note: AppCompat uses it's own feature id for the action bar:
502      * {@link AppCompatDelegate#FEATURE_SUPPORT_ACTION_BAR FEATURE_SUPPORT_ACTION_BAR}.</p>
503      */
504     @Override
onPanelClosed(int featureId, Menu menu)505     public void onPanelClosed(int featureId, Menu menu) {
506         super.onPanelClosed(featureId, menu);
507     }
508 
509     @Override
onSaveInstanceState(Bundle outState)510     protected void onSaveInstanceState(Bundle outState) {
511         super.onSaveInstanceState(outState);
512         getDelegate().onSaveInstanceState(outState);
513     }
514 
515     /**
516      * @return The {@link AppCompatDelegate} being used by this Activity.
517      */
518     @NonNull
getDelegate()519     public AppCompatDelegate getDelegate() {
520         if (mDelegate == null) {
521             mDelegate = AppCompatDelegate.create(this, this);
522         }
523         return mDelegate;
524     }
525 
526     @Override
dispatchKeyEvent(KeyEvent event)527     public boolean dispatchKeyEvent(KeyEvent event) {
528         if (KeyEventCompat.isCtrlPressed(event) &&
529                 event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK) == '<') {
530             // Capture the Control-< and send focus to the ActionBar
531             final int action = event.getAction();
532             if (action == KeyEvent.ACTION_DOWN) {
533                 final ActionBar actionBar = getSupportActionBar();
534                 if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) {
535                     mEatKeyUpEvent = true;
536                     return true;
537                 }
538             } else if (action == KeyEvent.ACTION_UP && mEatKeyUpEvent) {
539                 mEatKeyUpEvent = false;
540                 return true;
541             }
542         }
543         return super.dispatchKeyEvent(event);
544     }
545 
546     @Override
getResources()547     public Resources getResources() {
548         if (mResources == null && VectorEnabledTintResources.shouldBeUsed()) {
549             mResources = new VectorEnabledTintResources(this, super.getResources());
550         }
551         return mResources == null ? super.getResources() : mResources;
552     }
553 }
554