diff options
18 files changed, 1784 insertions, 84 deletions
diff --git a/api/current.txt b/api/current.txt index 73cf152..5d875a0 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1075,6 +1075,7 @@ package android { field public static final int strokeOpacity = 16843812; // 0x1010424 field public static final int strokeWidth = 16843813; // 0x1010425 field public static final int subtitle = 16843473; // 0x10102d1 + field public static final int subtitleTextAppearance = 16843834; // 0x101043a field public static final int subtitleTextStyle = 16843513; // 0x10102f9 field public static final int subtypeExtraValue = 16843674; // 0x101039a field public static final int subtypeId = 16843713; // 0x10103c1 @@ -1190,6 +1191,7 @@ package android { field public static final int tintMode = 16843798; // 0x1010416 field public static final int title = 16843233; // 0x10101e1 field public static final int titleCondensed = 16843234; // 0x10101e2 + field public static final int titleTextAppearance = 16843833; // 0x1010439 field public static final int titleTextStyle = 16843512; // 0x10102f8 field public static final int toAlpha = 16843211; // 0x10101cb field public static final int toDegrees = 16843188; // 0x10101b4 @@ -3088,6 +3090,7 @@ package android.app { ctor public ActionBar.LayoutParams(int); ctor public ActionBar.LayoutParams(android.app.ActionBar.LayoutParams); ctor public ActionBar.LayoutParams(android.view.ViewGroup.LayoutParams); + ctor public ActionBar.LayoutParams(android.view.ViewGroup.MarginLayoutParams); field public int gravity; } @@ -3263,6 +3266,7 @@ package android.app { method public void reportFullyDrawn(); method public final boolean requestWindowFeature(int); method public final void runOnUiThread(java.lang.Runnable); + method public void setActionBar(android.widget.Toolbar); method public void setActivityLabelAndIcon(java.lang.CharSequence, android.graphics.Bitmap); method public void setContentTransitionManager(android.transition.TransitionManager); method public void setContentView(int); @@ -32514,6 +32518,7 @@ package android.widget { method public android.view.Menu getMenu(); method public void onConfigurationChanged(android.content.res.Configuration); method public void onDetachedFromWindow(); + method public void setOnMenuItemClickListener(android.widget.ActionMenuView.OnMenuItemClickListener); } public static class ActionMenuView.LayoutParams extends android.widget.LinearLayout.LayoutParams { @@ -32523,6 +32528,10 @@ package android.widget { ctor public ActionMenuView.LayoutParams(int, int); } + public static abstract interface ActionMenuView.OnMenuItemClickListener { + method public abstract boolean onMenuItemClick(android.view.MenuItem); + } + public abstract interface Adapter { method public abstract int getCount(); method public abstract java.lang.Object getItem(int); @@ -34676,6 +34685,50 @@ package android.widget { method public void setTextOn(java.lang.CharSequence); } + public class Toolbar extends android.view.ViewGroup { + ctor public Toolbar(android.content.Context); + ctor public Toolbar(android.content.Context, android.util.AttributeSet); + ctor public Toolbar(android.content.Context, android.util.AttributeSet, int); + ctor public Toolbar(android.content.Context, android.util.AttributeSet, int, int); + method public android.graphics.drawable.Drawable getLogo(); + method public java.lang.CharSequence getLogoDescription(); + method public android.view.Menu getMenu(); + method public android.graphics.drawable.Drawable getNavigationIcon(); + method public java.lang.CharSequence getSubtitle(); + method public java.lang.CharSequence getTitle(); + method public void inflateMenu(int); + method protected void onLayout(boolean, int, int, int, int); + method public void setLogo(int); + method public void setLogo(android.graphics.drawable.Drawable); + method public void setLogoDescription(int); + method public void setLogoDescription(java.lang.CharSequence); + method public void setNavigationDescription(int); + method public void setNavigationDescription(java.lang.CharSequence); + method public void setNavigationIcon(int); + method public void setNavigationIcon(android.graphics.drawable.Drawable); + method public void setNavigationOnClickListener(android.view.View.OnClickListener); + method public void setOnMenuItemClickListener(android.widget.Toolbar.OnMenuItemClickListener); + method public void setSubtitle(int); + method public void setSubtitle(java.lang.CharSequence); + method public void setTitle(int); + method public void setTitle(java.lang.CharSequence); + } + + public static class Toolbar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams { + ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet); + ctor public Toolbar.LayoutParams(int, int); + ctor public Toolbar.LayoutParams(int, int, int); + ctor public Toolbar.LayoutParams(int); + ctor public Toolbar.LayoutParams(android.widget.Toolbar.LayoutParams); + ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams); + ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams); + field public int gravity; + } + + public static abstract interface Toolbar.OnMenuItemClickListener { + method public abstract boolean onMenuItemClick(android.view.MenuItem); + } + public deprecated class TwoLineListItem extends android.widget.RelativeLayout { ctor public TwoLineListItem(android.content.Context); ctor public TwoLineListItem(android.content.Context, android.util.AttributeSet); diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index 29c1c56..13ae352 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -20,9 +20,11 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.view.ActionMode; import android.view.Gravity; import android.view.View; import android.view.ViewDebug; @@ -30,31 +32,57 @@ import android.view.ViewGroup; import android.view.ViewGroup.MarginLayoutParams; import android.view.Window; import android.widget.SpinnerAdapter; +import android.widget.Toolbar; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Map; /** - * A window feature at the top of the activity that may display the activity title, navigation - * modes, and other interactive items. + * A primary toolbar within the activity that may display the activity title, application-level + * navigation affordances, and other interactive items. + * * <p>Beginning with Android 3.0 (API level 11), the action bar appears at the top of an * activity's window when the activity uses the system's {@link * android.R.style#Theme_Holo Holo} theme (or one of its descendant themes), which is the default. * You may otherwise add the action bar by calling {@link * android.view.Window#requestFeature requestFeature(FEATURE_ACTION_BAR)} or by declaring it in a * custom theme with the {@link android.R.styleable#Theme_windowActionBar windowActionBar} property. - * <p>By default, the action bar shows the application icon on + * </p> + * + * <p>Beginning with Android L (API level 21), the action bar may be represented by any + * Toolbar widget within the application layout. The application may signal to the Activity + * which Toolbar should be treated as the Activity's action bar. Activities that use this + * feature should use one of the supplied <code>.NoActionBar</code> themes, set the + * {@link android.R.styleable#Theme_windowActionBar windowActionBar} attribute to <code>false</code> + * or otherwise not request the window feature.</p> + * + * <p>By adjusting the window features requested by the theme and the layouts used for + * an Activity's content view, an app can use the standard system action bar on older platform + * releases and the newer inline toolbars on newer platform releases. The <code>ActionBar</code> + * object obtained from the Activity can be used to control either configuration transparently.</p> + * + * <p>When using the Holo themes the action bar shows the application icon on * the left, followed by the activity title. If your activity has an options menu, you can make * select items accessible directly from the action bar as "action items". You can also * modify various characteristics of the action bar or remove it completely.</p> + * + * <p>When using the Quantum themes (default in API 21 or newer) the navigation button + * (formerly "Home") takes over the space previously occupied by the application icon. + * Apps wishing to express a stronger branding should use their brand colors heavily + * in the action bar and other application chrome or use a {@link #setLogo(int) logo} + * in place of their standard title text.</p> + * * <p>From your activity, you can retrieve an instance of {@link ActionBar} by calling {@link * android.app.Activity#getActionBar getActionBar()}.</p> + * * <p>In some cases, the action bar may be overlayed by another bar that enables contextual actions, * using an {@link android.view.ActionMode}. For example, when the user selects one or more items in * your activity, you can enable an action mode that offers actions specific to the selected * items, with a UI that temporarily replaces the action bar. Although the UI may occupy the * same space, the {@link android.view.ActionMode} APIs are distinct and independent from those for - * {@link ActionBar}. + * {@link ActionBar}.</p> + * * <div class="special reference"> * <h3>Developer Guides</h3> * <p>For information about how to use the action bar, including how to add action items, navigation @@ -904,6 +932,31 @@ public abstract class ActionBar { */ public void setHomeActionContentDescription(int resId) { } + /** @hide */ + public void setDefaultDisplayHomeAsUpEnabled(boolean enabled) { + } + + /** @hide */ + public void setShowHideAnimationEnabled(boolean enabled) { + } + + /** @hide */ + public void onConfigurationChanged(Configuration config) { + } + + /** @hide */ + public void dispatchMenuVisibilityChanged(boolean visible) { + } + + /** @hide */ + public void captureSharedElements(Map<String, View> sharedElements) { + } + + /** @hide */ + public ActionMode startActionMode(ActionMode.Callback callback) { + return null; + } + /** * Listener interface for ActionBar navigation events. * @@ -1145,49 +1198,40 @@ public abstract class ActionBar { * * @attr ref android.R.styleable#ActionBar_LayoutParams_layout_gravity */ - public static class LayoutParams extends MarginLayoutParams { + public static class LayoutParams extends ViewGroup.MarginLayoutParams { /** * Gravity for the view associated with these LayoutParams. * * @see android.view.Gravity */ @ViewDebug.ExportedProperty(category = "layout", mapping = { - @ViewDebug.IntToString(from = -1, to = "NONE"), - @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), - @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), - @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), - @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"), - @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"), - @ViewDebug.IntToString(from = Gravity.START, to = "START"), - @ViewDebug.IntToString(from = Gravity.END, to = "END"), - @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"), - @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"), - @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"), - @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"), - @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), - @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") + @ViewDebug.IntToString(from = -1, to = "NONE"), + @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), + @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), + @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), + @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"), + @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"), + @ViewDebug.IntToString(from = Gravity.START, to = "START"), + @ViewDebug.IntToString(from = Gravity.END, to = "END"), + @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"), + @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"), + @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"), + @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"), + @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), + @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") }) public int gravity = Gravity.NO_GRAVITY; public LayoutParams(@NonNull Context c, AttributeSet attrs) { super(c, attrs); - - TypedArray a = c.obtainStyledAttributes(attrs, - com.android.internal.R.styleable.ActionBar_LayoutParams); - gravity = a.getInt( - com.android.internal.R.styleable.ActionBar_LayoutParams_layout_gravity, - Gravity.NO_GRAVITY); - a.recycle(); } public LayoutParams(int width, int height) { super(width, height); - this.gravity = Gravity.CENTER_VERTICAL | Gravity.START; } public LayoutParams(int width, int height, int gravity) { super(width, height); - this.gravity = gravity; } public LayoutParams(int gravity) { @@ -1196,12 +1240,14 @@ public abstract class ActionBar { public LayoutParams(LayoutParams source) { super(source); - - this.gravity = source.gravity; } public LayoutParams(ViewGroup.LayoutParams source) { super(source); } + + public LayoutParams(MarginLayoutParams source) { + super(source); + } } } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 20e7311..e8fdcaf 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -23,7 +23,9 @@ import android.transition.TransitionManager; import android.util.ArrayMap; import android.util.Pair; import android.util.SuperNotCalledException; -import com.android.internal.app.ActionBarImpl; +import android.widget.Toolbar; +import com.android.internal.app.WindowDecorActionBar; +import com.android.internal.app.ToolbarActionBar; import com.android.internal.policy.PolicyManager; import android.annotation.IntDef; @@ -723,7 +725,7 @@ public class Activity extends ContextThemeWrapper /*package*/ boolean mWindowAdded = false; /*package*/ boolean mVisibleFromServer = false; /*package*/ boolean mVisibleFromClient = true; - /*package*/ ActionBarImpl mActionBar = null; + /*package*/ ActionBar mActionBar = null; private boolean mEnableDefaultActionBarUp; private CharSequence mTitle; @@ -1906,15 +1908,39 @@ public class Activity extends ContextThemeWrapper */ @Nullable public ActionBar getActionBar() { - initActionBar(); + initWindowDecorActionBar(); return mActionBar; } + + /** + * Set a {@link android.widget.Toolbar Toolbar} to act as the {@link ActionBar} for this + * Activity window. + * + * <p>When set to a non-null value the {@link #getActionBar()} method will return + * an {@link ActionBar} object that can be used to control the given toolbar as if it were + * a traditional window decor action bar. The toolbar's menu will be populated with the + * Activity's options menu and the navigation button will be wired through the standard + * {@link android.R.id#home home} menu select action.</p> + * + * <p>In order to use a Toolbar within the Activity's window content the application + * must not request the window feature {@link Window#FEATURE_ACTION_BAR FEATURE_ACTION_BAR}.</p> + * + * @param actionBar Toolbar to set as the Activity's action bar + */ + public void setActionBar(@Nullable Toolbar actionBar) { + if (getActionBar() instanceof WindowDecorActionBar) { + throw new IllegalStateException("This Activity already has an action bar supplied " + + "by the window decor. Do not request Window.FEATURE_ACTION_BAR and set " + + "android:windowActionBar to false in your theme to use a Toolbar instead."); + } + mActionBar = new ToolbarActionBar(actionBar); + } /** * Creates a new ActionBar, locates the inflated ActionBarView, * initializes the ActionBar with the view, and sets mActionBar. */ - private void initActionBar() { + private void initWindowDecorActionBar() { Window window = getWindow(); // Initializing the window decor can change window feature flags. @@ -1925,7 +1951,7 @@ public class Activity extends ContextThemeWrapper return; } - mActionBar = new ActionBarImpl(this); + mActionBar = new WindowDecorActionBar(this); mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp); mWindow.setDefaultIcon(mActivityInfo.getIconResource()); @@ -1943,7 +1969,7 @@ public class Activity extends ContextThemeWrapper */ public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); - initActionBar(); + initWindowDecorActionBar(); } /** @@ -1963,7 +1989,7 @@ public class Activity extends ContextThemeWrapper */ public void setContentView(View view) { getWindow().setContentView(view); - initActionBar(); + initWindowDecorActionBar(); } /** @@ -1979,7 +2005,7 @@ public class Activity extends ContextThemeWrapper */ public void setContentView(View view, ViewGroup.LayoutParams params) { getWindow().setContentView(view, params); - initActionBar(); + initWindowDecorActionBar(); } /** @@ -1991,7 +2017,7 @@ public class Activity extends ContextThemeWrapper */ public void addContentView(View view, ViewGroup.LayoutParams params) { getWindow().addContentView(view, params); - initActionBar(); + initWindowDecorActionBar(); } /** @@ -2636,7 +2662,7 @@ public class Activity extends ContextThemeWrapper */ public boolean onMenuOpened(int featureId, Menu menu) { if (featureId == Window.FEATURE_ACTION_BAR) { - initActionBar(); + initWindowDecorActionBar(); if (mActionBar != null) { mActionBar.dispatchMenuVisibilityChanged(true); } else { @@ -2717,7 +2743,7 @@ public class Activity extends ContextThemeWrapper break; case Window.FEATURE_ACTION_BAR: - initActionBar(); + initWindowDecorActionBar(); mActionBar.dispatchMenuVisibilityChanged(false); break; } @@ -3417,7 +3443,7 @@ public class Activity extends ContextThemeWrapper public MenuInflater getMenuInflater() { // Make sure that action views can get an appropriate theme. if (mMenuInflater == null) { - initActionBar(); + initWindowDecorActionBar(); if (mActionBar != null) { mMenuInflater = new MenuInflater(mActionBar.getThemedContext(), this); } else { @@ -5139,7 +5165,7 @@ public class Activity extends ContextThemeWrapper @Nullable @Override public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { - initActionBar(); + initWindowDecorActionBar(); if (mActionBar != null) { return mActionBar.startActionMode(callback); } diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index fb96d8d..07583fd 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -17,7 +17,7 @@ package android.app; import android.content.pm.ApplicationInfo; -import com.android.internal.app.ActionBarImpl; +import com.android.internal.app.WindowDecorActionBar; import com.android.internal.policy.PolicyManager; import android.content.ComponentName; @@ -87,7 +87,7 @@ public class Dialog implements DialogInterface, Window.Callback, final WindowManager mWindowManager; Window mWindow; View mDecor; - private ActionBarImpl mActionBar; + private ActionBar mActionBar; /** * This field should be made private, so it is hidden from the SDK. * {@hide} @@ -280,7 +280,7 @@ public class Dialog implements DialogInterface, Window.Callback, final ApplicationInfo info = mContext.getApplicationInfo(); mWindow.setDefaultIcon(info.icon); mWindow.setDefaultLogo(info.logo); - mActionBar = new ActionBarImpl(this); + mActionBar = new WindowDecorActionBar(this); } WindowManager.LayoutParams l = mWindow.getAttributes(); diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java index 1f405a7..e4575e5 100644 --- a/core/java/android/widget/ActionMenuPresenter.java +++ b/core/java/android/widget/ActionMenuPresenter.java @@ -640,16 +640,6 @@ public class ActionMenuPresenter extends BaseMenuPresenter } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { - // Fill available height - heightMeasureSpec = MeasureSpec.makeMeasureSpec( - MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY); - } - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - - @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setCanOpenPopup(true); diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java index 32c7086..3975edf 100644 --- a/core/java/android/widget/ActionMenuView.java +++ b/core/java/android/widget/ActionMenuView.java @@ -20,6 +20,7 @@ import android.content.res.Configuration; import android.util.AttributeSet; import android.view.Gravity; import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; @@ -27,6 +28,7 @@ import android.view.accessibility.AccessibilityEvent; import com.android.internal.view.menu.ActionMenuItemView; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuItemImpl; +import com.android.internal.view.menu.MenuPresenter; import com.android.internal.view.menu.MenuView; /** @@ -50,6 +52,8 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo private int mMinCellSize; private int mGeneratedItemPadding; + private OnMenuItemClickListener mOnMenuItemClickListener; + public ActionMenuView(Context context) { this(context, null); } @@ -78,6 +82,10 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo } } + public void setOnMenuItemClickListener(OnMenuItemClickListener listener) { + mOnMenuItemClickListener = listener; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // If we've been given an exact size to match, apply special formatting during layout. @@ -96,11 +104,11 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo mMenu.onItemsChanged(true); } - if (mFormatItems) { + final int childCount = getChildCount(); + if (mFormatItems && childCount > 0) { onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec); } else { // Previous measurement at exact format may have set margins - reset them. - final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); @@ -559,9 +567,11 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo if (mMenu == null) { final Context context = getContext(); mMenu = new MenuBuilder(context); + mMenu.setCallback(new MenuBuilderCallback()); mPresenter = new ActionMenuPresenter(context); - mPresenter.initForMenu(context, mMenu); mPresenter.setMenuView(this); + mPresenter.setCallback(new ActionMenuPresenterCallback()); + mMenu.addMenuPresenter(mPresenter); } return mMenu; @@ -591,6 +601,44 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo return false; } + /** + * Interface responsible for receiving menu item click events if the items themselves + * do not have individual item click listeners. + */ + public interface OnMenuItemClickListener { + /** + * This method will be invoked when a menu item is clicked if the item itself did + * not already handle the event. + * + * @param item {@link MenuItem} that was clicked + * @return <code>true</code> if the event was handled, <code>false</code> otherwise. + */ + public boolean onMenuItemClick(MenuItem item); + } + + private class MenuBuilderCallback implements MenuBuilder.Callback { + @Override + public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { + return mOnMenuItemClickListener != null && + mOnMenuItemClickListener.onMenuItemClick(item); + } + + @Override + public void onMenuModeChange(MenuBuilder menu) { + } + } + + private class ActionMenuPresenterCallback implements ActionMenuPresenter.Callback { + @Override + public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { + } + + @Override + public boolean onOpenSubMenu(MenuBuilder subMenu) { + return false; + } + } + /** @hide */ public interface ActionMenuChildView { public boolean needsDividerBefore(); diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java new file mode 100644 index 0000000..075feba --- /dev/null +++ b/core/java/android/widget/Toolbar.java @@ -0,0 +1,1048 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.widget; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewDebug; +import android.view.ViewGroup; +import com.android.internal.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * A standard toolbar for use within application content. + * + * <p>A Toolbar is a generalization of {@link android.app.ActionBar action bars} for use + * within application layouts. While an action bar is traditionally part of an + * {@link android.app.Activity Activity's} opaque window decor controlled by the framework, + * a Toolbar may be placed at any arbitrary level of nesting within a view hierarchy. + * An application may choose to designate a Toolbar as the action bar for an Activity + * using the {@link android.app.Activity#setActionBar(Toolbar) setActionBar()} method.</p> + * + * <p>Toolbar supports a more focused feature set than ActionBar. From start to end, a toolbar + * may contain a combination of the following optional elements: + * + * <ul> + * <li><em>A navigation button.</em> This may be an Up arrow, navigation menu toggle, close, + * collapse, done or another glyph of the app's choosing. This button should always be used + * to access other navigational destinations within the container of the Toolbar and + * its signified content or otherwise leave the current context signified by the Toolbar.</li> + * <li><em>A branded logo image.</em> This may extend to the height of the bar and can be + * arbitrarily wide.</li> + * <li><em>A title and subtitle.</em> The title should be a signpost for the Toolbar's current + * position in the navigation hierarchy and the content contained there. The subtitle, + * if present should indicate any extended information about the current content. + * If an app uses a logo image it should strongly consider omitting a title and subtitle.</li> + * <li><em>One or more custom views.</em> The application may add arbitrary child views + * to the Toolbar. They will appear at this position within the layout. If a child view's + * {@link LayoutParams} indicates a {@link Gravity} value of + * {@link Gravity#CENTER_HORIZONTAL CENTER_HORIZONTAL} the view will attempt to center + * within the available space remaining in the Toolbar after all other elements have been + * measured.</li> + * <li><em>An {@link ActionMenuView action menu}.</em> The menu of actions will pin to the + * end of the Toolbar offering a few + * <a href="http://developer.android.com/design/patterns/actionbar.html#ActionButtons"> + * frequent, important or typical</a> actions along with an optional overflow menu for + * additional actions.</li> + * </ul> + * </p> + * + * <p>In modern Android UIs developers should lean more on a visually distinct color scheme for + * toolbars than on their application icon. The use of application icon plus title as a standard + * layout is discouraged on API 21 devices and newer.</p> + */ +public class Toolbar extends ViewGroup { + private ActionMenuView mMenuView; + private TextView mTitleTextView; + private TextView mSubtitleTextView; + private ImageButton mNavButtonView; + private ImageView mLogoView; + + private int mTitleTextAppearance; + private int mSubtitleTextAppearance; + private int mTitleMarginStart; + private int mTitleMarginEnd; + private int mTitleMarginTop; + private int mTitleMarginBottom; + + private int mGravity = Gravity.START | Gravity.CENTER_VERTICAL; + + private CharSequence mTitleText; + private CharSequence mSubtitleText; + + // Clear me after use. + private final ArrayList<View> mTempViews = new ArrayList<View>(); + + private OnMenuItemClickListener mOnMenuItemClickListener; + + private final ActionMenuView.OnMenuItemClickListener mMenuViewItemClickListener = + new ActionMenuView.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + if (mOnMenuItemClickListener != null) { + return mOnMenuItemClickListener.onMenuItemClick(item); + } + return false; + } + }; + + public Toolbar(Context context) { + this(context, null); + } + + public Toolbar(Context context, AttributeSet attrs) { + this(context, attrs, com.android.internal.R.attr.toolbarStyle); + } + + public Toolbar(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public Toolbar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Toolbar, + defStyleAttr, defStyleRes); + + mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0); + mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0); + mGravity = a.getInteger(R.styleable.Toolbar_gravity, mGravity); + mTitleMarginStart = mTitleMarginEnd = Math.max(0, a.getDimensionPixelOffset( + R.styleable.Toolbar_titleMargins, -1)); + + final int marginStart = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginStart, -1); + if (marginStart >= 0) { + mTitleMarginStart = marginStart; + } + + final int marginEnd = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginEnd, -1); + if (marginEnd >= 0) { + mTitleMarginEnd = marginEnd; + } + + final int marginTop = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginTop, -1); + if (marginTop >= 0) { + mTitleMarginTop = marginTop; + } + + final int marginBottom = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginBottom, + -1); + if (marginBottom >= 0) { + mTitleMarginBottom = marginBottom; + } + + final CharSequence title = a.getText(R.styleable.Toolbar_title); + if (!TextUtils.isEmpty(title)) { + setTitle(title); + } + + final CharSequence subtitle = a.getText(R.styleable.Toolbar_subtitle); + if (!TextUtils.isEmpty(subtitle)) { + setSubtitle(title); + } + a.recycle(); + } + + /** + * Set a logo drawable from a resource id. + * + * <p>This drawable should generally take the place of title text. The logo cannot be + * clicked. Apps using a logo should also supply a description using + * {@link #setLogoDescription(int)}.</p> + * + * @param resId ID of a drawable resource + */ + public void setLogo(int resId) { + setLogo(getContext().getDrawable(resId)); + } + + /** + * Set a logo drawable. + * + * <p>This drawable should generally take the place of title text. The logo cannot be + * clicked. Apps using a logo should also supply a description using + * {@link #setLogoDescription(int)}.</p> + * + * @param drawable Drawable to use as a logo + */ + public void setLogo(Drawable drawable) { + if (drawable != null) { + if (mLogoView == null) { + mLogoView = new ImageView(getContext()); + } + if (mLogoView.getParent() == null) { + addSystemView(mLogoView); + } + } else if (mLogoView != null && mLogoView.getParent() != null) { + removeView(mLogoView); + } + if (mLogoView != null) { + mLogoView.setImageDrawable(drawable); + } + } + + /** + * Return the current logo drawable. + * + * @return The current logo drawable + * @see #setLogo(int) + * @see #setLogo(android.graphics.drawable.Drawable) + */ + public Drawable getLogo() { + return mLogoView != null ? mLogoView.getDrawable() : null; + } + + /** + * Set a description of the toolbar's logo. + * + * <p>This description will be used for accessibility or other similar descriptions + * of the UI.</p> + * + * @param resId String resource id + */ + public void setLogoDescription(int resId) { + setLogoDescription(getContext().getText(resId)); + } + + /** + * Set a description of the toolbar's logo. + * + * <p>This description will be used for accessibility or other similar descriptions + * of the UI.</p> + * + * @param description Description to set + */ + public void setLogoDescription(CharSequence description) { + if (!TextUtils.isEmpty(description) && mLogoView == null) { + mLogoView = new ImageView(getContext()); + } + if (mLogoView != null) { + mLogoView.setContentDescription(description); + } + } + + /** + * Return the description of the toolbar's logo. + * + * @return A description of the logo + */ + public CharSequence getLogoDescription() { + return mLogoView != null ? mLogoView.getContentDescription() : null; + } + + /** + * Return the current title displayed in the toolbar. + * + * @return The current title + */ + public CharSequence getTitle() { + return mTitleText; + } + + /** + * Set the title of this toolbar. + * + * <p>A title should be used as the anchor for a section of content. It should + * describe or name the content being viewed.</p> + * + * @param resId Resource ID of a string to set as the title + */ + public void setTitle(int resId) { + setTitle(getContext().getText(resId)); + } + + /** + * Set the title of this toolbar. + * + * <p>A title should be used as the anchor for a section of content. It should + * describe or name the content being viewed.</p> + * + * @param title Title to set + */ + public void setTitle(CharSequence title) { + if (!TextUtils.isEmpty(title)) { + if (mTitleTextView == null) { + final Context context = getContext(); + mTitleTextView = new TextView(context); + mTitleTextView.setTextAppearance(context, mTitleTextAppearance); + } + if (mTitleTextView.getParent() == null) { + addSystemView(mTitleTextView); + } + } else if (mTitleTextView != null && mTitleTextView.getParent() != null) { + removeView(mTitleTextView); + } + if (mTitleTextView != null) { + mTitleTextView.setText(title); + } + mTitleText = title; + } + + /** + * Return the subtitle of this toolbar. + * + * @return The current subtitle + */ + public CharSequence getSubtitle() { + return mSubtitleText; + } + + /** + * Set the subtitle of this toolbar. + * + * <p>Subtitles should express extended information about the current content.</p> + * + * @param resId String resource ID + */ + public void setSubtitle(int resId) { + setSubtitle(getContext().getText(resId)); + } + + /** + * Set the subtitle of this toolbar. + * + * <p>Subtitles should express extended information about the current content.</p> + * + * @param subtitle Subtitle to set + */ + public void setSubtitle(CharSequence subtitle) { + if (!TextUtils.isEmpty(subtitle)) { + if (mSubtitleTextView == null) { + final Context context = getContext(); + mSubtitleTextView = new TextView(context); + mSubtitleTextView.setTextAppearance(context, mSubtitleTextAppearance); + } + if (mSubtitleTextView.getParent() == null) { + addSystemView(mSubtitleTextView); + } + } else if (mSubtitleTextView != null && mSubtitleTextView.getParent() != null) { + removeView(mSubtitleTextView); + } + if (mSubtitleTextView != null) { + mSubtitleTextView.setText(subtitle); + } + mSubtitleText = subtitle; + } + + /** + * Set the icon to use for the toolbar's navigation button. + * + * <p>The navigation button appears at the start of the toolbar if present. Setting an icon + * will make the navigation button visible.</p> + * + * <p>If you use a navigation icon you should also set a description for its action using + * {@link #setNavigationDescription(int)}. This is used for accessibility and tooltips.</p> + * + * @param resId Resource ID of a drawable to set + */ + public void setNavigationIcon(int resId) { + setNavigationIcon(getContext().getDrawable(resId)); + } + + /** + * Set the icon to use for the toolbar's navigation button. + * + * <p>The navigation button appears at the start of the toolbar if present. Setting an icon + * will make the navigation button visible.</p> + * + * <p>If you use a navigation icon you should also set a description for its action using + * {@link #setNavigationDescription(int)}. This is used for accessibility and tooltips.</p> + * + * @param icon Drawable to set + */ + public void setNavigationIcon(Drawable icon) { + if (icon != null) { + ensureNavButtonView(); + if (mNavButtonView.getParent() == null) { + addSystemView(mNavButtonView); + } + } else if (mNavButtonView != null && mNavButtonView.getParent() != null) { + removeView(mNavButtonView); + } + if (mNavButtonView != null) { + mNavButtonView.setImageDrawable(icon); + } + } + + /** + * Return the current drawable used as the navigation icon. + * + * @return The navigation icon drawable + */ + public Drawable getNavigationIcon() { + return mNavButtonView != null ? mNavButtonView.getDrawable() : null; + } + + /** + * Set a description for the navigation button. + * + * <p>This description string is used for accessibility, tooltips and other facilities + * to improve discoverability.</p> + * + * @param resId Resource ID of a string to set + */ + public void setNavigationDescription(int resId) { + setNavigationDescription(getContext().getText(resId)); + } + + /** + * Set a description for the navigation button. + * + * <p>This description string is used for accessibility, tooltips and other facilities + * to improve discoverability.</p> + * + * @param description String to set as the description + */ + public void setNavigationDescription(CharSequence description) { + if (!TextUtils.isEmpty(description)) { + ensureNavButtonView(); + } + if (mNavButtonView != null) { + mNavButtonView.setContentDescription(description); + } + } + + /** + * Set a listener to respond to navigation events. + * + * <p>This listener will be called whenever the user clicks the navigation button + * at the start of the toolbar. An icon must be set for the navigation button to appear.</p> + * + * @param listener Listener to set + * @see #setNavigationIcon(android.graphics.drawable.Drawable) + */ + public void setNavigationOnClickListener(OnClickListener listener) { + ensureNavButtonView(); + mNavButtonView.setOnClickListener(listener); + } + + /** + * Return the Menu shown in the toolbar. + * + * <p>Applications that wish to populate the toolbar's menu can do so from here. To use + * an XML menu resource, use {@link #inflateMenu(int)}.</p> + * + * @return The toolbar's Menu + */ + public Menu getMenu() { + if (mMenuView == null) { + mMenuView = new ActionMenuView(getContext()); + mMenuView.setOnMenuItemClickListener(mMenuViewItemClickListener); + addSystemView(mMenuView); + } + return mMenuView.getMenu(); + } + + private MenuInflater getMenuInflater() { + return new MenuInflater(getContext()); + } + + /** + * Inflate a menu resource into this toolbar. + * + * <p>Inflate an XML menu resource into this toolbar. Existing items in the menu will not + * be modified or removed.</p> + * + * @param resId ID of a menu resource to inflate + */ + public void inflateMenu(int resId) { + getMenuInflater().inflate(resId, getMenu()); + } + + /** + * Set a listener to respond to menu item click events. + * + * <p>This listener will be invoked whenever a user selects a menu item from + * the action buttons presented at the end of the toolbar or the associated overflow.</p> + * + * @param listener Listener to set + */ + public void setOnMenuItemClickListener(OnMenuItemClickListener listener) { + mOnMenuItemClickListener = listener; + } + + private void ensureNavButtonView() { + if (mNavButtonView == null) { + mNavButtonView = new ImageButton(getContext(), null, R.attr.borderlessButtonStyle); + } + } + + private void addSystemView(View v) { + final LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + lp.mViewType = LayoutParams.SYSTEM; + addView(v, lp); + } + + @Override + protected Parcelable onSaveInstanceState() { + SavedState state = new SavedState(super.onSaveInstanceState()); + return state; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + final SavedState ss = (SavedState) state; + super.onRestoreInstanceState(ss.getSuperState()); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = 0; + int height = 0; + int childState = 0; + + // System views measure first. + + if (shouldLayout(mNavButtonView)) { + measureChildWithMargins(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0); + width += mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView); + height = Math.max(height, mNavButtonView.getMeasuredHeight() + + getVerticalMargins(mNavButtonView)); + childState = combineMeasuredStates(childState, mNavButtonView.getMeasuredState()); + } + + if (shouldLayout(mMenuView)) { + measureChildWithMargins(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0); + width += mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView); + height = Math.max(height, mMenuView.getMeasuredHeight() + + getVerticalMargins(mMenuView)); + childState = combineMeasuredStates(childState, mMenuView.getMeasuredState()); + } + + if (shouldLayout(mLogoView)) { + measureChildWithMargins(mLogoView, widthMeasureSpec, width, heightMeasureSpec, 0); + width += mLogoView.getMeasuredWidth() + getHorizontalMargins(mLogoView); + height = Math.max(height, mLogoView.getMeasuredHeight() + + getVerticalMargins(mLogoView)); + childState = combineMeasuredStates(childState, mLogoView.getMeasuredState()); + } + + int titleWidth = 0; + int titleHeight = 0; + final int titleVertMargins = mTitleMarginTop + mTitleMarginBottom; + final int titleHorizMargins = mTitleMarginStart + mTitleMarginEnd; + if (shouldLayout(mTitleTextView)) { + measureChildWithMargins(mTitleTextView, widthMeasureSpec, width + titleHorizMargins, + heightMeasureSpec, titleVertMargins); + titleWidth = mTitleTextView.getMeasuredWidth() + getHorizontalMargins(mTitleTextView); + titleHeight = mTitleTextView.getMeasuredHeight() + getVerticalMargins(mTitleTextView); + childState = combineMeasuredStates(childState, mTitleTextView.getMeasuredState()); + } + if (shouldLayout(mSubtitleTextView)) { + measureChildWithMargins(mSubtitleTextView, widthMeasureSpec, width + titleHorizMargins, + heightMeasureSpec, titleHeight + titleVertMargins); + titleWidth = Math.max(titleWidth, mSubtitleTextView.getMeasuredWidth() + + getHorizontalMargins(mSubtitleTextView)); + titleHeight += mSubtitleTextView.getMeasuredHeight() + + getVerticalMargins(mSubtitleTextView); + childState = combineMeasuredStates(childState, mSubtitleTextView.getMeasuredState()); + } + + width += titleWidth; + height = Math.max(height, titleHeight); + + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.mViewType == LayoutParams.SYSTEM || !shouldLayout(child)) { + // We already got all system views above. Skip them and GONE views. + continue; + } + + measureChildWithMargins(child, widthMeasureSpec, width, heightMeasureSpec, 0); + width += child.getMeasuredWidth() + getHorizontalMargins(child); + height = Math.max(height, child.getMeasuredHeight() + getVerticalMargins(child)); + childState = combineMeasuredStates(childState, child.getMeasuredState()); + } + + // Measurement already took padding into account for available space for the children, + // add it in for the final size. + width += getPaddingLeft() + getPaddingRight(); + height += getPaddingTop() + getPaddingBottom(); + + final int measuredWidth = resolveSizeAndState( + Math.max(width, getSuggestedMinimumWidth()), + widthMeasureSpec, childState & MEASURED_STATE_MASK); + final int measuredHeight = resolveSizeAndState( + Math.max(height, getSuggestedMinimumHeight()), + heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT); + setMeasuredDimension(measuredWidth, measuredHeight); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; + final int width = getWidth(); + final int height = getHeight(); + final int paddingLeft = getPaddingLeft(); + final int paddingRight = getPaddingRight(); + final int paddingTop = getPaddingTop(); + final int paddingBottom = getPaddingBottom(); + int left = paddingLeft; + int right = width - paddingRight; + + if (shouldLayout(mNavButtonView)) { + if (isRtl) { + right = layoutChildRight(mNavButtonView, right); + } else { + left = layoutChildLeft(mNavButtonView, left); + } + } + + if (shouldLayout(mMenuView)) { + if (isRtl) { + left = layoutChildLeft(mMenuView, left); + } else { + right = layoutChildRight(mMenuView, right); + } + } + + if (shouldLayout(mLogoView)) { + if (isRtl) { + right = layoutChildRight(mLogoView, right); + } else { + left = layoutChildLeft(mLogoView, left); + } + } + + final boolean layoutTitle = shouldLayout(mTitleTextView); + final boolean layoutSubtitle = shouldLayout(mSubtitleTextView); + int titleHeight = 0; + if (layoutTitle) { + final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams(); + titleHeight += lp.topMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin; + } + if (layoutSubtitle) { + final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams(); + titleHeight += lp.bottomMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin; + } + + if (layoutTitle || layoutSubtitle) { + int titleTop; + switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) { + case Gravity.TOP: + titleTop = getPaddingTop(); + break; + default: + case Gravity.CENTER_VERTICAL: + final View child = layoutTitle ? mTitleTextView : mSubtitleTextView; + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + final int space = height - paddingTop - paddingBottom; + int spaceAbove = (space - titleHeight) / 2; + if (spaceAbove < lp.topMargin + mTitleMarginTop) { + spaceAbove = lp.topMargin + mTitleMarginTop; + } else { + final int spaceBelow = height - paddingBottom - titleHeight - + spaceAbove - paddingTop; + if (spaceBelow < lp.bottomMargin + mTitleMarginBottom) { + spaceAbove = Math.max(0, spaceAbove - + (lp.bottomMargin + mTitleMarginBottom - spaceBelow)); + } + } + titleTop = paddingTop + spaceAbove; + break; + case Gravity.BOTTOM: + titleTop = height - paddingBottom - titleHeight; + break; + } + if (isRtl) { + int titleRight = right; + int subtitleRight = right; + titleTop += mTitleMarginTop; + if (layoutTitle) { + final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams(); + titleRight -= lp.rightMargin + mTitleMarginStart; + titleTop += lp.topMargin; + final int titleLeft = titleRight - mTitleTextView.getMeasuredWidth(); + final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight(); + mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom); + titleRight = titleLeft - lp.leftMargin - mTitleMarginEnd; + titleTop = titleBottom + lp.bottomMargin; + } + if (layoutSubtitle) { + final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams(); + subtitleRight -= lp.rightMargin + mTitleMarginStart; + titleTop += lp.topMargin; + final int subtitleLeft = subtitleRight - mSubtitleTextView.getMeasuredWidth(); + final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight(); + mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom); + subtitleRight = subtitleRight - lp.leftMargin - mTitleMarginEnd; + titleTop = subtitleBottom + lp.bottomMargin; + } + right = Math.max(titleRight, subtitleRight); + } else { + int titleLeft = left; + int subtitleLeft = left; + titleTop += mTitleMarginTop; + if (layoutTitle) { + final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams(); + titleLeft += lp.leftMargin + mTitleMarginStart; + titleTop += lp.topMargin; + final int titleRight = titleLeft + mTitleTextView.getMeasuredWidth(); + final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight(); + mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom); + titleLeft = titleRight + lp.rightMargin + mTitleMarginEnd; + titleTop = titleBottom + lp.bottomMargin; + } + if (layoutSubtitle) { + final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams(); + subtitleLeft += lp.leftMargin + mTitleMarginStart; + titleTop += lp.topMargin; + final int subtitleRight = subtitleLeft + mSubtitleTextView.getMeasuredWidth(); + final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight(); + mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom); + subtitleLeft = subtitleRight + lp.rightMargin + mTitleMarginEnd; + titleTop = subtitleBottom + lp.bottomMargin; + } + left = Math.max(titleLeft, subtitleLeft); + } + } + + // Get all remaining children sorted for layout. This is all prepared + // such that absolute layout direction can be used below. + + addCustomViewsWithGravity(mTempViews, Gravity.LEFT); + final int leftViewsCount = mTempViews.size(); + for (int i = 0; i < leftViewsCount; i++) { + left = layoutChildLeft(getChildAt(i), left); + } + + addCustomViewsWithGravity(mTempViews, Gravity.RIGHT); + final int rightViewsCount = mTempViews.size(); + for (int i = 0; i < rightViewsCount; i++) { + right = layoutChildRight(getChildAt(i), right); + } + + // Centered views try to center with respect to the whole bar, but views pinned + // to the left or right can push the mass of centered views to one side or the other. + addCustomViewsWithGravity(mTempViews, Gravity.CENTER); + final int centerViewsWidth = getViewListMeasuredWidth(mTempViews); + final int parentCenter = paddingLeft + (width - paddingLeft - paddingRight) / 2; + final int halfCenterViewsWidth = centerViewsWidth / 2; + int centerLeft = parentCenter - halfCenterViewsWidth; + final int centerRight = centerLeft + centerViewsWidth; + if (centerLeft < left) { + centerLeft = left; + } else if (centerRight > right) { + centerLeft -= centerRight - right; + } + + final int centerViewsCount = mTempViews.size(); + for (int i = 0; i < centerViewsCount; i++) { + centerLeft = layoutChildLeft(getChildAt(i), centerLeft); + } + mTempViews.clear(); + } + + private int getViewListMeasuredWidth(List<View> views) { + int width = 0; + final int count = views.size(); + for (int i = 0; i < count; i++) { + final View v = views.get(i); + final LayoutParams lp = (LayoutParams) v.getLayoutParams(); + width += lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin; + } + return width; + } + + private int layoutChildLeft(View child, int left) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + left += lp.leftMargin; + int top = getChildTop(child); + child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight()); + left += lp.rightMargin; + return left; + } + + private int layoutChildRight(View child, int right) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + right -= lp.rightMargin; + int top = getChildTop(child); + child.layout(right - child.getMeasuredWidth(), top, right, top + child.getMeasuredHeight()); + right -= lp.leftMargin; + return right; + } + + private int getChildTop(View child) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + switch (getChildVerticalGravity(lp.gravity)) { + case Gravity.TOP: + return getPaddingTop(); + + case Gravity.BOTTOM: + return getPaddingBottom() - child.getMeasuredHeight() - lp.bottomMargin; + + default: + case Gravity.CENTER_VERTICAL: + final int paddingTop = getPaddingTop(); + final int paddingBottom = getPaddingBottom(); + final int height = getHeight(); + final int childHeight = child.getMeasuredHeight(); + final int space = height - paddingTop - paddingBottom; + int spaceAbove = (space - childHeight) / 2; + if (spaceAbove < lp.topMargin) { + spaceAbove = lp.topMargin; + } else { + final int spaceBelow = height - paddingBottom - childHeight - + spaceAbove - paddingTop; + if (spaceBelow < lp.bottomMargin) { + spaceAbove = Math.max(0, spaceAbove - (lp.bottomMargin - spaceBelow)); + } + } + return paddingTop + spaceAbove; + } + } + + private int getChildVerticalGravity(int gravity) { + final int vgrav = gravity & Gravity.VERTICAL_GRAVITY_MASK; + switch (vgrav) { + case Gravity.TOP: + case Gravity.BOTTOM: + case Gravity.CENTER_VERTICAL: + return vgrav; + default: + return mGravity & Gravity.VERTICAL_GRAVITY_MASK; + } + } + + /** + * Prepare a list of non-SYSTEM child views. If the layout direction is RTL + * this will be in reverse child order. + * + * @param views List to populate. It will be cleared before use. + * @param gravity Horizontal gravity to match against + */ + private void addCustomViewsWithGravity(List<View> views, int gravity) { + final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; + final int childCount = getChildCount(); + final int absGrav = Gravity.getAbsoluteGravity(gravity, getLayoutDirection()); + + views.clear(); + + if (isRtl) { + for (int i = childCount - 1; i >= 0; i--) { + final View child = getChildAt(i); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.mViewType != LayoutParams.SYSTEM && shouldLayout(child) && + getChildHorizontalGravity(lp.gravity) == absGrav) { + views.add(child); + } + + } + } else { + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.mViewType != LayoutParams.SYSTEM && shouldLayout(child) && + getChildHorizontalGravity(lp.gravity) == absGrav) { + views.add(child); + } + } + } + } + + private int getChildHorizontalGravity(int gravity) { + final int ld = getLayoutDirection(); + final int absGrav = Gravity.getAbsoluteGravity(gravity, ld); + final int hGrav = absGrav & Gravity.HORIZONTAL_GRAVITY_MASK; + switch (hGrav) { + case Gravity.LEFT: + case Gravity.RIGHT: + case Gravity.CENTER_HORIZONTAL: + return hGrav; + default: + return ld == LAYOUT_DIRECTION_RTL ? Gravity.RIGHT : Gravity.LEFT; + } + } + + private boolean shouldLayout(View view) { + return view != null && view.getParent() == this && view.getVisibility() != GONE; + } + + private int getHorizontalMargins(View v) { + final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams(); + return mlp.getMarginStart() + mlp.getMarginEnd(); + } + + private int getVerticalMargins(View v) { + final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams(); + return mlp.topMargin + mlp.bottomMargin; + } + + @Override + public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { + return super.generateLayoutParams(attrs); + } + + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + if (p instanceof LayoutParams) { + return new LayoutParams((LayoutParams) p); + } else if (p instanceof MarginLayoutParams) { + return new LayoutParams((MarginLayoutParams) p); + } else { + return new LayoutParams(p); + } + } + + @Override + protected ViewGroup.LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return super.checkLayoutParams(p) && p instanceof LayoutParams; + } + + private static boolean isCustomView(View child) { + return ((LayoutParams) child.getLayoutParams()).mViewType == LayoutParams.CUSTOM; + } + + /** + * Interface responsible for receiving menu item click events if the items themselves + * do not have individual item click listeners. + */ + public interface OnMenuItemClickListener { + /** + * This method will be invoked when a menu item is clicked if the item itself did + * not already handle the event. + * + * @param item {@link MenuItem} that was clicked + * @return <code>true</code> if the event was handled, <code>false</code> otherwise. + */ + public boolean onMenuItemClick(MenuItem item); + } + + /** + * Layout information for child views of Toolbars. + * + * @attr ref android.R.styleable#Toolbar_LayoutParams_layout_gravity + */ + public static class LayoutParams extends MarginLayoutParams { + /** + * Gravity for the view associated with these LayoutParams. + * + * @see android.view.Gravity + */ + @ViewDebug.ExportedProperty(category = "layout", mapping = { + @ViewDebug.IntToString(from = -1, to = "NONE"), + @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), + @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), + @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), + @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"), + @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"), + @ViewDebug.IntToString(from = Gravity.START, to = "START"), + @ViewDebug.IntToString(from = Gravity.END, to = "END"), + @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"), + @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"), + @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"), + @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"), + @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), + @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") + }) + public int gravity = Gravity.NO_GRAVITY; + + static final int CUSTOM = 0; + static final int SYSTEM = 1; + + int mViewType = CUSTOM; + + public LayoutParams(@NonNull Context c, AttributeSet attrs) { + super(c, attrs); + + TypedArray a = c.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.Toolbar_LayoutParams); + gravity = a.getInt( + com.android.internal.R.styleable.Toolbar_LayoutParams_layout_gravity, + Gravity.NO_GRAVITY); + a.recycle(); + } + + public LayoutParams(int width, int height) { + super(width, height); + this.gravity = Gravity.CENTER_VERTICAL | Gravity.START; + } + + public LayoutParams(int width, int height, int gravity) { + super(width, height); + this.gravity = gravity; + } + + public LayoutParams(int gravity) { + this(WRAP_CONTENT, MATCH_PARENT, gravity); + } + + public LayoutParams(LayoutParams source) { + super(source); + + this.gravity = source.gravity; + } + + public LayoutParams(MarginLayoutParams source) { + super(source); + } + + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + } + + static class SavedState extends BaseSavedState { + public SavedState(Parcel source) { + super(source); + } + + public SavedState(Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + } + + public static final Creator<SavedState> CREATOR = new Creator<SavedState>() { + + @Override + public SavedState createFromParcel(Parcel source) { + return new SavedState(source); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } +} diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java new file mode 100644 index 0000000..34156e5 --- /dev/null +++ b/core/java/com/android/internal/app/ToolbarActionBar.java @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.internal.app; + +import android.annotation.Nullable; +import android.app.ActionBar; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.drawable.Drawable; +import android.view.ActionMode; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.SpinnerAdapter; +import android.widget.Toolbar; + +import java.util.ArrayList; +import java.util.Map; + +public class ToolbarActionBar extends ActionBar { + private Toolbar mToolbar; + private View mCustomView; + + private int mDisplayOptions; + + private int mNavResId; + private int mIconResId; + private int mLogoResId; + private Drawable mNavDrawable; + private Drawable mIconDrawable; + private Drawable mLogoDrawable; + private int mTitleResId; + private int mSubtitleResId; + private CharSequence mTitle; + private CharSequence mSubtitle; + + private boolean mLastMenuVisibility; + private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners = + new ArrayList<OnMenuVisibilityListener>(); + + public ToolbarActionBar(Toolbar toolbar) { + mToolbar = toolbar; + } + + @Override + public void setCustomView(View view) { + setCustomView(view, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); + } + + @Override + public void setCustomView(View view, LayoutParams layoutParams) { + if (mCustomView != null) { + mToolbar.removeView(mCustomView); + } + mCustomView = view; + if (view != null) { + mToolbar.addView(view, generateLayoutParams(layoutParams)); + } + } + + private Toolbar.LayoutParams generateLayoutParams(LayoutParams lp) { + final Toolbar.LayoutParams result = new Toolbar.LayoutParams(lp); + result.gravity = lp.gravity; + return result; + } + + @Override + public void setCustomView(int resId) { + final LayoutInflater inflater = LayoutInflater.from(mToolbar.getContext()); + setCustomView(inflater.inflate(resId, mToolbar, false)); + } + + @Override + public void setIcon(int resId) { + mIconResId = resId; + mIconDrawable = null; + updateToolbarLogo(); + } + + @Override + public void setIcon(Drawable icon) { + mIconResId = 0; + mIconDrawable = icon; + updateToolbarLogo(); + } + + @Override + public void setLogo(int resId) { + mLogoResId = resId; + mLogoDrawable = null; + updateToolbarLogo(); + } + + @Override + public void setLogo(Drawable logo) { + mLogoResId = 0; + mLogoDrawable = logo; + updateToolbarLogo(); + } + + private void updateToolbarLogo() { + Drawable drawable = null; + if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) { + final int resId; + if ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) { + resId = mLogoResId; + drawable = mLogoDrawable; + } else { + resId = mIconResId; + drawable = mIconDrawable; + } + if (resId != 0) { + drawable = mToolbar.getContext().getDrawable(resId); + } + } + mToolbar.setLogo(drawable); + } + + @Override + public void setStackedBackgroundDrawable(Drawable d) { + // This space for rent (do nothing) + } + + @Override + public void setSplitBackgroundDrawable(Drawable d) { + // This space for rent (do nothing) + } + + @Override + public void setHomeButtonEnabled(boolean enabled) { + // If the nav button on a Toolbar is present, it's enabled. No-op. + } + + @Override + public Context getThemedContext() { + return mToolbar.getContext(); + } + + @Override + public boolean isTitleTruncated() { + return super.isTitleTruncated(); + } + + @Override + public void setHomeAsUpIndicator(Drawable indicator) { + mToolbar.setNavigationIcon(indicator); + } + + @Override + public void setHomeAsUpIndicator(int resId) { + mToolbar.setNavigationIcon(resId); + } + + @Override + public void setHomeActionContentDescription(CharSequence description) { + mToolbar.setNavigationDescription(description); + } + + @Override + public void setDefaultDisplayHomeAsUpEnabled(boolean enabled) { + // Do nothing + } + + @Override + public void setHomeActionContentDescription(int resId) { + mToolbar.setNavigationDescription(resId); + } + + @Override + public void setShowHideAnimationEnabled(boolean enabled) { + // This space for rent; no-op. + } + + @Override + public void onConfigurationChanged(Configuration config) { + super.onConfigurationChanged(config); + } + + @Override + public ActionMode startActionMode(ActionMode.Callback callback) { + return mToolbar.startActionMode(callback); + } + + @Override + public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void setSelectedNavigationItem(int position) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public int getSelectedNavigationIndex() { + return -1; + } + + @Override + public int getNavigationItemCount() { + return 0; + } + + @Override + public void setTitle(CharSequence title) { + mTitle = title; + mTitleResId = 0; + updateToolbarTitle(); + } + + @Override + public void setTitle(int resId) { + mTitleResId = resId; + mTitle = null; + updateToolbarTitle(); + } + + @Override + public void setSubtitle(CharSequence subtitle) { + mSubtitle = subtitle; + mSubtitleResId = 0; + updateToolbarTitle(); + } + + @Override + public void setSubtitle(int resId) { + mSubtitleResId = resId; + mSubtitle = null; + updateToolbarTitle(); + } + + private void updateToolbarTitle() { + final Context context = mToolbar.getContext(); + CharSequence title = null; + CharSequence subtitle = null; + if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) { + title = mTitleResId != 0 ? context.getText(mTitleResId) : mTitle; + subtitle = mSubtitleResId != 0 ? context.getText(mSubtitleResId) : mSubtitle; + } + mToolbar.setTitle(title); + mToolbar.setSubtitle(subtitle); + } + + @Override + public void setDisplayOptions(@DisplayOptions int options) { + setDisplayOptions(options, 0xffffffff); + } + + @Override + public void setDisplayOptions(@DisplayOptions int options, @DisplayOptions int mask) { + final int oldOptions = mDisplayOptions; + mDisplayOptions = (options & mask) | (mDisplayOptions & ~mask); + final int optionsChanged = oldOptions ^ mDisplayOptions; + } + + @Override + public void setDisplayUseLogoEnabled(boolean useLogo) { + setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO); + } + + @Override + public void setDisplayShowHomeEnabled(boolean showHome) { + setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME); + } + + @Override + public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) { + setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP); + } + + @Override + public void setDisplayShowTitleEnabled(boolean showTitle) { + setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE); + } + + @Override + public void setDisplayShowCustomEnabled(boolean showCustom) { + setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM); + } + + @Override + public void setBackgroundDrawable(@Nullable Drawable d) { + mToolbar.setBackground(d); + } + + @Override + public View getCustomView() { + return mCustomView; + } + + @Override + public CharSequence getTitle() { + return mToolbar.getTitle(); + } + + @Override + public CharSequence getSubtitle() { + return mToolbar.getSubtitle(); + } + + @Override + public int getNavigationMode() { + return NAVIGATION_MODE_STANDARD; + } + + @Override + public void setNavigationMode(@NavigationMode int mode) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public int getDisplayOptions() { + return mDisplayOptions; + } + + @Override + public Tab newTab() { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void addTab(Tab tab) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void addTab(Tab tab, boolean setSelected) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void addTab(Tab tab, int position) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void addTab(Tab tab, int position, boolean setSelected) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void removeTab(Tab tab) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void removeTabAt(int position) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void removeAllTabs() { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public void selectTab(Tab tab) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public Tab getSelectedTab() { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public Tab getTabAt(int index) { + throw new UnsupportedOperationException( + "Navigation modes are not supported in toolbar action bars"); + } + + @Override + public int getTabCount() { + return 0; + } + + @Override + public int getHeight() { + return mToolbar.getHeight(); + } + + @Override + public void show() { + // TODO: Consider a better transition for this. + // Right now use no automatic transition so that the app can supply one if desired. + mToolbar.setVisibility(View.VISIBLE); + } + + @Override + public void hide() { + // TODO: Consider a better transition for this. + // Right now use no automatic transition so that the app can supply one if desired. + mToolbar.setVisibility(View.GONE); + } + + @Override + public boolean isShowing() { + return mToolbar.getVisibility() == View.VISIBLE; + } + + public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) { + mMenuVisibilityListeners.add(listener); + } + + public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) { + mMenuVisibilityListeners.remove(listener); + } + + public void dispatchMenuVisibilityChanged(boolean isVisible) { + if (isVisible == mLastMenuVisibility) { + return; + } + mLastMenuVisibility = isVisible; + + final int count = mMenuVisibilityListeners.size(); + for (int i = 0; i < count; i++) { + mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible); + } + } + + @Override + public void captureSharedElements(Map<String, View> sharedElements) { + mToolbar.findSharedElements(sharedElements); + } +} diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/WindowDecorActionBar.java index 80e1caa..c6afae0 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/WindowDecorActionBar.java @@ -60,14 +60,14 @@ import java.util.ArrayList; import java.util.Map; /** - * ActionBarImpl is the ActionBar implementation used - * by devices of all screen sizes. If it detects a compatible decor, - * it will split contextual modes across both the ActionBarView at - * the top of the screen and a horizontal LinearLayout at the bottom - * which is normally hidden. + * WindowDecorActionBar is the ActionBar implementation used + * by devices of all screen sizes as part of the window decor layout. + * If it detects a compatible decor, it will split contextual modes + * across both the ActionBarView at the top of the screen and + * a horizontal LinearLayout at the bottom which is normally hidden. */ -public class ActionBarImpl extends ActionBar { - private static final String TAG = "ActionBarImpl"; +public class WindowDecorActionBar extends ActionBar { + private static final String TAG = "WindowDecorActionBar"; private Context mContext; private Context mThemedContext; @@ -105,9 +105,6 @@ public class ActionBarImpl extends ActionBar { private int mContextDisplayMode; private boolean mHasEmbeddedTabs; - final Handler mHandler = new Handler(); - Runnable mTabSelector; - private int mCurWindowVisibility = View.VISIBLE; private boolean mContentAnimations = true; @@ -157,7 +154,7 @@ public class ActionBarImpl extends ActionBar { } }; - public ActionBarImpl(Activity activity) { + public WindowDecorActionBar(Activity activity) { mActivity = activity; Window window = activity.getWindow(); View decor = window.getDecorView(); @@ -168,7 +165,7 @@ public class ActionBarImpl extends ActionBar { } } - public ActionBarImpl(Dialog dialog) { + public WindowDecorActionBar(Dialog dialog) { mDialog = dialog; init(dialog.getWindow().getDecorView()); } diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java index 3cceebe..891baea 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java @@ -282,11 +282,6 @@ public class ActionMenuItemView extends TextView @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { - // Fill all available height. - heightMeasureSpec = MeasureSpec.makeMeasureSpec( - MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY); - } final boolean textVisible = hasText(); if (textVisible && mSavedPaddingLeft >= 0) { super.setPadding(mSavedPaddingLeft, getPaddingTop(), diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java index c2d22dd..ed07514 100644 --- a/core/java/com/android/internal/widget/ActionBarContainer.java +++ b/core/java/com/android/internal/widget/ActionBarContainer.java @@ -43,6 +43,7 @@ public class ActionBarContainer extends FrameLayout { private Drawable mSplitBackground; private boolean mIsSplit; private boolean mIsStacked; + private int mHeight; public ActionBarContainer(Context context) { this(context, null); @@ -59,6 +60,7 @@ public class ActionBarContainer extends FrameLayout { mBackground = a.getDrawable(com.android.internal.R.styleable.ActionBar_background); mStackedBackground = a.getDrawable( com.android.internal.R.styleable.ActionBar_backgroundStacked); + mHeight = a.getDimensionPixelSize(com.android.internal.R.styleable.ActionBar_height, -1); if (getId() == com.android.internal.R.id.split_action_bar) { mIsSplit = true; @@ -251,6 +253,11 @@ public class ActionBarContainer extends FrameLayout { @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mActionBarView == null && + MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST && mHeight >= 0) { + heightMeasureSpec = MeasureSpec.makeMeasureSpec( + Math.min(mHeight, MeasureSpec.getSize(heightMeasureSpec)), MeasureSpec.AT_MOST); + } super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mActionBarView == null) return; diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java index adb9bf8..c9dff1a 100644 --- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java +++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java @@ -21,7 +21,7 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.view.ViewGroup; import android.view.WindowInsets; -import com.android.internal.app.ActionBarImpl; +import com.android.internal.app.WindowDecorActionBar; import android.content.Context; import android.content.res.TypedArray; @@ -38,7 +38,7 @@ public class ActionBarOverlayLayout extends ViewGroup { private static final String TAG = "ActionBarOverlayLayout"; private int mActionBarHeight; - private ActionBarImpl mActionBar; + private WindowDecorActionBar mActionBar; private int mWindowVisibility = View.VISIBLE; // The main UI elements that we handle the layout of. @@ -88,7 +88,7 @@ public class ActionBarOverlayLayout extends ViewGroup { Build.VERSION_CODES.KITKAT; } - public void setActionBar(ActionBarImpl impl) { + public void setActionBar(WindowDecorActionBar impl) { mActionBar = impl; if (getWindowToken() != null) { // This is being initialized after being added to a window; diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 1273c4d..60631b9 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -430,7 +430,7 @@ public class ActionBarView extends AbsActionBarView { mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); // Span the whole width layoutParams.width = LayoutParams.MATCH_PARENT; - layoutParams.height = mContentHeight; + layoutParams.height = LayoutParams.WRAP_CONTENT; configPresenters(builder); menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); if (mSplitView != null) { diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index aa5005f..00b61a5 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -650,6 +650,8 @@ <!-- Default ActivityChooserView style. --> <attr name="activityChooserViewStyle" format="reference" /> + <attr name="toolbarStyle" format="reference" /> + <!-- Fast scroller styles --> <eat-comment /> @@ -6235,10 +6237,6 @@ <attr name="inputType" /> </declare-styleable> - <declare-styleable name="ActionBar_LayoutParams"> - <attr name="layout_gravity" /> - </declare-styleable> - <declare-styleable name="Switch"> <!-- Drawable to use as the "thumb" that switches back and forth. --> <attr name="thumb" /> @@ -6447,4 +6445,21 @@ <attr name="textView" format="reference" /> </declare-styleable> + <declare-styleable name="Toolbar"> + <attr name="titleTextAppearance" format="reference" /> + <attr name="subtitleTextAppearance" format="reference" /> + <attr name="title" /> + <attr name="subtitle" /> + <attr name="gravity" /> + <attr name="titleMargins" format="dimension" /> + <attr name="titleMarginStart" format="dimension" /> + <attr name="titleMarginEnd" format="dimension" /> + <attr name="titleMarginTop" format="dimension" /> + <attr name="titleMarginBottom" format="dimension" /> + </declare-styleable> + + <declare-styleable name="Toolbar_LayoutParams"> + <attr name="layout_gravity" /> + </declare-styleable> + </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 667adde..76a3bd7 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2143,6 +2143,8 @@ <public type="attr" name="colorButtonNormalColored" /> <public type="attr" name="colorButtonPressedColored" /> <public type="attr" name="persistable" /> + <public type="attr" name="titleTextAppearance" /> + <public type="attr" name="subtitleTextAppearance" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index be875ff..fc0fccc 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1208,6 +1208,13 @@ please see styles_device_defaults.xml. <item name="android:subtitleTextStyle">@android:style/TextAppearance.Widget.ActionMode.Subtitle</item> </style> + <style name="Widget.Toolbar"> + <item name="android:titleTextAppearance">@android:style/TextAppearance.Widget.Toolbar.Title</item> + <item name="android:subtitleTextAppearance">@android:style/TextAppearance.Widget.Toolbar.Subtitle</item> + <item name="android:minHeight">?android:attr/actionBarSize</item> + <item name="android:titleMargins">4dp</item> + </style> + <style name="TextAppearance.Widget.ActionBar.Title" parent="@android:style/TextAppearance.Medium"> </style> @@ -1225,6 +1232,14 @@ please see styles_device_defaults.xml. <item name="android:textColor">?android:attr/textColorSecondary</item> </style> + <style name="TextAppearance.Widget.Toolbar.Title" + parent="@android:style/TextAppearance.Widget.ActionBar.Title"> + </style> + + <style name="TextAppearance.Widget.Toolbar.Subtitle" + parent="@android:style/TextAppearance.Widget.ActionBar.Subtitle"> + </style> + <style name="Widget.ActionButton"> <item name="android:background">?android:attr/actionBarItemBackground</item> <item name="android:paddingStart">12dip</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 557dce24..57075ee 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1829,4 +1829,8 @@ <java-symbol type="color" name="timepicker_default_ampm_selected_background_color_holo_light" /> <java-symbol type="array" name="config_clockTickVibePattern" /> + <java-symbol type="attr" name="toolbarStyle" /> + <java-symbol type="attr" name="titleTextAppearance" /> + <java-symbol type="attr" name="subtitleTextAppearance" /> + </resources> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 3c4e848..7aea814 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -352,6 +352,8 @@ please see themes_device_defaults.xml. <item name="actionBarDivider">?android:attr/dividerVertical</item> <item name="actionBarItemBackground">?android:attr/selectableItemBackground</item> + <item name="toolbarStyle">@android:style/Widget.Toolbar</item> + <item name="dividerVertical">@drawable/divider_vertical_dark</item> <item name="dividerHorizontal">@drawable/divider_vertical_dark</item> <item name="buttonBarStyle">@android:style/ButtonBar</item> |