ActionBarContextView.java revision c39d9c75590eca86a5e7e32a8824ba04a0d42e9b
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
17package android.support.v7.widget;
18
19import android.content.Context;
20import android.os.Build;
21import android.support.annotation.RestrictTo;
22import android.support.v4.view.ViewCompat;
23import android.support.v7.appcompat.R;
24import android.support.v7.view.ActionMode;
25import android.support.v7.view.menu.MenuBuilder;
26import android.text.TextUtils;
27import android.util.AttributeSet;
28import android.view.LayoutInflater;
29import android.view.View;
30import android.view.ViewGroup;
31import android.view.accessibility.AccessibilityEvent;
32import android.widget.LinearLayout;
33import android.widget.TextView;
34
35import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
36
37/**
38 * @hide
39 */
40@RestrictTo(GROUP_ID)
41public class ActionBarContextView extends AbsActionBarView {
42    private static final String TAG = "ActionBarContextView";
43
44    private CharSequence mTitle;
45    private CharSequence mSubtitle;
46
47    private View mClose;
48    private View mCustomView;
49    private LinearLayout mTitleLayout;
50    private TextView mTitleView;
51    private TextView mSubtitleView;
52    private int mTitleStyleRes;
53    private int mSubtitleStyleRes;
54    private boolean mTitleOptional;
55    private int mCloseItemLayout;
56
57    public ActionBarContextView(Context context) {
58        this(context, null);
59    }
60
61    public ActionBarContextView(Context context, AttributeSet attrs) {
62        this(context, attrs, R.attr.actionModeStyle);
63    }
64
65    public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) {
66        super(context, attrs, defStyle);
67
68        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
69                R.styleable.ActionMode, defStyle, 0);
70        ViewCompat.setBackground(this, a.getDrawable(R.styleable.ActionMode_background));
71        mTitleStyleRes = a.getResourceId(
72                R.styleable.ActionMode_titleTextStyle, 0);
73        mSubtitleStyleRes = a.getResourceId(
74                R.styleable.ActionMode_subtitleTextStyle, 0);
75
76        mContentHeight = a.getLayoutDimension(
77                R.styleable.ActionMode_height, 0);
78
79        mCloseItemLayout = a.getResourceId(
80                R.styleable.ActionMode_closeItemLayout,
81                R.layout.abc_action_mode_close_item_material);
82
83        a.recycle();
84    }
85
86    @Override
87    public void onDetachedFromWindow() {
88        super.onDetachedFromWindow();
89        if (mActionMenuPresenter != null) {
90            mActionMenuPresenter.hideOverflowMenu();
91            mActionMenuPresenter.hideSubMenus();
92        }
93    }
94
95    public void setContentHeight(int height) {
96        mContentHeight = height;
97    }
98
99    public void setCustomView(View view) {
100        if (mCustomView != null) {
101            removeView(mCustomView);
102        }
103        mCustomView = view;
104        if (view != null && mTitleLayout != null) {
105            removeView(mTitleLayout);
106            mTitleLayout = null;
107        }
108        if (view != null) {
109            addView(view);
110        }
111        requestLayout();
112    }
113
114    public void setTitle(CharSequence title) {
115        mTitle = title;
116        initTitle();
117    }
118
119    public void setSubtitle(CharSequence subtitle) {
120        mSubtitle = subtitle;
121        initTitle();
122    }
123
124    public CharSequence getTitle() {
125        return mTitle;
126    }
127
128    public CharSequence getSubtitle() {
129        return mSubtitle;
130    }
131
132    private void initTitle() {
133        if (mTitleLayout == null) {
134            LayoutInflater inflater = LayoutInflater.from(getContext());
135            inflater.inflate(R.layout.abc_action_bar_title_item, this);
136            mTitleLayout = (LinearLayout) getChildAt(getChildCount() - 1);
137            mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
138            mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
139            if (mTitleStyleRes != 0) {
140                mTitleView.setTextAppearance(getContext(), mTitleStyleRes);
141            }
142            if (mSubtitleStyleRes != 0) {
143                mSubtitleView.setTextAppearance(getContext(), mSubtitleStyleRes);
144            }
145        }
146
147        mTitleView.setText(mTitle);
148        mSubtitleView.setText(mSubtitle);
149
150        final boolean hasTitle = !TextUtils.isEmpty(mTitle);
151        final boolean hasSubtitle = !TextUtils.isEmpty(mSubtitle);
152        mSubtitleView.setVisibility(hasSubtitle ? VISIBLE : GONE);
153        mTitleLayout.setVisibility(hasTitle || hasSubtitle ? VISIBLE : GONE);
154        if (mTitleLayout.getParent() == null) {
155            addView(mTitleLayout);
156        }
157    }
158
159    public void initForMode(final ActionMode mode) {
160        if (mClose == null) {
161            LayoutInflater inflater = LayoutInflater.from(getContext());
162            mClose = inflater.inflate(mCloseItemLayout, this, false);
163            addView(mClose);
164        } else if (mClose.getParent() == null) {
165            addView(mClose);
166        }
167
168        View closeButton = mClose.findViewById(R.id.action_mode_close_button);
169        closeButton.setOnClickListener(new OnClickListener() {
170            public void onClick(View v) {
171                mode.finish();
172            }
173        });
174
175        final MenuBuilder menu = (MenuBuilder) mode.getMenu();
176        if (mActionMenuPresenter != null) {
177            mActionMenuPresenter.dismissPopupMenus();
178        }
179        mActionMenuPresenter = new ActionMenuPresenter(getContext());
180        mActionMenuPresenter.setReserveOverflow(true);
181
182        final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
183                LayoutParams.MATCH_PARENT);
184        menu.addMenuPresenter(mActionMenuPresenter, mPopupContext);
185        mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
186        ViewCompat.setBackground(mMenuView, null);
187        addView(mMenuView, layoutParams);
188    }
189
190    public void closeMode() {
191        if (mClose == null) {
192            killMode();
193            return;
194        }
195    }
196
197    public void killMode() {
198        removeAllViews();
199        mCustomView = null;
200        mMenuView = null;
201    }
202
203    @Override
204    public boolean showOverflowMenu() {
205        if (mActionMenuPresenter != null) {
206            return mActionMenuPresenter.showOverflowMenu();
207        }
208        return false;
209    }
210
211    @Override
212    public boolean hideOverflowMenu() {
213        if (mActionMenuPresenter != null) {
214            return mActionMenuPresenter.hideOverflowMenu();
215        }
216        return false;
217    }
218
219    @Override
220    public boolean isOverflowMenuShowing() {
221        if (mActionMenuPresenter != null) {
222            return mActionMenuPresenter.isOverflowMenuShowing();
223        }
224        return false;
225    }
226
227    @Override
228    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
229        // Used by custom views if they don't supply layout params. Everything else
230        // added to an ActionBarContextView should have them already.
231        return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
232    }
233
234    @Override
235    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
236        return new MarginLayoutParams(getContext(), attrs);
237    }
238
239    @Override
240    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
241        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
242        if (widthMode != MeasureSpec.EXACTLY) {
243            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
244                    "with android:layout_width=\"match_parent\" (or fill_parent)");
245        }
246
247        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
248        if (heightMode == MeasureSpec.UNSPECIFIED) {
249            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
250                    "with android:layout_height=\"wrap_content\"");
251        }
252
253        final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
254
255        int maxHeight = mContentHeight > 0 ?
256                mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
257
258        final int verticalPadding = getPaddingTop() + getPaddingBottom();
259        int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
260        final int height = maxHeight - verticalPadding;
261        final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
262
263        if (mClose != null) {
264            availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
265            MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
266            availableWidth -= lp.leftMargin + lp.rightMargin;
267        }
268
269        if (mMenuView != null && mMenuView.getParent() == this) {
270            availableWidth = measureChildView(mMenuView, availableWidth,
271                    childSpecHeight, 0);
272        }
273
274        if (mTitleLayout != null && mCustomView == null) {
275            if (mTitleOptional) {
276                final int titleWidthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
277                mTitleLayout.measure(titleWidthSpec, childSpecHeight);
278                final int titleWidth = mTitleLayout.getMeasuredWidth();
279                final boolean titleFits = titleWidth <= availableWidth;
280                if (titleFits) {
281                    availableWidth -= titleWidth;
282                }
283                mTitleLayout.setVisibility(titleFits ? VISIBLE : GONE);
284            } else {
285                availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
286            }
287        }
288
289        if (mCustomView != null) {
290            ViewGroup.LayoutParams lp = mCustomView.getLayoutParams();
291            final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
292                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
293            final int customWidth = lp.width >= 0 ?
294                    Math.min(lp.width, availableWidth) : availableWidth;
295            final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
296                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
297            final int customHeight = lp.height >= 0 ?
298                    Math.min(lp.height, height) : height;
299            mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode),
300                    MeasureSpec.makeMeasureSpec(customHeight, customHeightMode));
301        }
302
303        if (mContentHeight <= 0) {
304            int measuredHeight = 0;
305            final int count = getChildCount();
306            for (int i = 0; i < count; i++) {
307                View v = getChildAt(i);
308                int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
309                if (paddedViewHeight > measuredHeight) {
310                    measuredHeight = paddedViewHeight;
311                }
312            }
313            setMeasuredDimension(contentWidth, measuredHeight);
314        } else {
315            setMeasuredDimension(contentWidth, maxHeight);
316        }
317    }
318
319    @Override
320    protected void onLayout(boolean changed, int l, int t, int r, int b) {
321        final boolean isLayoutRtl = ViewUtils.isLayoutRtl(this);
322        int x = isLayoutRtl ? r - l - getPaddingRight() : getPaddingLeft();
323        final int y = getPaddingTop();
324        final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
325
326        if (mClose != null && mClose.getVisibility() != GONE) {
327            MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
328            final int startMargin = (isLayoutRtl ? lp.rightMargin : lp.leftMargin);
329            final int endMargin = (isLayoutRtl ? lp.leftMargin : lp.rightMargin);
330            x = next(x, startMargin, isLayoutRtl);
331            x += positionChild(mClose, x, y, contentHeight, isLayoutRtl);
332            x = next(x, endMargin, isLayoutRtl);
333        }
334
335        if (mTitleLayout != null && mCustomView == null && mTitleLayout.getVisibility() != GONE) {
336            x += positionChild(mTitleLayout, x, y, contentHeight, isLayoutRtl);
337        }
338
339        if (mCustomView != null) {
340            x += positionChild(mCustomView, x, y, contentHeight, isLayoutRtl);
341        }
342
343        x = isLayoutRtl ? getPaddingLeft() : r - l - getPaddingRight();
344
345        if (mMenuView != null) {
346            x += positionChild(mMenuView, x, y, contentHeight, !isLayoutRtl);
347        }
348    }
349
350    @Override
351    public boolean shouldDelayChildPressedState() {
352        return false;
353    }
354
355    @Override
356    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
357        if (Build.VERSION.SDK_INT >= 14) {
358            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
359                // Action mode started
360                event.setSource(this);
361                event.setClassName(getClass().getName());
362                event.setPackageName(getContext().getPackageName());
363                event.setContentDescription(mTitle);
364            } else {
365                super.onInitializeAccessibilityEvent(event);
366            }
367        }
368    }
369
370    public void setTitleOptional(boolean titleOptional) {
371        if (titleOptional != mTitleOptional) {
372            requestLayout();
373        }
374        mTitleOptional = titleOptional;
375    }
376
377    public boolean isTitleOptional() {
378        return mTitleOptional;
379    }
380}
381