create changelog entry
[debian/openrocket] / android-libraries / ActionBarSherlock / src / com / actionbarsherlock / internal / widget / ActionBarContextView.java
1 /*
2  * Copyright (C) 2010 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 package com.actionbarsherlock.internal.widget;
17
18 import android.content.Context;
19 import android.content.res.TypedArray;
20 import android.graphics.drawable.Drawable;
21 import android.text.TextUtils;
22 import android.util.AttributeSet;
23 import android.view.LayoutInflater;
24 import android.view.View;
25 import android.view.ViewGroup;
26 import android.view.accessibility.AccessibilityEvent;
27 import android.view.animation.DecelerateInterpolator;
28 import android.widget.LinearLayout;
29 import android.widget.TextView;
30
31 import com.actionbarsherlock.R;
32 import com.actionbarsherlock.internal.nineoldandroids.animation.Animator;
33 import com.actionbarsherlock.internal.nineoldandroids.animation.Animator.AnimatorListener;
34 import com.actionbarsherlock.internal.nineoldandroids.animation.AnimatorSet;
35 import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator;
36 import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
37 import com.actionbarsherlock.internal.nineoldandroids.widget.NineLinearLayout;
38 import com.actionbarsherlock.internal.view.menu.ActionMenuPresenter;
39 import com.actionbarsherlock.internal.view.menu.ActionMenuView;
40 import com.actionbarsherlock.internal.view.menu.MenuBuilder;
41 import com.actionbarsherlock.view.ActionMode;
42
43 /**
44  * @hide
45  */
46 public class ActionBarContextView extends AbsActionBarView implements AnimatorListener {
47     //UNUSED private static final String TAG = "ActionBarContextView";
48
49     private CharSequence mTitle;
50     private CharSequence mSubtitle;
51
52     private NineLinearLayout mClose;
53     private View mCustomView;
54     private LinearLayout mTitleLayout;
55     private TextView mTitleView;
56     private TextView mSubtitleView;
57     private int mTitleStyleRes;
58     private int mSubtitleStyleRes;
59     private Drawable mSplitBackground;
60
61     private Animator mCurrentAnimation;
62     private boolean mAnimateInOnLayout;
63     private int mAnimationMode;
64
65     private static final int ANIMATE_IDLE = 0;
66     private static final int ANIMATE_IN = 1;
67     private static final int ANIMATE_OUT = 2;
68
69     public ActionBarContextView(Context context) {
70         this(context, null);
71     }
72
73     public ActionBarContextView(Context context, AttributeSet attrs) {
74         this(context, attrs, R.attr.actionModeStyle);
75     }
76
77     public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) {
78         super(context, attrs, defStyle);
79
80         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SherlockActionMode, defStyle, 0);
81         setBackgroundDrawable(a.getDrawable(
82                 R.styleable.SherlockActionMode_background));
83         mTitleStyleRes = a.getResourceId(
84                 R.styleable.SherlockActionMode_titleTextStyle, 0);
85         mSubtitleStyleRes = a.getResourceId(
86                 R.styleable.SherlockActionMode_subtitleTextStyle, 0);
87
88         mContentHeight = a.getLayoutDimension(
89                 R.styleable.SherlockActionMode_height, 0);
90
91         mSplitBackground = a.getDrawable(
92                 R.styleable.SherlockActionMode_backgroundSplit);
93
94         a.recycle();
95     }
96
97     @Override
98     public void onDetachedFromWindow() {
99         super.onDetachedFromWindow();
100         if (mActionMenuPresenter != null) {
101             mActionMenuPresenter.hideOverflowMenu();
102             mActionMenuPresenter.hideSubMenus();
103         }
104     }
105
106     @Override
107     public void setSplitActionBar(boolean split) {
108         if (mSplitActionBar != split) {
109             if (mActionMenuPresenter != null) {
110                 // Mode is already active; move everything over and adjust the menu itself.
111                 final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
112                         LayoutParams.MATCH_PARENT);
113                 if (!split) {
114                     mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
115                     mMenuView.setBackgroundDrawable(null);
116                     final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
117                     if (oldParent != null) oldParent.removeView(mMenuView);
118                     addView(mMenuView, layoutParams);
119                 } else {
120                     // Allow full screen width in split mode.
121                     mActionMenuPresenter.setWidthLimit(
122                             getContext().getResources().getDisplayMetrics().widthPixels, true);
123                     // No limit to the item count; use whatever will fit.
124                     mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
125                     // Span the whole width
126                     layoutParams.width = LayoutParams.MATCH_PARENT;
127                     layoutParams.height = mContentHeight;
128                     mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
129                     mMenuView.setBackgroundDrawable(mSplitBackground);
130                     final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
131                     if (oldParent != null) oldParent.removeView(mMenuView);
132                     mSplitView.addView(mMenuView, layoutParams);
133                 }
134             }
135             super.setSplitActionBar(split);
136         }
137     }
138
139     public void setContentHeight(int height) {
140         mContentHeight = height;
141     }
142
143     public void setCustomView(View view) {
144         if (mCustomView != null) {
145             removeView(mCustomView);
146         }
147         mCustomView = view;
148         if (mTitleLayout != null) {
149             removeView(mTitleLayout);
150             mTitleLayout = null;
151         }
152         if (view != null) {
153             addView(view);
154         }
155         requestLayout();
156     }
157
158     public void setTitle(CharSequence title) {
159         mTitle = title;
160         initTitle();
161     }
162
163     public void setSubtitle(CharSequence subtitle) {
164         mSubtitle = subtitle;
165         initTitle();
166     }
167
168     public CharSequence getTitle() {
169         return mTitle;
170     }
171
172     public CharSequence getSubtitle() {
173         return mSubtitle;
174     }
175
176     private void initTitle() {
177         if (mTitleLayout == null) {
178             LayoutInflater inflater = LayoutInflater.from(getContext());
179             inflater.inflate(R.layout.abs__action_bar_title_item, this);
180             mTitleLayout = (LinearLayout) getChildAt(getChildCount() - 1);
181             mTitleView = (TextView) mTitleLayout.findViewById(R.id.abs__action_bar_title);
182             mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.abs__action_bar_subtitle);
183             if (mTitleStyleRes != 0) {
184                 mTitleView.setTextAppearance(mContext, mTitleStyleRes);
185             }
186             if (mSubtitleStyleRes != 0) {
187                 mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
188             }
189         }
190
191         mTitleView.setText(mTitle);
192         mSubtitleView.setText(mSubtitle);
193
194         final boolean hasTitle = !TextUtils.isEmpty(mTitle);
195         final boolean hasSubtitle = !TextUtils.isEmpty(mSubtitle);
196         mSubtitleView.setVisibility(hasSubtitle ? VISIBLE : GONE);
197         mTitleLayout.setVisibility(hasTitle || hasSubtitle ? VISIBLE : GONE);
198         if (mTitleLayout.getParent() == null) {
199             addView(mTitleLayout);
200         }
201     }
202
203     public void initForMode(final ActionMode mode) {
204         if (mClose == null) {
205             LayoutInflater inflater = LayoutInflater.from(mContext);
206             mClose = (NineLinearLayout)inflater.inflate(R.layout.abs__action_mode_close_item, this, false);
207             addView(mClose);
208         } else if (mClose.getParent() == null) {
209             addView(mClose);
210         }
211
212         View closeButton = mClose.findViewById(R.id.abs__action_mode_close_button);
213         closeButton.setOnClickListener(new OnClickListener() {
214             public void onClick(View v) {
215                 mode.finish();
216             }
217         });
218
219         final MenuBuilder menu = (MenuBuilder) mode.getMenu();
220         if (mActionMenuPresenter != null) {
221             mActionMenuPresenter.dismissPopupMenus();
222         }
223         mActionMenuPresenter = new ActionMenuPresenter(mContext);
224         mActionMenuPresenter.setReserveOverflow(true);
225
226         final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
227                 LayoutParams.MATCH_PARENT);
228         if (!mSplitActionBar) {
229             menu.addMenuPresenter(mActionMenuPresenter);
230             mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
231             mMenuView.setBackgroundDrawable(null);
232             addView(mMenuView, layoutParams);
233         } else {
234             // Allow full screen width in split mode.
235             mActionMenuPresenter.setWidthLimit(
236                     getContext().getResources().getDisplayMetrics().widthPixels, true);
237             // No limit to the item count; use whatever will fit.
238             mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
239             // Span the whole width
240             layoutParams.width = LayoutParams.MATCH_PARENT;
241             layoutParams.height = mContentHeight;
242             menu.addMenuPresenter(mActionMenuPresenter);
243             mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
244             mMenuView.setBackgroundDrawable(mSplitBackground);
245             mSplitView.addView(mMenuView, layoutParams);
246         }
247
248         mAnimateInOnLayout = true;
249     }
250
251     public void closeMode() {
252         if (mAnimationMode == ANIMATE_OUT) {
253             // Called again during close; just finish what we were doing.
254             return;
255         }
256         if (mClose == null) {
257             killMode();
258             return;
259         }
260
261         finishAnimation();
262         mAnimationMode = ANIMATE_OUT;
263         mCurrentAnimation = makeOutAnimation();
264         mCurrentAnimation.start();
265     }
266
267     private void finishAnimation() {
268         final Animator a = mCurrentAnimation;
269         if (a != null) {
270             mCurrentAnimation = null;
271             a.end();
272         }
273     }
274
275     public void killMode() {
276         finishAnimation();
277         removeAllViews();
278         if (mSplitView != null) {
279             mSplitView.removeView(mMenuView);
280         }
281         mCustomView = null;
282         mMenuView = null;
283         mAnimateInOnLayout = false;
284     }
285
286     @Override
287     public boolean showOverflowMenu() {
288         if (mActionMenuPresenter != null) {
289             return mActionMenuPresenter.showOverflowMenu();
290         }
291         return false;
292     }
293
294     @Override
295     public boolean hideOverflowMenu() {
296         if (mActionMenuPresenter != null) {
297             return mActionMenuPresenter.hideOverflowMenu();
298         }
299         return false;
300     }
301
302     @Override
303     public boolean isOverflowMenuShowing() {
304         if (mActionMenuPresenter != null) {
305             return mActionMenuPresenter.isOverflowMenuShowing();
306         }
307         return false;
308     }
309
310     @Override
311     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
312         // Used by custom views if they don't supply layout params. Everything else
313         // added to an ActionBarContextView should have them already.
314         return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
315     }
316
317     @Override
318     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
319         return new MarginLayoutParams(getContext(), attrs);
320     }
321
322     @Override
323     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
324         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
325         if (widthMode != MeasureSpec.EXACTLY) {
326             throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
327                     "with android:layout_width=\"match_parent\" (or fill_parent)");
328         }
329
330         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
331         if (heightMode == MeasureSpec.UNSPECIFIED) {
332             throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
333                     "with android:layout_height=\"wrap_content\"");
334         }
335
336         final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
337
338         int maxHeight = mContentHeight > 0 ?
339                 mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
340
341         final int verticalPadding = getPaddingTop() + getPaddingBottom();
342         int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
343         final int height = maxHeight - verticalPadding;
344         final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
345
346         if (mClose != null) {
347             availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
348             MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
349             availableWidth -= lp.leftMargin + lp.rightMargin;
350         }
351
352         if (mMenuView != null && mMenuView.getParent() == this) {
353             availableWidth = measureChildView(mMenuView, availableWidth,
354                     childSpecHeight, 0);
355         }
356
357         if (mTitleLayout != null && mCustomView == null) {
358             availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
359         }
360
361         if (mCustomView != null) {
362             ViewGroup.LayoutParams lp = mCustomView.getLayoutParams();
363             final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
364                     MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
365             final int customWidth = lp.width >= 0 ?
366                     Math.min(lp.width, availableWidth) : availableWidth;
367             final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
368                     MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
369             final int customHeight = lp.height >= 0 ?
370                     Math.min(lp.height, height) : height;
371             mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode),
372                     MeasureSpec.makeMeasureSpec(customHeight, customHeightMode));
373         }
374
375         if (mContentHeight <= 0) {
376             int measuredHeight = 0;
377             final int count = getChildCount();
378             for (int i = 0; i < count; i++) {
379                 View v = getChildAt(i);
380                 int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
381                 if (paddedViewHeight > measuredHeight) {
382                     measuredHeight = paddedViewHeight;
383                 }
384             }
385             setMeasuredDimension(contentWidth, measuredHeight);
386         } else {
387             setMeasuredDimension(contentWidth, maxHeight);
388         }
389     }
390
391     private Animator makeInAnimation() {
392         mClose.setTranslationX(-mClose.getWidth() -
393                 ((MarginLayoutParams) mClose.getLayoutParams()).leftMargin);
394         ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX", 0);
395         buttonAnimator.setDuration(200);
396         buttonAnimator.addListener(this);
397         buttonAnimator.setInterpolator(new DecelerateInterpolator());
398
399         AnimatorSet set = new AnimatorSet();
400         AnimatorSet.Builder b = set.play(buttonAnimator);
401
402         if (mMenuView != null) {
403             final int count = mMenuView.getChildCount();
404             if (count > 0) {
405                 for (int i = count - 1, j = 0; i >= 0; i--, j++) {
406                     AnimatorProxy child = AnimatorProxy.wrap(mMenuView.getChildAt(i));
407                     child.setScaleY(0);
408                     ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0, 1);
409                     a.setDuration(100);
410                     a.setStartDelay(j * 70);
411                     b.with(a);
412                 }
413             }
414         }
415
416         return set;
417     }
418
419     private Animator makeOutAnimation() {
420         ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX",
421                 -mClose.getWidth() - ((MarginLayoutParams) mClose.getLayoutParams()).leftMargin);
422         buttonAnimator.setDuration(200);
423         buttonAnimator.addListener(this);
424         buttonAnimator.setInterpolator(new DecelerateInterpolator());
425
426         AnimatorSet set = new AnimatorSet();
427         AnimatorSet.Builder b = set.play(buttonAnimator);
428
429         if (mMenuView != null) {
430             final int count = mMenuView.getChildCount();
431             if (count > 0) {
432                 for (int i = 0; i < 0; i++) {
433                     AnimatorProxy child = AnimatorProxy.wrap(mMenuView.getChildAt(i));
434                     child.setScaleY(0);
435                     ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0);
436                     a.setDuration(100);
437                     a.setStartDelay(i * 70);
438                     b.with(a);
439                 }
440             }
441         }
442
443         return set;
444     }
445
446     @Override
447     protected void onLayout(boolean changed, int l, int t, int r, int b) {
448         int x = getPaddingLeft();
449         final int y = getPaddingTop();
450         final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
451
452         if (mClose != null && mClose.getVisibility() != GONE) {
453             MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
454             x += lp.leftMargin;
455             x += positionChild(mClose, x, y, contentHeight);
456             x += lp.rightMargin;
457
458             if (mAnimateInOnLayout) {
459                 mAnimationMode = ANIMATE_IN;
460                 mCurrentAnimation = makeInAnimation();
461                 mCurrentAnimation.start();
462                 mAnimateInOnLayout = false;
463             }
464         }
465
466         if (mTitleLayout != null && mCustomView == null) {
467             x += positionChild(mTitleLayout, x, y, contentHeight);
468         }
469
470         if (mCustomView != null) {
471             x += positionChild(mCustomView, x, y, contentHeight);
472         }
473
474         x = r - l - getPaddingRight();
475
476         if (mMenuView != null) {
477             x -= positionChildInverse(mMenuView, x, y, contentHeight);
478         }
479     }
480
481     @Override
482     public void onAnimationStart(Animator animation) {
483     }
484
485     @Override
486     public void onAnimationEnd(Animator animation) {
487         if (mAnimationMode == ANIMATE_OUT) {
488             killMode();
489         }
490         mAnimationMode = ANIMATE_IDLE;
491     }
492
493     @Override
494     public void onAnimationCancel(Animator animation) {
495     }
496
497     @Override
498     public void onAnimationRepeat(Animator animation) {
499     }
500
501     @Override
502     public boolean shouldDelayChildPressedState() {
503         return false;
504     }
505
506     @Override
507     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
508         if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
509             // Action mode started
510             //TODO event.setSource(this);
511             event.setClassName(getClass().getName());
512             event.setPackageName(getContext().getPackageName());
513             event.setContentDescription(mTitle);
514         } else {
515             //TODO super.onInitializeAccessibilityEvent(event);
516         }
517     }
518 }