1/*
2 * Copyright (C) 2013 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
17package android.support.v7.app;
18
19import android.content.Context;
20import android.content.pm.ActivityInfo;
21import android.content.pm.PackageManager;
22import android.content.res.Configuration;
23import android.content.res.TypedArray;
24import android.graphics.drawable.Drawable;
25import android.os.Build;
26import android.os.Bundle;
27import android.support.annotation.NonNull;
28import android.support.v4.app.ActionBarDrawerToggle;
29import android.support.v7.appcompat.R;
30import android.support.v7.internal.app.WindowCallback;
31import android.support.v7.internal.view.SupportMenuInflater;
32import android.support.v7.internal.widget.TintTypedArray;
33import android.support.v7.view.ActionMode;
34import android.support.v7.widget.Toolbar;
35import android.util.AttributeSet;
36import android.util.Log;
37import android.view.KeyEvent;
38import android.view.Menu;
39import android.view.MenuInflater;
40import android.view.MenuItem;
41import android.view.View;
42import android.view.ViewGroup;
43
44abstract class ActionBarActivityDelegate {
45
46    static final String METADATA_UI_OPTIONS = "android.support.UI_OPTIONS";
47
48    private static final String TAG = "ActionBarActivityDelegate";
49
50    static ActionBarActivityDelegate createDelegate(ActionBarActivity activity) {
51        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
52            return new ActionBarActivityDelegateHC(activity);
53        } else {
54            return new ActionBarActivityDelegateBase(activity);
55        }
56    }
57
58    final ActionBarActivity mActivity;
59
60    private ActionBar mActionBar;
61    private MenuInflater mMenuInflater;
62
63    // true if this activity has an action bar.
64    boolean mHasActionBar;
65    // true if this activity's action bar overlays other activity content.
66    boolean mOverlayActionBar;
67    // true if this any action modes should overlay the activity content
68    boolean mOverlayActionMode;
69    // true if this activity is floating (e.g. Dialog)
70    boolean mIsFloating;
71    // The default window callback
72    final WindowCallback mDefaultWindowCallback = new WindowCallback() {
73        @Override
74        public boolean onMenuItemSelected(int featureId, MenuItem menuItem) {
75            return mActivity.onMenuItemSelected(featureId, menuItem);
76        }
77
78        @Override
79        public boolean onCreatePanelMenu(int featureId, Menu menu) {
80            return mActivity.superOnCreatePanelMenu(featureId, menu);
81        }
82
83        @Override
84        public boolean onPreparePanel(int featureId, View menuView, Menu menu) {
85            return mActivity.superOnPreparePanel(featureId, menuView, menu);
86        }
87
88        @Override
89        public void onPanelClosed(int featureId, Menu menu) {
90            mActivity.onPanelClosed(featureId, menu);
91        }
92
93        @Override
94        public boolean onMenuOpened(int featureId, Menu menu) {
95            return mActivity.onMenuOpened(featureId, menu);
96        }
97
98        @Override
99        public ActionMode startActionMode(ActionMode.Callback callback) {
100            return startSupportActionModeFromWindow(callback);
101        }
102
103        @Override
104        public View onCreatePanelView(int featureId) {
105            return mActivity.onCreatePanelView(featureId);
106        }
107    };
108    // The fake window callback we're currently using
109    private WindowCallback mWindowCallback;
110    private boolean mIsDestroyed;
111
112    ActionBarActivityDelegate(ActionBarActivity activity) {
113        mActivity = activity;
114        mWindowCallback = mDefaultWindowCallback;
115    }
116
117    abstract ActionBar createSupportActionBar();
118
119    final ActionBar getSupportActionBar() {
120        // The Action Bar should be lazily created as hasActionBar
121        // could change after onCreate
122        if (mHasActionBar) {
123            if (mActionBar == null) {
124                mActionBar = createSupportActionBar();
125            }
126        }
127        return mActionBar;
128    }
129
130    final ActionBar peekSupportActionBar() {
131        return mActionBar;
132    }
133
134    protected final void setSupportActionBar(ActionBar actionBar) {
135        mActionBar = actionBar;
136    }
137
138    abstract void setSupportActionBar(Toolbar toolbar);
139
140    MenuInflater getMenuInflater() {
141        if (mMenuInflater == null) {
142            mMenuInflater = new SupportMenuInflater(getActionBarThemedContext());
143        }
144        return mMenuInflater;
145    }
146
147    void onCreate(Bundle savedInstanceState) {
148        TypedArray a = mActivity.obtainStyledAttributes(R.styleable.Theme);
149
150        if (!a.hasValue(R.styleable.Theme_windowActionBar)) {
151            a.recycle();
152            throw new IllegalStateException(
153                    "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
154        }
155
156        if (a.getBoolean(R.styleable.Theme_windowActionBar, false)) {
157            mHasActionBar = true;
158        }
159        if (a.getBoolean(R.styleable.Theme_windowActionBarOverlay, false)) {
160            mOverlayActionBar = true;
161        }
162        if (a.getBoolean(R.styleable.Theme_windowActionModeOverlay, false)) {
163            mOverlayActionMode = true;
164        }
165        mIsFloating = a.getBoolean(R.styleable.Theme_android_windowIsFloating, false);
166        a.recycle();
167    }
168
169    abstract void onPostCreate(Bundle savedInstanceState);
170
171    abstract void onConfigurationChanged(Configuration newConfig);
172
173    abstract void onStop();
174
175    abstract void onPostResume();
176
177    abstract void setContentView(View v);
178
179    abstract void setContentView(int resId);
180
181    abstract void setContentView(View v, ViewGroup.LayoutParams lp);
182
183    abstract void addContentView(View v, ViewGroup.LayoutParams lp);
184
185    abstract void onTitleChanged(CharSequence title);
186
187    abstract void supportInvalidateOptionsMenu();
188
189    abstract boolean supportRequestWindowFeature(int featureId);
190
191    // Methods used to create and respond to options menu
192    abstract boolean onPreparePanel(int featureId, View view, Menu menu);
193
194    abstract void onPanelClosed(int featureId, Menu menu);
195
196    abstract boolean onMenuOpened(int featureId, Menu menu);
197
198    boolean onPrepareOptionsPanel(View view, Menu menu) {
199        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
200            // Call straight through to onPrepareOptionsMenu, bypassing super.onPreparePanel().
201            // This is because Activity.onPreparePanel() on <v4.1 calls menu.hasVisibleItems(),
202            // which interferes with the initially invisible items.
203            return mActivity.onPrepareOptionsMenu(menu);
204        }
205        return mActivity.superOnPrepareOptionsPanel(view, menu);
206    }
207
208    abstract boolean onCreatePanelMenu(int featureId, Menu menu);
209
210    abstract boolean onBackPressed();
211
212    abstract ActionMode startSupportActionMode(ActionMode.Callback callback);
213
214    abstract void setSupportProgressBarVisibility(boolean visible);
215
216    abstract void setSupportProgressBarIndeterminateVisibility(boolean visible);
217
218    abstract void setSupportProgressBarIndeterminate(boolean indeterminate);
219
220    abstract void setSupportProgress(int progress);
221
222    abstract boolean dispatchKeyEvent(KeyEvent event);
223
224    abstract boolean onKeyShortcut(int keyCode, KeyEvent event);
225
226    final ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() {
227        return new ActionBarDrawableToggleImpl();
228    }
229
230    final android.support.v7.app.ActionBarDrawerToggle.Delegate getV7DrawerToggleDelegate() {
231        return new ActionBarDrawableToggleImpl();
232    }
233
234    abstract int getHomeAsUpIndicatorAttrId();
235
236    abstract void onContentChanged();
237
238    final String getUiOptionsFromMetadata() {
239        try {
240            PackageManager pm = mActivity.getPackageManager();
241            ActivityInfo info = pm.getActivityInfo(mActivity.getComponentName(),
242                    PackageManager.GET_META_DATA);
243
244            String uiOptions = null;
245            if (info.metaData != null) {
246                uiOptions = info.metaData.getString(METADATA_UI_OPTIONS);
247            }
248            return uiOptions;
249        } catch (PackageManager.NameNotFoundException e) {
250            Log.e(TAG, "getUiOptionsFromMetadata: Activity '" + mActivity.getClass()
251                    .getSimpleName() + "' not in manifest");
252            return null;
253        }
254    }
255
256    protected final Context getActionBarThemedContext() {
257        Context context = null;
258
259        // If we have an action bar, let it return a themed context
260        ActionBar ab = getSupportActionBar();
261        if (ab != null) {
262            context = ab.getThemedContext();
263        }
264
265        if (context == null) {
266            context = mActivity;
267        }
268        return context;
269    }
270
271    abstract View createView(String name, @NonNull Context context, @NonNull AttributeSet attrs);
272
273    private class ActionBarDrawableToggleImpl implements
274            android.support.v7.app.ActionBarDrawerToggle.Delegate,
275            ActionBarDrawerToggle.Delegate {
276        @Override
277        public Drawable getThemeUpIndicator() {
278            final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
279                    getActionBarThemedContext(), null, new int[]{ getHomeAsUpIndicatorAttrId() });
280            final Drawable result = a.getDrawable(0);
281            a.recycle();
282            return result;
283        }
284
285        @Override
286        public Context getActionBarThemedContext() {
287            return ActionBarActivityDelegate.this.getActionBarThemedContext();
288        }
289
290        @Override
291        public boolean isNavigationVisible() {
292            final ActionBar ab = getSupportActionBar();
293            return ab != null && (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0;
294        }
295
296        @Override
297        public void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
298            ActionBar ab = getSupportActionBar();
299            if (ab != null) {
300                ab.setHomeAsUpIndicator(upDrawable);
301                ab.setHomeActionContentDescription(contentDescRes);
302            }
303        }
304
305        @Override
306        public void setActionBarDescription(int contentDescRes) {
307            ActionBar ab = getSupportActionBar();
308            if (ab != null) {
309                ab.setHomeActionContentDescription(contentDescRes);
310            }
311        }
312    }
313
314    abstract ActionMode startSupportActionModeFromWindow(ActionMode.Callback callback);
315
316    final void setWindowCallback(WindowCallback callback) {
317        if (callback == null) {
318            throw new IllegalArgumentException("callback can not be null");
319        }
320        mWindowCallback = callback;
321    }
322
323    final WindowCallback getWindowCallback() {
324        return mWindowCallback;
325    }
326
327    final void destroy() {
328        mIsDestroyed = true;
329    }
330
331    final boolean isDestroyed() {
332        return mIsDestroyed;
333    }
334}
335