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