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