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