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