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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 20 21import android.content.Context; 22import android.support.annotation.RestrictTo; 23import android.support.v4.view.ViewCompat; 24import android.support.v7.appcompat.R; 25import android.support.v7.view.ActionMode; 26import android.support.v7.view.menu.MenuBuilder; 27import android.text.TextUtils; 28import android.util.AttributeSet; 29import android.view.LayoutInflater; 30import android.view.View; 31import android.view.ViewGroup; 32import android.view.accessibility.AccessibilityEvent; 33import android.widget.LinearLayout; 34import android.widget.TextView; 35 36/** 37 * @hide 38 */ 39@RestrictTo(LIBRARY_GROUP) 40public class ActionBarContextView extends AbsActionBarView { 41 private static final String TAG = "ActionBarContextView"; 42 43 private CharSequence mTitle; 44 private CharSequence mSubtitle; 45 46 private View mClose; 47 private View mCustomView; 48 private LinearLayout mTitleLayout; 49 private TextView mTitleView; 50 private TextView mSubtitleView; 51 private int mTitleStyleRes; 52 private int mSubtitleStyleRes; 53 private boolean mTitleOptional; 54 private int mCloseItemLayout; 55 56 public ActionBarContextView(Context context) { 57 this(context, null); 58 } 59 60 public ActionBarContextView(Context context, AttributeSet attrs) { 61 this(context, attrs, R.attr.actionModeStyle); 62 } 63 64 public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) { 65 super(context, attrs, defStyle); 66 67 final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, 68 R.styleable.ActionMode, defStyle, 0); 69 ViewCompat.setBackground(this, a.getDrawable(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 86 public void onDetachedFromWindow() { 87 super.onDetachedFromWindow(); 88 if (mActionMenuPresenter != null) { 89 mActionMenuPresenter.hideOverflowMenu(); 90 mActionMenuPresenter.hideSubMenus(); 91 } 92 } 93 94 @Override 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 @Override 171 public void onClick(View v) { 172 mode.finish(); 173 } 174 }); 175 176 final MenuBuilder menu = (MenuBuilder) mode.getMenu(); 177 if (mActionMenuPresenter != null) { 178 mActionMenuPresenter.dismissPopupMenus(); 179 } 180 mActionMenuPresenter = new ActionMenuPresenter(getContext()); 181 mActionMenuPresenter.setReserveOverflow(true); 182 183 final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, 184 LayoutParams.MATCH_PARENT); 185 menu.addMenuPresenter(mActionMenuPresenter, mPopupContext); 186 mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); 187 ViewCompat.setBackground(mMenuView, null); 188 addView(mMenuView, layoutParams); 189 } 190 191 public void closeMode() { 192 if (mClose == null) { 193 killMode(); 194 return; 195 } 196 } 197 198 public void killMode() { 199 removeAllViews(); 200 mCustomView = null; 201 mMenuView = null; 202 } 203 204 @Override 205 public boolean showOverflowMenu() { 206 if (mActionMenuPresenter != null) { 207 return mActionMenuPresenter.showOverflowMenu(); 208 } 209 return false; 210 } 211 212 @Override 213 public boolean hideOverflowMenu() { 214 if (mActionMenuPresenter != null) { 215 return mActionMenuPresenter.hideOverflowMenu(); 216 } 217 return false; 218 } 219 220 @Override 221 public boolean isOverflowMenuShowing() { 222 if (mActionMenuPresenter != null) { 223 return mActionMenuPresenter.isOverflowMenuShowing(); 224 } 225 return false; 226 } 227 228 @Override 229 protected ViewGroup.LayoutParams generateDefaultLayoutParams() { 230 // Used by custom views if they don't supply layout params. Everything else 231 // added to an ActionBarContextView should have them already. 232 return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 233 } 234 235 @Override 236 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { 237 return new MarginLayoutParams(getContext(), attrs); 238 } 239 240 @Override 241 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 242 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 243 if (widthMode != MeasureSpec.EXACTLY) { 244 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 245 "with android:layout_width=\"match_parent\" (or fill_parent)"); 246 } 247 248 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 249 if (heightMode == MeasureSpec.UNSPECIFIED) { 250 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 251 "with android:layout_height=\"wrap_content\""); 252 } 253 254 final int contentWidth = MeasureSpec.getSize(widthMeasureSpec); 255 256 int maxHeight = mContentHeight > 0 ? 257 mContentHeight : MeasureSpec.getSize(heightMeasureSpec); 258 259 final int verticalPadding = getPaddingTop() + getPaddingBottom(); 260 int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight(); 261 final int height = maxHeight - verticalPadding; 262 final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); 263 264 if (mClose != null) { 265 availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0); 266 MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams(); 267 availableWidth -= lp.leftMargin + lp.rightMargin; 268 } 269 270 if (mMenuView != null && mMenuView.getParent() == this) { 271 availableWidth = measureChildView(mMenuView, availableWidth, 272 childSpecHeight, 0); 273 } 274 275 if (mTitleLayout != null && mCustomView == null) { 276 if (mTitleOptional) { 277 final int titleWidthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 278 mTitleLayout.measure(titleWidthSpec, childSpecHeight); 279 final int titleWidth = mTitleLayout.getMeasuredWidth(); 280 final boolean titleFits = titleWidth <= availableWidth; 281 if (titleFits) { 282 availableWidth -= titleWidth; 283 } 284 mTitleLayout.setVisibility(titleFits ? VISIBLE : GONE); 285 } else { 286 availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0); 287 } 288 } 289 290 if (mCustomView != null) { 291 ViewGroup.LayoutParams lp = mCustomView.getLayoutParams(); 292 final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ? 293 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 294 final int customWidth = lp.width >= 0 ? 295 Math.min(lp.width, availableWidth) : availableWidth; 296 final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ? 297 MeasureSpec.EXACTLY : MeasureSpec.AT_MOST; 298 final int customHeight = lp.height >= 0 ? 299 Math.min(lp.height, height) : height; 300 mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode), 301 MeasureSpec.makeMeasureSpec(customHeight, customHeightMode)); 302 } 303 304 if (mContentHeight <= 0) { 305 int measuredHeight = 0; 306 final int count = getChildCount(); 307 for (int i = 0; i < count; i++) { 308 View v = getChildAt(i); 309 int paddedViewHeight = v.getMeasuredHeight() + verticalPadding; 310 if (paddedViewHeight > measuredHeight) { 311 measuredHeight = paddedViewHeight; 312 } 313 } 314 setMeasuredDimension(contentWidth, measuredHeight); 315 } else { 316 setMeasuredDimension(contentWidth, maxHeight); 317 } 318 } 319 320 @Override 321 protected void onLayout(boolean changed, int l, int t, int r, int b) { 322 final boolean isLayoutRtl = ViewUtils.isLayoutRtl(this); 323 int x = isLayoutRtl ? r - l - getPaddingRight() : getPaddingLeft(); 324 final int y = getPaddingTop(); 325 final int contentHeight = b - t - getPaddingTop() - getPaddingBottom(); 326 327 if (mClose != null && mClose.getVisibility() != GONE) { 328 MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams(); 329 final int startMargin = (isLayoutRtl ? lp.rightMargin : lp.leftMargin); 330 final int endMargin = (isLayoutRtl ? lp.leftMargin : lp.rightMargin); 331 x = next(x, startMargin, isLayoutRtl); 332 x += positionChild(mClose, x, y, contentHeight, isLayoutRtl); 333 x = next(x, endMargin, isLayoutRtl); 334 } 335 336 if (mTitleLayout != null && mCustomView == null && mTitleLayout.getVisibility() != GONE) { 337 x += positionChild(mTitleLayout, x, y, contentHeight, isLayoutRtl); 338 } 339 340 if (mCustomView != null) { 341 x += positionChild(mCustomView, x, y, contentHeight, isLayoutRtl); 342 } 343 344 x = isLayoutRtl ? getPaddingLeft() : r - l - getPaddingRight(); 345 346 if (mMenuView != null) { 347 x += positionChild(mMenuView, x, y, contentHeight, !isLayoutRtl); 348 } 349 } 350 351 @Override 352 public boolean shouldDelayChildPressedState() { 353 return false; 354 } 355 356 @Override 357 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 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 public void setTitleOptional(boolean titleOptional) { 370 if (titleOptional != mTitleOptional) { 371 requestLayout(); 372 } 373 mTitleOptional = titleOptional; 374 } 375 376 public boolean isTitleOptional() { 377 return mTitleOptional; 378 } 379} 380