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