include actionbarsherlock as a part of owncloud application
[pub/Android/ownCloud.git] / actionbarsherlock / src / com / actionbarsherlock / internal / ActionBarSherlockCompat.java
1 package com.actionbarsherlock.internal;
2
3 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
4 import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean;
5 import java.util.ArrayList;
6 import java.util.HashMap;
7 import java.util.List;
8 import org.xmlpull.v1.XmlPullParser;
9 import android.app.Activity;
10 import android.content.Context;
11 import android.content.pm.ActivityInfo;
12 import android.content.res.AssetManager;
13 import android.content.res.Configuration;
14 import android.content.res.Resources;
15 import android.content.res.TypedArray;
16 import android.content.res.XmlResourceParser;
17 import android.os.Bundle;
18 import android.util.AndroidRuntimeException;
19 import android.util.Log;
20 import android.util.TypedValue;
21 import android.view.ContextThemeWrapper;
22 import android.view.KeyCharacterMap;
23 import android.view.KeyEvent;
24 import android.view.View;
25 import android.view.ViewGroup;
26 import android.view.ViewStub;
27 import android.view.Window;
28 import android.view.accessibility.AccessibilityEvent;
29 import android.view.animation.Animation;
30 import android.view.animation.AnimationUtils;
31 import android.widget.FrameLayout;
32 import android.widget.TextView;
33 import com.actionbarsherlock.ActionBarSherlock;
34 import com.actionbarsherlock.R;
35 import com.actionbarsherlock.app.ActionBar;
36 import com.actionbarsherlock.internal.app.ActionBarImpl;
37 import com.actionbarsherlock.internal.view.StandaloneActionMode;
38 import com.actionbarsherlock.internal.view.menu.ActionMenuPresenter;
39 import com.actionbarsherlock.internal.view.menu.MenuBuilder;
40 import com.actionbarsherlock.internal.view.menu.MenuItemImpl;
41 import com.actionbarsherlock.internal.view.menu.MenuPresenter;
42 import com.actionbarsherlock.internal.widget.ActionBarContainer;
43 import com.actionbarsherlock.internal.widget.ActionBarContextView;
44 import com.actionbarsherlock.internal.widget.ActionBarView;
45 import com.actionbarsherlock.internal.widget.IcsProgressBar;
46 import com.actionbarsherlock.view.ActionMode;
47 import com.actionbarsherlock.view.Menu;
48 import com.actionbarsherlock.view.MenuItem;
49
50 @ActionBarSherlock.Implementation(api = 7)
51 public class ActionBarSherlockCompat extends ActionBarSherlock implements MenuBuilder.Callback, com.actionbarsherlock.view.Window.Callback, MenuPresenter.Callback, android.view.MenuItem.OnMenuItemClickListener {
52 /** Window features which are enabled by default. */
53 protected static final int DEFAULT_FEATURES = 0;
54
55 static private final String PANELS_TAG = "sherlock:Panels";
56
57 public ActionBarSherlockCompat(Activity activity, int flags) {
58 super(activity, flags);
59 }
60
61
62 ///////////////////////////////////////////////////////////////////////////
63 // Properties
64 ///////////////////////////////////////////////////////////////////////////
65
66 /** Whether or not the device has a dedicated menu key button. */
67 private boolean mReserveOverflow;
68 /** Lazy-load indicator for {@link #mReserveOverflow}. */
69 private boolean mReserveOverflowSet = false;
70
71 /** Current menu instance for managing action items. */
72 private MenuBuilder mMenu;
73 /** Map between native options items and sherlock items. */
74 protected HashMap<android.view.MenuItem, MenuItemImpl> mNativeItemMap;
75
76 /** Parent view of the window decoration (action bar, mode, etc.). */
77 private ViewGroup mDecor;
78 /** Parent view of the activity content. */
79 private ViewGroup mContentParent;
80
81 /** Whether or not the title is stable and can be displayed. */
82 private boolean mIsTitleReady = false;
83 /** Whether or not the parent activity has been destroyed. */
84 private boolean mIsDestroyed = false;
85
86 /* Emulate PanelFeatureState */
87 private boolean mClosingActionMenu;
88 private boolean mMenuIsPrepared;
89 private boolean mMenuRefreshContent;
90 private Bundle mMenuFrozenActionViewState;
91
92 /** Implementation which backs the action bar interface API. */
93 private ActionBarImpl aActionBar;
94 /** Main action bar view which displays the core content. */
95 private ActionBarView wActionBar;
96 /** Relevant window and action bar features flags. */
97 private int mFeatures = DEFAULT_FEATURES;
98 /** Relevant user interface option flags. */
99 private int mUiOptions = 0;
100
101 /** Decor indeterminate progress indicator. */
102 private IcsProgressBar mCircularProgressBar;
103 /** Decor progress indicator. */
104 private IcsProgressBar mHorizontalProgressBar;
105
106 /** Current displayed context action bar, if any. */
107 private ActionMode mActionMode;
108 /** Parent view in which the context action bar is displayed. */
109 private ActionBarContextView mActionModeView;
110
111 /** Title view used with dialogs. */
112 private TextView mTitleView;
113 /** Current activity title. */
114 private CharSequence mTitle = null;
115 /** Whether or not this "activity" is floating (i.e., a dialog) */
116 private boolean mIsFloating;
117
118
119
120 ///////////////////////////////////////////////////////////////////////////
121 // Instance methods
122 ///////////////////////////////////////////////////////////////////////////
123
124 @Override
125 public ActionBar getActionBar() {
126 if (DEBUG) Log.d(TAG, "[getActionBar]");
127
128 initActionBar();
129 return aActionBar;
130 }
131
132 private void initActionBar() {
133 if (DEBUG) Log.d(TAG, "[initActionBar]");
134
135 // Initializing the window decor can change window feature flags.
136 // Make sure that we have the correct set before performing the test below.
137 if (mDecor == null) {
138 installDecor();
139 }
140
141 if ((aActionBar != null) || !hasFeature(Window.FEATURE_ACTION_BAR) || hasFeature(Window.FEATURE_NO_TITLE) || mActivity.isChild()) {
142 return;
143 }
144
145 aActionBar = new ActionBarImpl(mActivity, mFeatures);
146
147 if (!mIsDelegate) {
148 //We may never get another chance to set the title
149 wActionBar.setWindowTitle(mActivity.getTitle());
150 }
151 }
152
153 @Override
154 protected Context getThemedContext() {
155 return aActionBar.getThemedContext();
156 }
157
158 @Override
159 public void setTitle(CharSequence title) {
160 if (DEBUG) Log.d(TAG, "[setTitle] title: " + title);
161
162 dispatchTitleChanged(title, 0);
163 }
164
165 @Override
166 public ActionMode startActionMode(ActionMode.Callback callback) {
167 if (DEBUG) Log.d(TAG, "[startActionMode] callback: " + callback);
168
169 if (mActionMode != null) {
170 mActionMode.finish();
171 }
172
173 final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
174 ActionMode mode = null;
175
176 //Emulate Activity's onWindowStartingActionMode:
177 initActionBar();
178 if (aActionBar != null) {
179 mode = aActionBar.startActionMode(wrappedCallback);
180 }
181
182 if (mode != null) {
183 mActionMode = mode;
184 } else {
185 if (mActionModeView == null) {
186 ViewStub stub = (ViewStub)mDecor.findViewById(R.id.abs__action_mode_bar_stub);
187 if (stub != null) {
188 mActionModeView = (ActionBarContextView)stub.inflate();
189 }
190 }
191 if (mActionModeView != null) {
192 mActionModeView.killMode();
193 mode = new StandaloneActionMode(mActivity, mActionModeView, wrappedCallback, true);
194 if (callback.onCreateActionMode(mode, mode.getMenu())) {
195 mode.invalidate();
196 mActionModeView.initForMode(mode);
197 mActionModeView.setVisibility(View.VISIBLE);
198 mActionMode = mode;
199 mActionModeView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
200 } else {
201 mActionMode = null;
202 }
203 }
204 }
205 if (mActionMode != null && mActivity instanceof OnActionModeStartedListener) {
206 ((OnActionModeStartedListener)mActivity).onActionModeStarted(mActionMode);
207 }
208 return mActionMode;
209 }
210
211
212 ///////////////////////////////////////////////////////////////////////////
213 // Lifecycle and interaction callbacks for delegation
214 ///////////////////////////////////////////////////////////////////////////
215
216 @Override
217 public void dispatchConfigurationChanged(Configuration newConfig) {
218 if (DEBUG) Log.d(TAG, "[dispatchConfigurationChanged] newConfig: " + newConfig);
219
220 if (aActionBar != null) {
221 aActionBar.onConfigurationChanged(newConfig);
222 }
223 }
224
225 @Override
226 public void dispatchPostResume() {
227 if (DEBUG) Log.d(TAG, "[dispatchPostResume]");
228
229 if (aActionBar != null) {
230 aActionBar.setShowHideAnimationEnabled(true);
231 }
232 }
233
234 @Override
235 public void dispatchPause() {
236 if (DEBUG) Log.d(TAG, "[dispatchPause]");
237
238 if (wActionBar != null && wActionBar.isOverflowMenuShowing()) {
239 wActionBar.hideOverflowMenu();
240 }
241 }
242
243 @Override
244 public void dispatchStop() {
245 if (DEBUG) Log.d(TAG, "[dispatchStop]");
246
247 if (aActionBar != null) {
248 aActionBar.setShowHideAnimationEnabled(false);
249 }
250 }
251
252 @Override
253 public void dispatchInvalidateOptionsMenu() {
254 if (DEBUG) Log.d(TAG, "[dispatchInvalidateOptionsMenu]");
255
256 Bundle savedActionViewStates = null;
257 if (mMenu != null) {
258 savedActionViewStates = new Bundle();
259 mMenu.saveActionViewStates(savedActionViewStates);
260 if (savedActionViewStates.size() > 0) {
261 mMenuFrozenActionViewState = savedActionViewStates;
262 }
263 // This will be started again when the panel is prepared.
264 mMenu.stopDispatchingItemsChanged();
265 mMenu.clear();
266 }
267 mMenuRefreshContent = true;
268
269 // Prepare the options panel if we have an action bar
270 if (wActionBar != null) {
271 mMenuIsPrepared = false;
272 preparePanel();
273 }
274 }
275
276 @Override
277 public boolean dispatchOpenOptionsMenu() {
278 if (DEBUG) Log.d(TAG, "[dispatchOpenOptionsMenu]");
279
280 if (!isReservingOverflow()) {
281 return false;
282 }
283
284 return wActionBar.showOverflowMenu();
285 }
286
287 @Override
288 public boolean dispatchCloseOptionsMenu() {
289 if (DEBUG) Log.d(TAG, "[dispatchCloseOptionsMenu]");
290
291 if (!isReservingOverflow()) {
292 return false;
293 }
294
295 if (wActionBar != null) {
296 return wActionBar.hideOverflowMenu();
297 }
298 return false;
299 }
300
301 @Override
302 public void dispatchPostCreate(Bundle savedInstanceState) {
303 if (DEBUG) Log.d(TAG, "[dispatchOnPostCreate]");
304
305 if (mIsDelegate) {
306 mIsTitleReady = true;
307 }
308
309 if (mDecor == null) {
310 initActionBar();
311 }
312 }
313
314 @Override
315 public boolean dispatchCreateOptionsMenu(android.view.Menu menu) {
316 if (DEBUG) {
317 Log.d(TAG, "[dispatchCreateOptionsMenu] android.view.Menu: " + menu);
318 Log.d(TAG, "[dispatchCreateOptionsMenu] returning true");
319 }
320 return true;
321 }
322
323 @Override
324 public boolean dispatchPrepareOptionsMenu(android.view.Menu menu) {
325 if (DEBUG) Log.d(TAG, "[dispatchPrepareOptionsMenu] android.view.Menu: " + menu);
326
327 if (mActionMode != null) {
328 return false;
329 }
330
331 mMenuIsPrepared = false;
332 if (!preparePanel()) {
333 return false;
334 }
335
336 if (isReservingOverflow()) {
337 return false;
338 }
339
340 if (mNativeItemMap == null) {
341 mNativeItemMap = new HashMap<android.view.MenuItem, MenuItemImpl>();
342 } else {
343 mNativeItemMap.clear();
344 }
345
346 if (mMenu == null) {
347 return false;
348 }
349
350 boolean result = mMenu.bindNativeOverflow(menu, this, mNativeItemMap);
351 if (DEBUG) Log.d(TAG, "[dispatchPrepareOptionsMenu] returning " + result);
352 return result;
353 }
354
355 @Override
356 public boolean dispatchOptionsItemSelected(android.view.MenuItem item) {
357 throw new IllegalStateException("Native callback invoked. Create a test case and report!");
358 }
359
360 @Override
361 public boolean dispatchMenuOpened(int featureId, android.view.Menu menu) {
362 if (DEBUG) Log.d(TAG, "[dispatchMenuOpened] featureId: " + featureId + ", menu: " + menu);
363
364 if (featureId == Window.FEATURE_ACTION_BAR || featureId == Window.FEATURE_OPTIONS_PANEL) {
365 if (aActionBar != null) {
366 aActionBar.dispatchMenuVisibilityChanged(true);
367 }
368 return true;
369 }
370
371 return false;
372 }
373
374 @Override
375 public void dispatchPanelClosed(int featureId, android.view.Menu menu){
376 if (DEBUG) Log.d(TAG, "[dispatchPanelClosed] featureId: " + featureId + ", menu: " + menu);
377
378 if (featureId == Window.FEATURE_ACTION_BAR || featureId == Window.FEATURE_OPTIONS_PANEL) {
379 if (aActionBar != null) {
380 aActionBar.dispatchMenuVisibilityChanged(false);
381 }
382 }
383 }
384
385 @Override
386 public void dispatchTitleChanged(CharSequence title, int color) {
387 if (DEBUG) Log.d(TAG, "[dispatchTitleChanged] title: " + title + ", color: " + color);
388
389 if (!mIsDelegate || mIsTitleReady) {
390 if (mTitleView != null) {
391 mTitleView.setText(title);
392 } else if (wActionBar != null) {
393 wActionBar.setWindowTitle(title);
394 }
395 }
396
397 mTitle = title;
398 }
399
400 @Override
401 public boolean dispatchKeyEvent(KeyEvent event) {
402 if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] event: " + event);
403
404 final int keyCode = event.getKeyCode();
405
406 // Not handled by the view hierarchy, does the action bar want it
407 // to cancel out of something special?
408 if (keyCode == KeyEvent.KEYCODE_BACK) {
409 final int action = event.getAction();
410 // Back cancels action modes first.
411 if (mActionMode != null) {
412 if (action == KeyEvent.ACTION_UP) {
413 mActionMode.finish();
414 }
415 if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] returning true");
416 return true;
417 }
418
419 // Next collapse any expanded action views.
420 if (wActionBar != null && wActionBar.hasExpandedActionView()) {
421 if (action == KeyEvent.ACTION_UP) {
422 wActionBar.collapseActionView();
423 }
424 if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] returning true");
425 return true;
426 }
427 }
428
429 if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] returning false");
430 return false;
431 }
432
433 @Override
434 public void dispatchDestroy() {
435 mIsDestroyed = true;
436 }
437
438 @Override
439 public void dispatchSaveInstanceState(Bundle outState) {
440 if (mMenu != null) {
441 mMenuFrozenActionViewState = new Bundle();
442 mMenu.saveActionViewStates(mMenuFrozenActionViewState);
443 }
444 outState.putParcelable(PANELS_TAG, mMenuFrozenActionViewState);
445 }
446
447 @Override
448 public void dispatchRestoreInstanceState(Bundle savedInstanceState) {
449 mMenuFrozenActionViewState = savedInstanceState.getParcelable(PANELS_TAG);
450 }
451
452 ///////////////////////////////////////////////////////////////////////////
453 // Menu callback lifecycle and creation
454 ///////////////////////////////////////////////////////////////////////////
455
456 private boolean preparePanel() {
457 // Already prepared (isPrepared will be reset to false later)
458 if (mMenuIsPrepared) {
459 return true;
460 }
461
462 // Init the panel state's menu--return false if init failed
463 if (mMenu == null || mMenuRefreshContent) {
464 if (mMenu == null) {
465 if (!initializePanelMenu() || (mMenu == null)) {
466 return false;
467 }
468 }
469
470 if (wActionBar != null) {
471 wActionBar.setMenu(mMenu, this);
472 }
473
474 // Call callback, and return if it doesn't want to display menu.
475
476 // Creating the panel menu will involve a lot of manipulation;
477 // don't dispatch change events to presenters until we're done.
478 mMenu.stopDispatchingItemsChanged();
479 if (!callbackCreateOptionsMenu(mMenu)) {
480 // Ditch the menu created above
481 mMenu = null;
482
483 if (wActionBar != null) {
484 // Don't show it in the action bar either
485 wActionBar.setMenu(null, this);
486 }
487
488 return false;
489 }
490
491 mMenuRefreshContent = false;
492 }
493
494 // Callback and return if the callback does not want to show the menu
495
496 // Preparing the panel menu can involve a lot of manipulation;
497 // don't dispatch change events to presenters until we're done.
498 mMenu.stopDispatchingItemsChanged();
499
500 // Restore action view state before we prepare. This gives apps
501 // an opportunity to override frozen/restored state in onPrepare.
502 if (mMenuFrozenActionViewState != null) {
503 mMenu.restoreActionViewStates(mMenuFrozenActionViewState);
504 mMenuFrozenActionViewState = null;
505 }
506
507 if (!callbackPrepareOptionsMenu(mMenu)) {
508 if (wActionBar != null) {
509 // The app didn't want to show the menu for now but it still exists.
510 // Clear it out of the action bar.
511 wActionBar.setMenu(null, this);
512 }
513 mMenu.startDispatchingItemsChanged();
514 return false;
515 }
516
517 // Set the proper keymap
518 KeyCharacterMap kmap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
519 mMenu.setQwertyMode(kmap.getKeyboardType() != KeyCharacterMap.NUMERIC);
520 mMenu.startDispatchingItemsChanged();
521
522 // Set other state
523 mMenuIsPrepared = true;
524
525 return true;
526 }
527
528 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
529 return callbackOptionsItemSelected(item);
530 }
531
532 public void onMenuModeChange(MenuBuilder menu) {
533 reopenMenu(true);
534 }
535
536 private void reopenMenu(boolean toggleMenuMode) {
537 if (wActionBar != null && wActionBar.isOverflowReserved()) {
538 if (!wActionBar.isOverflowMenuShowing() || !toggleMenuMode) {
539 if (wActionBar.getVisibility() == View.VISIBLE) {
540 if (callbackPrepareOptionsMenu(mMenu)) {
541 wActionBar.showOverflowMenu();
542 }
543 }
544 } else {
545 wActionBar.hideOverflowMenu();
546 }
547 return;
548 }
549 }
550
551 private boolean initializePanelMenu() {
552 Context context = mActivity;//getContext();
553
554 // If we have an action bar, initialize the menu with a context themed for it.
555 if (wActionBar != null) {
556 TypedValue outValue = new TypedValue();
557 Resources.Theme currentTheme = context.getTheme();
558 currentTheme.resolveAttribute(R.attr.actionBarWidgetTheme,
559 outValue, true);
560 final int targetThemeRes = outValue.resourceId;
561
562 if (targetThemeRes != 0 /*&& context.getThemeResId() != targetThemeRes*/) {
563 context = new ContextThemeWrapper(context, targetThemeRes);
564 }
565 }
566
567 mMenu = new MenuBuilder(context);
568 mMenu.setCallback(this);
569
570 return true;
571 }
572
573 void checkCloseActionMenu(Menu menu) {
574 if (mClosingActionMenu) {
575 return;
576 }
577
578 mClosingActionMenu = true;
579 wActionBar.dismissPopupMenus();
580 //Callback cb = getCallback();
581 //if (cb != null && !isDestroyed()) {
582 // cb.onPanelClosed(FEATURE_ACTION_BAR, menu);
583 //}
584 mClosingActionMenu = false;
585 }
586
587 @Override
588 public boolean onOpenSubMenu(MenuBuilder subMenu) {
589 return true;
590 }
591
592 @Override
593 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
594 checkCloseActionMenu(menu);
595 }
596
597 @Override
598 public boolean onMenuItemClick(android.view.MenuItem item) {
599 if (DEBUG) Log.d(TAG, "[mNativeItemListener.onMenuItemClick] item: " + item);
600
601 final MenuItemImpl sherlockItem = mNativeItemMap.get(item);
602 if (sherlockItem != null) {
603 sherlockItem.invoke();
604 } else {
605 Log.e(TAG, "Options item \"" + item + "\" not found in mapping");
606 }
607
608 return true; //Do not allow continuation of native handling
609 }
610
611 @Override
612 public boolean onMenuItemSelected(int featureId, MenuItem item) {
613 return callbackOptionsItemSelected(item);
614 }
615
616
617 ///////////////////////////////////////////////////////////////////////////
618 // Progress bar interaction and internal handling
619 ///////////////////////////////////////////////////////////////////////////
620
621 @Override
622 public void setProgressBarVisibility(boolean visible) {
623 if (DEBUG) Log.d(TAG, "[setProgressBarVisibility] visible: " + visible);
624
625 setFeatureInt(Window.FEATURE_PROGRESS, visible ? Window.PROGRESS_VISIBILITY_ON :
626 Window.PROGRESS_VISIBILITY_OFF);
627 }
628
629 @Override
630 public void setProgressBarIndeterminateVisibility(boolean visible) {
631 if (DEBUG) Log.d(TAG, "[setProgressBarIndeterminateVisibility] visible: " + visible);
632
633 setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
634 visible ? Window.PROGRESS_VISIBILITY_ON : Window.PROGRESS_VISIBILITY_OFF);
635 }
636
637 @Override
638 public void setProgressBarIndeterminate(boolean indeterminate) {
639 if (DEBUG) Log.d(TAG, "[setProgressBarIndeterminate] indeterminate: " + indeterminate);
640
641 setFeatureInt(Window.FEATURE_PROGRESS,
642 indeterminate ? Window.PROGRESS_INDETERMINATE_ON : Window.PROGRESS_INDETERMINATE_OFF);
643 }
644
645 @Override
646 public void setProgress(int progress) {
647 if (DEBUG) Log.d(TAG, "[setProgress] progress: " + progress);
648
649 setFeatureInt(Window.FEATURE_PROGRESS, progress + Window.PROGRESS_START);
650 }
651
652 @Override
653 public void setSecondaryProgress(int secondaryProgress) {
654 if (DEBUG) Log.d(TAG, "[setSecondaryProgress] secondaryProgress: " + secondaryProgress);
655
656 setFeatureInt(Window.FEATURE_PROGRESS,
657 secondaryProgress + Window.PROGRESS_SECONDARY_START);
658 }
659
660 private void setFeatureInt(int featureId, int value) {
661 updateInt(featureId, value, false);
662 }
663
664 private void updateInt(int featureId, int value, boolean fromResume) {
665 // Do nothing if the decor is not yet installed... an update will
666 // need to be forced when we eventually become active.
667 if (mContentParent == null) {
668 return;
669 }
670
671 final int featureMask = 1 << featureId;
672
673 if ((getFeatures() & featureMask) == 0 && !fromResume) {
674 return;
675 }
676
677 onIntChanged(featureId, value);
678 }
679
680 private void onIntChanged(int featureId, int value) {
681 if (featureId == Window.FEATURE_PROGRESS || featureId == Window.FEATURE_INDETERMINATE_PROGRESS) {
682 updateProgressBars(value);
683 }
684 }
685
686 private void updateProgressBars(int value) {
687 IcsProgressBar circularProgressBar = getCircularProgressBar(true);
688 IcsProgressBar horizontalProgressBar = getHorizontalProgressBar(true);
689
690 final int features = mFeatures;//getLocalFeatures();
691 if (value == Window.PROGRESS_VISIBILITY_ON) {
692 if ((features & (1 << Window.FEATURE_PROGRESS)) != 0) {
693 int level = horizontalProgressBar.getProgress();
694 int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
695 View.VISIBLE : View.INVISIBLE;
696 horizontalProgressBar.setVisibility(visibility);
697 }
698 if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0) {
699 circularProgressBar.setVisibility(View.VISIBLE);
700 }
701 } else if (value == Window.PROGRESS_VISIBILITY_OFF) {
702 if ((features & (1 << Window.FEATURE_PROGRESS)) != 0) {
703 horizontalProgressBar.setVisibility(View.GONE);
704 }
705 if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0) {
706 circularProgressBar.setVisibility(View.GONE);
707 }
708 } else if (value == Window.PROGRESS_INDETERMINATE_ON) {
709 horizontalProgressBar.setIndeterminate(true);
710 } else if (value == Window.PROGRESS_INDETERMINATE_OFF) {
711 horizontalProgressBar.setIndeterminate(false);
712 } else if (Window.PROGRESS_START <= value && value <= Window.PROGRESS_END) {
713 // We want to set the progress value before testing for visibility
714 // so that when the progress bar becomes visible again, it has the
715 // correct level.
716 horizontalProgressBar.setProgress(value - Window.PROGRESS_START);
717
718 if (value < Window.PROGRESS_END) {
719 showProgressBars(horizontalProgressBar, circularProgressBar);
720 } else {
721 hideProgressBars(horizontalProgressBar, circularProgressBar);
722 }
723 } else if (Window.PROGRESS_SECONDARY_START <= value && value <= Window.PROGRESS_SECONDARY_END) {
724 horizontalProgressBar.setSecondaryProgress(value - Window.PROGRESS_SECONDARY_START);
725
726 showProgressBars(horizontalProgressBar, circularProgressBar);
727 }
728 }
729
730 private void showProgressBars(IcsProgressBar horizontalProgressBar, IcsProgressBar spinnyProgressBar) {
731 final int features = mFeatures;//getLocalFeatures();
732 if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
733 spinnyProgressBar.getVisibility() == View.INVISIBLE) {
734 spinnyProgressBar.setVisibility(View.VISIBLE);
735 }
736 // Only show the progress bars if the primary progress is not complete
737 if ((features & (1 << Window.FEATURE_PROGRESS)) != 0 &&
738 horizontalProgressBar.getProgress() < 10000) {
739 horizontalProgressBar.setVisibility(View.VISIBLE);
740 }
741 }
742
743 private void hideProgressBars(IcsProgressBar horizontalProgressBar, IcsProgressBar spinnyProgressBar) {
744 final int features = mFeatures;//getLocalFeatures();
745 Animation anim = AnimationUtils.loadAnimation(mActivity, android.R.anim.fade_out);
746 anim.setDuration(1000);
747 if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
748 spinnyProgressBar.getVisibility() == View.VISIBLE) {
749 spinnyProgressBar.startAnimation(anim);
750 spinnyProgressBar.setVisibility(View.INVISIBLE);
751 }
752 if ((features & (1 << Window.FEATURE_PROGRESS)) != 0 &&
753 horizontalProgressBar.getVisibility() == View.VISIBLE) {
754 horizontalProgressBar.startAnimation(anim);
755 horizontalProgressBar.setVisibility(View.INVISIBLE);
756 }
757 }
758
759 private IcsProgressBar getCircularProgressBar(boolean shouldInstallDecor) {
760 if (mCircularProgressBar != null) {
761 return mCircularProgressBar;
762 }
763 if (mContentParent == null && shouldInstallDecor) {
764 installDecor();
765 }
766 mCircularProgressBar = (IcsProgressBar)mDecor.findViewById(R.id.abs__progress_circular);
767 if (mCircularProgressBar != null) {
768 mCircularProgressBar.setVisibility(View.INVISIBLE);
769 }
770 return mCircularProgressBar;
771 }
772
773 private IcsProgressBar getHorizontalProgressBar(boolean shouldInstallDecor) {
774 if (mHorizontalProgressBar != null) {
775 return mHorizontalProgressBar;
776 }
777 if (mContentParent == null && shouldInstallDecor) {
778 installDecor();
779 }
780 mHorizontalProgressBar = (IcsProgressBar)mDecor.findViewById(R.id.abs__progress_horizontal);
781 if (mHorizontalProgressBar != null) {
782 mHorizontalProgressBar.setVisibility(View.INVISIBLE);
783 }
784 return mHorizontalProgressBar;
785 }
786
787
788 ///////////////////////////////////////////////////////////////////////////
789 // Feature management and content interaction and creation
790 ///////////////////////////////////////////////////////////////////////////
791
792 private int getFeatures() {
793 if (DEBUG) Log.d(TAG, "[getFeatures] returning " + mFeatures);
794
795 return mFeatures;
796 }
797
798 @Override
799 public boolean hasFeature(int featureId) {
800 if (DEBUG) Log.d(TAG, "[hasFeature] featureId: " + featureId);
801
802 boolean result = (mFeatures & (1 << featureId)) != 0;
803 if (DEBUG) Log.d(TAG, "[hasFeature] returning " + result);
804 return result;
805 }
806
807 @Override
808 public boolean requestFeature(int featureId) {
809 if (DEBUG) Log.d(TAG, "[requestFeature] featureId: " + featureId);
810
811 if (mContentParent != null) {
812 throw new AndroidRuntimeException("requestFeature() must be called before adding content");
813 }
814
815 switch (featureId) {
816 case Window.FEATURE_ACTION_BAR:
817 case Window.FEATURE_ACTION_BAR_OVERLAY:
818 case Window.FEATURE_ACTION_MODE_OVERLAY:
819 case Window.FEATURE_INDETERMINATE_PROGRESS:
820 case Window.FEATURE_NO_TITLE:
821 case Window.FEATURE_PROGRESS:
822 mFeatures |= (1 << featureId);
823 return true;
824
825 default:
826 return false;
827 }
828 }
829
830 @Override
831 public void setUiOptions(int uiOptions) {
832 if (DEBUG) Log.d(TAG, "[setUiOptions] uiOptions: " + uiOptions);
833
834 mUiOptions = uiOptions;
835 }
836
837 @Override
838 public void setUiOptions(int uiOptions, int mask) {
839 if (DEBUG) Log.d(TAG, "[setUiOptions] uiOptions: " + uiOptions + ", mask: " + mask);
840
841 mUiOptions = (mUiOptions & ~mask) | (uiOptions & mask);
842 }
843
844 @Override
845 public void setContentView(int layoutResId) {
846 if (DEBUG) Log.d(TAG, "[setContentView] layoutResId: " + layoutResId);
847
848 if (mContentParent == null) {
849 installDecor();
850 } else {
851 mContentParent.removeAllViews();
852 }
853 mActivity.getLayoutInflater().inflate(layoutResId, mContentParent);
854
855 android.view.Window.Callback callback = mActivity.getWindow().getCallback();
856 if (callback != null) {
857 callback.onContentChanged();
858 }
859
860 initActionBar();
861 }
862
863 @Override
864 public void setContentView(View view, ViewGroup.LayoutParams params) {
865 if (DEBUG) Log.d(TAG, "[setContentView] view: " + view + ", params: " + params);
866
867 if (mContentParent == null) {
868 installDecor();
869 } else {
870 mContentParent.removeAllViews();
871 }
872 mContentParent.addView(view, params);
873
874 android.view.Window.Callback callback = mActivity.getWindow().getCallback();
875 if (callback != null) {
876 callback.onContentChanged();
877 }
878
879 initActionBar();
880 }
881
882 @Override
883 public void addContentView(View view, ViewGroup.LayoutParams params) {
884 if (DEBUG) Log.d(TAG, "[addContentView] view: " + view + ", params: " + params);
885
886 if (mContentParent == null) {
887 installDecor();
888 }
889 mContentParent.addView(view, params);
890
891 initActionBar();
892 }
893
894 private void installDecor() {
895 if (DEBUG) Log.d(TAG, "[installDecor]");
896
897 if (mDecor == null) {
898 mDecor = (ViewGroup)mActivity.getWindow().getDecorView().findViewById(android.R.id.content);
899 }
900 if (mContentParent == null) {
901 //Since we are not operating at the window level we need to take
902 //into account the fact that the true decor may have already been
903 //initialized and had content attached to it. If that is the case,
904 //copy over its children to our new content container.
905 List<View> views = null;
906 if (mDecor.getChildCount() > 0) {
907 views = new ArrayList<View>(1); //Usually there's only one child
908 for (int i = 0, children = mDecor.getChildCount(); i < children; i++) {
909 View child = mDecor.getChildAt(0);
910 mDecor.removeView(child);
911 views.add(child);
912 }
913 }
914
915 mContentParent = generateLayout();
916
917 //Copy over the old children. See above for explanation.
918 if (views != null) {
919 for (View child : views) {
920 mContentParent.addView(child);
921 }
922 }
923
924 mTitleView = (TextView)mDecor.findViewById(android.R.id.title);
925 if (mTitleView != null) {
926 if (hasFeature(Window.FEATURE_NO_TITLE)) {
927 mTitleView.setVisibility(View.GONE);
928 if (mContentParent instanceof FrameLayout) {
929 ((FrameLayout)mContentParent).setForeground(null);
930 }
931 } else {
932 mTitleView.setText(mTitle);
933 }
934 } else {
935 wActionBar = (ActionBarView)mDecor.findViewById(R.id.abs__action_bar);
936 if (wActionBar != null) {
937 wActionBar.setWindowCallback(this);
938 if (wActionBar.getTitle() == null) {
939 wActionBar.setWindowTitle(mActivity.getTitle());
940 }
941 if (hasFeature(Window.FEATURE_PROGRESS)) {
942 wActionBar.initProgress();
943 }
944 if (hasFeature(Window.FEATURE_INDETERMINATE_PROGRESS)) {
945 wActionBar.initIndeterminateProgress();
946 }
947
948 //Since we don't require onCreate dispatching, parse for uiOptions here
949 int uiOptions = loadUiOptionsFromManifest(mActivity);
950 if (uiOptions != 0) {
951 mUiOptions = uiOptions;
952 }
953
954 boolean splitActionBar = false;
955 final boolean splitWhenNarrow = (mUiOptions & ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0;
956 if (splitWhenNarrow) {
957 splitActionBar = getResources_getBoolean(mActivity, R.bool.abs__split_action_bar_is_narrow);
958 } else {
959 splitActionBar = mActivity.getTheme()
960 .obtainStyledAttributes(R.styleable.SherlockTheme)
961 .getBoolean(R.styleable.SherlockTheme_windowSplitActionBar, false);
962 }
963 final ActionBarContainer splitView = (ActionBarContainer)mDecor.findViewById(R.id.abs__split_action_bar);
964 if (splitView != null) {
965 wActionBar.setSplitView(splitView);
966 wActionBar.setSplitActionBar(splitActionBar);
967 wActionBar.setSplitWhenNarrow(splitWhenNarrow);
968
969 mActionModeView = (ActionBarContextView)mDecor.findViewById(R.id.abs__action_context_bar);
970 mActionModeView.setSplitView(splitView);
971 mActionModeView.setSplitActionBar(splitActionBar);
972 mActionModeView.setSplitWhenNarrow(splitWhenNarrow);
973 } else if (splitActionBar) {
974 Log.e(TAG, "Requested split action bar with incompatible window decor! Ignoring request.");
975 }
976
977 // Post the panel invalidate for later; avoid application onCreateOptionsMenu
978 // being called in the middle of onCreate or similar.
979 mDecor.post(new Runnable() {
980 @Override
981 public void run() {
982 //Invalidate if the panel menu hasn't been created before this.
983 if (!mIsDestroyed && !mActivity.isFinishing() && mMenu == null) {
984 dispatchInvalidateOptionsMenu();
985 }
986 }
987 });
988 }
989 }
990 }
991 }
992
993 private ViewGroup generateLayout() {
994 if (DEBUG) Log.d(TAG, "[generateLayout]");
995
996 // Apply data from current theme.
997
998 TypedArray a = mActivity.getTheme().obtainStyledAttributes(R.styleable.SherlockTheme);
999
1000 mIsFloating = a.getBoolean(R.styleable.SherlockTheme_android_windowIsFloating, false);
1001
1002 if (!a.hasValue(R.styleable.SherlockTheme_windowActionBar)) {
1003 throw new IllegalStateException("You must use Theme.Sherlock, Theme.Sherlock.Light, Theme.Sherlock.Light.DarkActionBar, or a derivative.");
1004 }
1005
1006 if (a.getBoolean(R.styleable.SherlockTheme_windowNoTitle, false)) {
1007 requestFeature(Window.FEATURE_NO_TITLE);
1008 } else if (a.getBoolean(R.styleable.SherlockTheme_windowActionBar, false)) {
1009 // Don't allow an action bar if there is no title.
1010 requestFeature(Window.FEATURE_ACTION_BAR);
1011 }
1012
1013 if (a.getBoolean(R.styleable.SherlockTheme_windowActionBarOverlay, false)) {
1014 requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
1015 }
1016
1017 if (a.getBoolean(R.styleable.SherlockTheme_windowActionModeOverlay, false)) {
1018 requestFeature(Window.FEATURE_ACTION_MODE_OVERLAY);
1019 }
1020
1021 a.recycle();
1022
1023 int layoutResource;
1024 if (!hasFeature(Window.FEATURE_NO_TITLE)) {
1025 if (mIsFloating) {
1026 //Trash original dialog LinearLayout
1027 mDecor = (ViewGroup)mDecor.getParent();
1028 mDecor.removeAllViews();
1029
1030 layoutResource = R.layout.abs__dialog_title_holo;
1031 } else {
1032 if (hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY)) {
1033 layoutResource = R.layout.abs__screen_action_bar_overlay;
1034 } else {
1035 layoutResource = R.layout.abs__screen_action_bar;
1036 }
1037 }
1038 } else if (hasFeature(Window.FEATURE_ACTION_MODE_OVERLAY) && !hasFeature(Window.FEATURE_NO_TITLE)) {
1039 layoutResource = R.layout.abs__screen_simple_overlay_action_mode;
1040 } else {
1041 layoutResource = R.layout.abs__screen_simple;
1042 }
1043
1044 if (DEBUG) Log.d(TAG, "[generateLayout] using screen XML " + mActivity.getResources().getString(layoutResource));
1045 View in = mActivity.getLayoutInflater().inflate(layoutResource, null);
1046 mDecor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1047
1048 ViewGroup contentParent = (ViewGroup)mDecor.findViewById(R.id.abs__content);
1049 if (contentParent == null) {
1050 throw new RuntimeException("Couldn't find content container view");
1051 }
1052
1053 //Make our new child the true content view (for fragments). VERY VOLATILE!
1054 mDecor.setId(View.NO_ID);
1055 contentParent.setId(android.R.id.content);
1056
1057 if (hasFeature(Window.FEATURE_INDETERMINATE_PROGRESS)) {
1058 IcsProgressBar progress = getCircularProgressBar(false);
1059 if (progress != null) {
1060 progress.setIndeterminate(true);
1061 }
1062 }
1063
1064 return contentParent;
1065 }
1066
1067
1068 ///////////////////////////////////////////////////////////////////////////
1069 // Miscellaneous
1070 ///////////////////////////////////////////////////////////////////////////
1071
1072 /**
1073 * Determine whether or not the device has a dedicated menu key.
1074 *
1075 * @return {@code true} if native menu key is present.
1076 */
1077 private boolean isReservingOverflow() {
1078 if (!mReserveOverflowSet) {
1079 mReserveOverflow = ActionMenuPresenter.reserveOverflow(mActivity);
1080 mReserveOverflowSet = true;
1081 }
1082 return mReserveOverflow;
1083 }
1084
1085 private static int loadUiOptionsFromManifest(Activity activity) {
1086 int uiOptions = 0;
1087 try {
1088 final String thisPackage = activity.getClass().getName();
1089 if (DEBUG) Log.i(TAG, "Parsing AndroidManifest.xml for " + thisPackage);
1090
1091 final String packageName = activity.getApplicationInfo().packageName;
1092 final AssetManager am = activity.createPackageContext(packageName, 0).getAssets();
1093 final XmlResourceParser xml = am.openXmlResourceParser("AndroidManifest.xml");
1094
1095 int eventType = xml.getEventType();
1096 while (eventType != XmlPullParser.END_DOCUMENT) {
1097 if (eventType == XmlPullParser.START_TAG) {
1098 String name = xml.getName();
1099
1100 if ("application".equals(name)) {
1101 //Check if the <application> has the attribute
1102 if (DEBUG) Log.d(TAG, "Got <application>");
1103
1104 for (int i = xml.getAttributeCount() - 1; i >= 0; i--) {
1105 if (DEBUG) Log.d(TAG, xml.getAttributeName(i) + ": " + xml.getAttributeValue(i));
1106
1107 if ("uiOptions".equals(xml.getAttributeName(i))) {
1108 uiOptions = xml.getAttributeIntValue(i, 0);
1109 break; //out of for loop
1110 }
1111 }
1112 } else if ("activity".equals(name)) {
1113 //Check if the <activity> is us and has the attribute
1114 if (DEBUG) Log.d(TAG, "Got <activity>");
1115 Integer activityUiOptions = null;
1116 String activityPackage = null;
1117 boolean isOurActivity = false;
1118
1119 for (int i = xml.getAttributeCount() - 1; i >= 0; i--) {
1120 if (DEBUG) Log.d(TAG, xml.getAttributeName(i) + ": " + xml.getAttributeValue(i));
1121
1122 //We need both uiOptions and name attributes
1123 String attrName = xml.getAttributeName(i);
1124 if ("uiOptions".equals(attrName)) {
1125 activityUiOptions = xml.getAttributeIntValue(i, 0);
1126 } else if ("name".equals(attrName)) {
1127 activityPackage = cleanActivityName(packageName, xml.getAttributeValue(i));
1128 if (!thisPackage.equals(activityPackage)) {
1129 break; //out of for loop
1130 }
1131 isOurActivity = true;
1132 }
1133
1134 //Make sure we have both attributes before processing
1135 if ((activityUiOptions != null) && (activityPackage != null)) {
1136 //Our activity, uiOptions specified, override with our value
1137 uiOptions = activityUiOptions.intValue();
1138 }
1139 }
1140 if (isOurActivity) {
1141 //If we matched our activity but it had no logo don't
1142 //do any more processing of the manifest
1143 break;
1144 }
1145 }
1146 }
1147 eventType = xml.nextToken();
1148 }
1149 } catch (Exception e) {
1150 e.printStackTrace();
1151 }
1152 if (DEBUG) Log.i(TAG, "Returning " + Integer.toHexString(uiOptions));
1153 return uiOptions;
1154 }
1155
1156 public static String cleanActivityName(String manifestPackage, String activityName) {
1157 if (activityName.charAt(0) == '.') {
1158 //Relative activity name (e.g., android:name=".ui.SomeClass")
1159 return manifestPackage + activityName;
1160 }
1161 if (activityName.indexOf('.', 1) == -1) {
1162 //Unqualified activity name (e.g., android:name="SomeClass")
1163 return manifestPackage + "." + activityName;
1164 }
1165 //Fully-qualified activity name (e.g., "com.my.package.SomeClass")
1166 return activityName;
1167 }
1168
1169 /**
1170 * Clears out internal reference when the action mode is destroyed.
1171 */
1172 private class ActionModeCallbackWrapper implements ActionMode.Callback {
1173 private final ActionMode.Callback mWrapped;
1174
1175 public ActionModeCallbackWrapper(ActionMode.Callback wrapped) {
1176 mWrapped = wrapped;
1177 }
1178
1179 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
1180 return mWrapped.onCreateActionMode(mode, menu);
1181 }
1182
1183 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
1184 return mWrapped.onPrepareActionMode(mode, menu);
1185 }
1186
1187 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
1188 return mWrapped.onActionItemClicked(mode, item);
1189 }
1190
1191 public void onDestroyActionMode(ActionMode mode) {
1192 mWrapped.onDestroyActionMode(mode);
1193 if (mActionModeView != null) {
1194 mActionModeView.setVisibility(View.GONE);
1195 mActionModeView.removeAllViews();
1196 }
1197 if (mActivity instanceof OnActionModeFinishedListener) {
1198 ((OnActionModeFinishedListener)mActivity).onActionModeFinished(mActionMode);
1199 }
1200 mActionMode = null;
1201 }
1202 }
1203 }