Use appcompat as sources
This commit is contained in:
parent
d4263cc5cb
commit
e6d2fe81f3
519 changed files with 79009 additions and 528 deletions
|
@ -142,14 +142,14 @@ android {
|
|||
buildTypes {
|
||||
debug {
|
||||
// proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
|
||||
minifyEnabled true
|
||||
proguardFiles 'proguard-project.txt'
|
||||
// minifyEnabled true
|
||||
// proguardFiles 'proguard-project.txt'
|
||||
signingConfig signingConfigs.development
|
||||
}
|
||||
release {
|
||||
// proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
|
||||
minifyEnabled true
|
||||
proguardFiles 'proguard-project.txt'
|
||||
// minifyEnabled true
|
||||
//proguardFiles 'proguard-project.txt'
|
||||
signingConfig signingConfigs.publishing
|
||||
}
|
||||
}
|
||||
|
@ -294,6 +294,7 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compile project(path: ":eclipse-compile:appcompat", configuration: "android")
|
||||
compile project(path: ":OsmAnd-java", configuration: "android")
|
||||
compile fileTree(
|
||||
dir: "libs",
|
||||
|
@ -305,8 +306,8 @@ dependencies {
|
|||
"OsmAndCore_android.jar",
|
||||
"OsmAndCore_wrapper.jar"])
|
||||
compile "com.github.ksoichiro:android-observablescrollview:1.5.0"
|
||||
compile "com.android.support:appcompat-v7:21.0.3"
|
||||
compile "com.github.shell-software:fab:1.0.5"
|
||||
// compile "com.android.support:appcompat-v7:21.0.3"
|
||||
// compile "com.github.shell-software:fab:1.0.5"
|
||||
legacyCompile "net.osmand:OsmAndCore_android:0.1-SNAPSHOT@jar"
|
||||
qtcoredebugCompile "net.osmand:OsmAndCore_androidNativeDebug:0.1-SNAPSHOT@aar"
|
||||
qtcoredebugCompile "net.osmand:OsmAndCore_android:0.1-SNAPSHOT@aar"
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -96,8 +96,7 @@ public class Version {
|
|||
}
|
||||
|
||||
public static boolean isDeveloperVersion(OsmandApplication ctx){
|
||||
return "osmand~".equalsIgnoreCase(getAppName(ctx)) || "osmand~f".equalsIgnoreCase(getAppName(ctx)) ;
|
||||
|
||||
return getAppName(ctx).contains("~");
|
||||
}
|
||||
|
||||
public static String getVersionForTracker(OsmandApplication ctx) {
|
||||
|
|
1358
android/support/v7/app/ActionBar.java
Normal file
1358
android/support/v7/app/ActionBar.java
Normal file
File diff suppressed because it is too large
Load diff
24
android/support/v7/app/ActionBarActivity.java
Normal file
24
android/support/v7/app/ActionBarActivity.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (C) 2012 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.support.v7.app;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link android.support.v7.app.AppCompatActivity} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ActionBarActivity extends AppCompatActivity {
|
||||
}
|
697
android/support/v7/app/ActionBarDrawerToggle.java
Normal file
697
android/support/v7/app/ActionBarDrawerToggle.java
Normal file
|
@ -0,0 +1,697 @@
|
|||
/*
|
||||
* 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.support.v7.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActionBar;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.support.v7.appcompat.R;
|
||||
|
||||
/**
|
||||
* This class provides a handy way to tie together the functionality of
|
||||
* {@link android.support.v4.widget.DrawerLayout} and the framework <code>ActionBar</code> to
|
||||
* implement the recommended design for navigation drawers.
|
||||
*
|
||||
* <p>To use <code>ActionBarDrawerToggle</code>, create one in your Activity and call through
|
||||
* to the following methods corresponding to your Activity callbacks:</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link android.app.Activity#onConfigurationChanged(android.content.res.Configuration)
|
||||
* onConfigurationChanged}
|
||||
* <li>{@link android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
|
||||
* onOptionsItemSelected}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Call {@link #syncState()} from your <code>Activity</code>'s
|
||||
* {@link android.app.Activity#onPostCreate(android.os.Bundle) onPostCreate} to synchronize the
|
||||
* indicator with the state of the linked DrawerLayout after <code>onRestoreInstanceState</code>
|
||||
* has occurred.</p>
|
||||
*
|
||||
* <p><code>ActionBarDrawerToggle</code> can be used directly as a
|
||||
* {@link android.support.v4.widget.DrawerLayout.DrawerListener}, or if you are already providing
|
||||
* your own listener, call through to each of the listener methods from your own.</p>
|
||||
*
|
||||
* <p>
|
||||
* You can customize the the animated toggle by defining the
|
||||
* {@link android.support.v7.appcompat.R.styleable#DrawerArrowToggle drawerArrowStyle} in your
|
||||
* ActionBar theme.
|
||||
*/
|
||||
public class ActionBarDrawerToggle implements DrawerLayout.DrawerListener {
|
||||
|
||||
/**
|
||||
* Allows an implementing Activity to return an {@link ActionBarDrawerToggle.Delegate} to use
|
||||
* with ActionBarDrawerToggle.
|
||||
*/
|
||||
public interface DelegateProvider {
|
||||
|
||||
/**
|
||||
* @return Delegate to use for ActionBarDrawableToggles, or null if the Activity
|
||||
* does not wish to override the default behavior.
|
||||
*/
|
||||
@Nullable
|
||||
Delegate getDrawerToggleDelegate();
|
||||
}
|
||||
|
||||
public interface Delegate {
|
||||
|
||||
/**
|
||||
* Set the Action Bar's up indicator drawable and content description.
|
||||
*
|
||||
* @param upDrawable - Drawable to set as up indicator
|
||||
* @param contentDescRes - Content description to set
|
||||
*/
|
||||
void setActionBarUpIndicator(Drawable upDrawable, @StringRes int contentDescRes);
|
||||
|
||||
/**
|
||||
* Set the Action Bar's up indicator content description.
|
||||
*
|
||||
* @param contentDescRes - Content description to set
|
||||
*/
|
||||
void setActionBarDescription(@StringRes int contentDescRes);
|
||||
|
||||
/**
|
||||
* Returns the drawable to be set as up button when DrawerToggle is disabled
|
||||
*/
|
||||
Drawable getThemeUpIndicator();
|
||||
|
||||
/**
|
||||
* Returns the context of ActionBar
|
||||
*/
|
||||
Context getActionBarThemedContext();
|
||||
|
||||
/**
|
||||
* Returns whether navigation icon is visible or not.
|
||||
* Used to print warning messages in case developer forgets to set displayHomeAsUp to true
|
||||
*/
|
||||
boolean isNavigationVisible();
|
||||
}
|
||||
|
||||
private final Delegate mActivityImpl;
|
||||
private final DrawerLayout mDrawerLayout;
|
||||
|
||||
private DrawerToggle mSlider;
|
||||
private Drawable mHomeAsUpIndicator;
|
||||
private boolean mDrawerIndicatorEnabled = true;
|
||||
private boolean mHasCustomUpIndicator;
|
||||
private final int mOpenDrawerContentDescRes;
|
||||
private final int mCloseDrawerContentDescRes;
|
||||
// used in toolbar mode when DrawerToggle is disabled
|
||||
private View.OnClickListener mToolbarNavigationClickListener;
|
||||
// If developer does not set displayHomeAsUp, DrawerToggle won't show up.
|
||||
// DrawerToggle logs a warning if this case is detected
|
||||
private boolean mWarnedForDisplayHomeAsUp = false;
|
||||
|
||||
/**
|
||||
* Construct a new ActionBarDrawerToggle.
|
||||
*
|
||||
* <p>The given {@link Activity} will be linked to the specified {@link DrawerLayout} and
|
||||
* its Actionbar's Up button will be set to a custom drawable.
|
||||
* <p>This drawable shows a Hamburger icon when drawer is closed and an arrow when drawer
|
||||
* is open. It animates between these two states as the drawer opens.</p>
|
||||
*
|
||||
* <p>String resources must be provided to describe the open/close drawer actions for
|
||||
* accessibility services.</p>
|
||||
*
|
||||
* @param activity The Activity hosting the drawer. Should have an ActionBar.
|
||||
* @param drawerLayout The DrawerLayout to link to the given Activity's ActionBar
|
||||
* @param openDrawerContentDescRes A String resource to describe the "open drawer" action
|
||||
* for accessibility
|
||||
* @param closeDrawerContentDescRes A String resource to describe the "close drawer" action
|
||||
* for accessibility
|
||||
*/
|
||||
public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout,
|
||||
@StringRes int openDrawerContentDescRes,
|
||||
@StringRes int closeDrawerContentDescRes) {
|
||||
this(activity, null, drawerLayout, null, openDrawerContentDescRes,
|
||||
closeDrawerContentDescRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ActionBarDrawerToggle with a Toolbar.
|
||||
* <p>
|
||||
* The given {@link Activity} will be linked to the specified {@link DrawerLayout} and
|
||||
* the Toolbar's navigation icon will be set to a custom drawable. Using this constructor
|
||||
* will set Toolbar's navigation click listener to toggle the drawer when it is clicked.
|
||||
* <p>
|
||||
* This drawable shows a Hamburger icon when drawer is closed and an arrow when drawer
|
||||
* is open. It animates between these two states as the drawer opens.
|
||||
* <p>
|
||||
* String resources must be provided to describe the open/close drawer actions for
|
||||
* accessibility services.
|
||||
* <p>
|
||||
* Please use {@link #ActionBarDrawerToggle(Activity, DrawerLayout, int, int)} if you are
|
||||
* setting the Toolbar as the ActionBar of your activity.
|
||||
*
|
||||
* @param activity The Activity hosting the drawer.
|
||||
* @param toolbar The toolbar to use if you have an independent Toolbar.
|
||||
* @param drawerLayout The DrawerLayout to link to the given Activity's ActionBar
|
||||
* @param openDrawerContentDescRes A String resource to describe the "open drawer" action
|
||||
* for accessibility
|
||||
* @param closeDrawerContentDescRes A String resource to describe the "close drawer" action
|
||||
* for accessibility
|
||||
*/
|
||||
public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout,
|
||||
Toolbar toolbar, @StringRes int openDrawerContentDescRes,
|
||||
@StringRes int closeDrawerContentDescRes) {
|
||||
this(activity, toolbar, drawerLayout, null, openDrawerContentDescRes,
|
||||
closeDrawerContentDescRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* In the future, we can make this constructor public if we want to let developers customize
|
||||
* the
|
||||
* animation.
|
||||
*/
|
||||
<T extends Drawable & DrawerToggle> ActionBarDrawerToggle(Activity activity, Toolbar toolbar,
|
||||
DrawerLayout drawerLayout, T slider,
|
||||
@StringRes int openDrawerContentDescRes,
|
||||
@StringRes int closeDrawerContentDescRes) {
|
||||
if (toolbar != null) {
|
||||
mActivityImpl = new ToolbarCompatDelegate(toolbar);
|
||||
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mDrawerIndicatorEnabled) {
|
||||
toggle();
|
||||
} else if (mToolbarNavigationClickListener != null) {
|
||||
mToolbarNavigationClickListener.onClick(v);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (activity instanceof DelegateProvider) { // Allow the Activity to provide an impl
|
||||
mActivityImpl = ((DelegateProvider) activity).getDrawerToggleDelegate();
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
mActivityImpl = new JellybeanMr2Delegate(activity);
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
mActivityImpl = new HoneycombDelegate(activity);
|
||||
} else {
|
||||
mActivityImpl = new DummyDelegate(activity);
|
||||
}
|
||||
|
||||
mDrawerLayout = drawerLayout;
|
||||
mOpenDrawerContentDescRes = openDrawerContentDescRes;
|
||||
mCloseDrawerContentDescRes = closeDrawerContentDescRes;
|
||||
if (slider == null) {
|
||||
mSlider = new DrawerArrowDrawableToggle(activity,
|
||||
mActivityImpl.getActionBarThemedContext());
|
||||
} else {
|
||||
mSlider = slider;
|
||||
}
|
||||
|
||||
mHomeAsUpIndicator = getThemeUpIndicator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize the state of the drawer indicator/affordance with the linked DrawerLayout.
|
||||
*
|
||||
* <p>This should be called from your <code>Activity</code>'s
|
||||
* {@link Activity#onPostCreate(android.os.Bundle) onPostCreate} method to synchronize after
|
||||
* the DrawerLayout's instance state has been restored, and any other time when the state
|
||||
* may have diverged in such a way that the ActionBarDrawerToggle was not notified.
|
||||
* (For example, if you stop forwarding appropriate drawer events for a period of time.)</p>
|
||||
*/
|
||||
public void syncState() {
|
||||
if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
|
||||
mSlider.setPosition(1);
|
||||
} else {
|
||||
mSlider.setPosition(0);
|
||||
}
|
||||
if (mDrawerIndicatorEnabled) {
|
||||
setActionBarUpIndicator((Drawable) mSlider,
|
||||
mDrawerLayout.isDrawerOpen(GravityCompat.START) ?
|
||||
mCloseDrawerContentDescRes : mOpenDrawerContentDescRes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should always be called by your <code>Activity</code>'s
|
||||
* {@link Activity#onConfigurationChanged(android.content.res.Configuration)
|
||||
* onConfigurationChanged}
|
||||
* method.
|
||||
*
|
||||
* @param newConfig The new configuration
|
||||
*/
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
// Reload drawables that can change with configuration
|
||||
if (!mHasCustomUpIndicator) {
|
||||
mHomeAsUpIndicator = getThemeUpIndicator();
|
||||
}
|
||||
syncState();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be called by your <code>Activity</code>'s
|
||||
* {@link Activity#onOptionsItemSelected(android.view.MenuItem) onOptionsItemSelected} method.
|
||||
* If it returns true, your <code>onOptionsItemSelected</code> method should return true and
|
||||
* skip further processing.
|
||||
*
|
||||
* @param item the MenuItem instance representing the selected menu item
|
||||
* @return true if the event was handled and further processing should not occur
|
||||
*/
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item != null && item.getItemId() == android.R.id.home && mDrawerIndicatorEnabled) {
|
||||
toggle();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void toggle() {
|
||||
if (mDrawerLayout.isDrawerVisible(GravityCompat.START)) {
|
||||
mDrawerLayout.closeDrawer(GravityCompat.START);
|
||||
} else {
|
||||
mDrawerLayout.openDrawer(GravityCompat.START);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the up indicator to display when the drawer indicator is not
|
||||
* enabled.
|
||||
* <p>
|
||||
* If you pass <code>null</code> to this method, the default drawable from
|
||||
* the theme will be used.
|
||||
*
|
||||
* @param indicator A drawable to use for the up indicator, or null to use
|
||||
* the theme's default
|
||||
* @see #setDrawerIndicatorEnabled(boolean)
|
||||
*/
|
||||
public void setHomeAsUpIndicator(Drawable indicator) {
|
||||
if (indicator == null) {
|
||||
mHomeAsUpIndicator = getThemeUpIndicator();
|
||||
mHasCustomUpIndicator = false;
|
||||
} else {
|
||||
mHomeAsUpIndicator = indicator;
|
||||
mHasCustomUpIndicator = true;
|
||||
}
|
||||
|
||||
if (!mDrawerIndicatorEnabled) {
|
||||
setActionBarUpIndicator(mHomeAsUpIndicator, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the up indicator to display when the drawer indicator is not
|
||||
* enabled.
|
||||
* <p>
|
||||
* If you pass 0 to this method, the default drawable from the theme will
|
||||
* be used.
|
||||
*
|
||||
* @param resId Resource ID of a drawable to use for the up indicator, or 0
|
||||
* to use the theme's default
|
||||
* @see #setDrawerIndicatorEnabled(boolean)
|
||||
*/
|
||||
public void setHomeAsUpIndicator(int resId) {
|
||||
Drawable indicator = null;
|
||||
if (resId != 0) {
|
||||
indicator = mDrawerLayout.getResources().getDrawable(resId);
|
||||
}
|
||||
setHomeAsUpIndicator(indicator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the enhanced drawer indicator is enabled, false otherwise
|
||||
* @see #setDrawerIndicatorEnabled(boolean)
|
||||
*/
|
||||
public boolean isDrawerIndicatorEnabled() {
|
||||
return mDrawerIndicatorEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the drawer indicator. The indicator defaults to enabled.
|
||||
*
|
||||
* <p>When the indicator is disabled, the <code>ActionBar</code> will revert to displaying
|
||||
* the home-as-up indicator provided by the <code>Activity</code>'s theme in the
|
||||
* <code>android.R.attr.homeAsUpIndicator</code> attribute instead of the animated
|
||||
* drawer glyph.</p>
|
||||
*
|
||||
* @param enable true to enable, false to disable
|
||||
*/
|
||||
public void setDrawerIndicatorEnabled(boolean enable) {
|
||||
if (enable != mDrawerIndicatorEnabled) {
|
||||
if (enable) {
|
||||
setActionBarUpIndicator((Drawable) mSlider,
|
||||
mDrawerLayout.isDrawerOpen(GravityCompat.START) ?
|
||||
mCloseDrawerContentDescRes : mOpenDrawerContentDescRes);
|
||||
} else {
|
||||
setActionBarUpIndicator(mHomeAsUpIndicator, 0);
|
||||
}
|
||||
mDrawerIndicatorEnabled = enable;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link DrawerLayout.DrawerListener} callback method. If you do not use your
|
||||
* ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
|
||||
* through to this method from your own listener object.
|
||||
*
|
||||
* @param drawerView The child view that was moved
|
||||
* @param slideOffset The new offset of this drawer within its range, from 0-1
|
||||
*/
|
||||
@Override
|
||||
public void onDrawerSlide(View drawerView, float slideOffset) {
|
||||
mSlider.setPosition(Math.min(1f, Math.max(0, slideOffset)));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link DrawerLayout.DrawerListener} callback method. If you do not use your
|
||||
* ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
|
||||
* through to this method from your own listener object.
|
||||
*
|
||||
* @param drawerView Drawer view that is now open
|
||||
*/
|
||||
@Override
|
||||
public void onDrawerOpened(View drawerView) {
|
||||
mSlider.setPosition(1);
|
||||
if (mDrawerIndicatorEnabled) {
|
||||
setActionBarDescription(mCloseDrawerContentDescRes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link DrawerLayout.DrawerListener} callback method. If you do not use your
|
||||
* ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
|
||||
* through to this method from your own listener object.
|
||||
*
|
||||
* @param drawerView Drawer view that is now closed
|
||||
*/
|
||||
@Override
|
||||
public void onDrawerClosed(View drawerView) {
|
||||
mSlider.setPosition(0);
|
||||
if (mDrawerIndicatorEnabled) {
|
||||
setActionBarDescription(mOpenDrawerContentDescRes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link DrawerLayout.DrawerListener} callback method. If you do not use your
|
||||
* ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
|
||||
* through to this method from your own listener object.
|
||||
*
|
||||
* @param newState The new drawer motion state
|
||||
*/
|
||||
@Override
|
||||
public void onDrawerStateChanged(int newState) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fallback listener for Navigation icon click events.
|
||||
*
|
||||
* @return The click listener which receives Navigation click events from Toolbar when
|
||||
* drawer indicator is disabled.
|
||||
* @see #setToolbarNavigationClickListener(android.view.View.OnClickListener)
|
||||
* @see #setDrawerIndicatorEnabled(boolean)
|
||||
* @see #isDrawerIndicatorEnabled()
|
||||
*/
|
||||
public View.OnClickListener getToolbarNavigationClickListener() {
|
||||
return mToolbarNavigationClickListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* When DrawerToggle is constructed with a Toolbar, it sets the click listener on
|
||||
* the Navigation icon. If you want to listen for clicks on the Navigation icon when
|
||||
* DrawerToggle is disabled ({@link #setDrawerIndicatorEnabled(boolean)}, you should call this
|
||||
* method with your listener and DrawerToggle will forward click events to that listener
|
||||
* when drawer indicator is disabled.
|
||||
*
|
||||
* @see #setDrawerIndicatorEnabled(boolean)
|
||||
*/
|
||||
public void setToolbarNavigationClickListener(
|
||||
View.OnClickListener onToolbarNavigationClickListener) {
|
||||
mToolbarNavigationClickListener = onToolbarNavigationClickListener;
|
||||
}
|
||||
|
||||
void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
|
||||
if (!mWarnedForDisplayHomeAsUp && !mActivityImpl.isNavigationVisible()) {
|
||||
Log.w("ActionBarDrawerToggle", "DrawerToggle may not show up because NavigationIcon"
|
||||
+ " is not visible. You may need to call "
|
||||
+ "actionbar.setDisplayHomeAsUpEnabled(true);");
|
||||
mWarnedForDisplayHomeAsUp = true;
|
||||
}
|
||||
mActivityImpl.setActionBarUpIndicator(upDrawable, contentDescRes);
|
||||
}
|
||||
|
||||
void setActionBarDescription(int contentDescRes) {
|
||||
mActivityImpl.setActionBarDescription(contentDescRes);
|
||||
}
|
||||
|
||||
Drawable getThemeUpIndicator() {
|
||||
return mActivityImpl.getThemeUpIndicator();
|
||||
}
|
||||
|
||||
static class DrawerArrowDrawableToggle extends DrawerArrowDrawable
|
||||
implements DrawerToggle {
|
||||
|
||||
private final Activity mActivity;
|
||||
|
||||
public DrawerArrowDrawableToggle(Activity activity, Context themedContext) {
|
||||
super(themedContext);
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
public void setPosition(float position) {
|
||||
if (position == 1f) {
|
||||
setVerticalMirror(true);
|
||||
} else if (position == 0f) {
|
||||
setVerticalMirror(false);
|
||||
}
|
||||
super.setProgress(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isLayoutRtl() {
|
||||
return ViewCompat.getLayoutDirection(mActivity.getWindow().getDecorView())
|
||||
== ViewCompat.LAYOUT_DIRECTION_RTL;
|
||||
}
|
||||
|
||||
public float getPosition() {
|
||||
return super.getProgress();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for toggle drawables. Can be public in the future
|
||||
*/
|
||||
static interface DrawerToggle {
|
||||
|
||||
public void setPosition(float position);
|
||||
|
||||
public float getPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate if SDK version is between honeycomb and JBMR2
|
||||
*/
|
||||
private static class HoneycombDelegate implements Delegate {
|
||||
|
||||
final Activity mActivity;
|
||||
ActionBarDrawerToggleHoneycomb.SetIndicatorInfo mSetIndicatorInfo;
|
||||
|
||||
private HoneycombDelegate(Activity activity) {
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getThemeUpIndicator() {
|
||||
return ActionBarDrawerToggleHoneycomb.getThemeUpIndicator(mActivity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getActionBarThemedContext() {
|
||||
final ActionBar actionBar = mActivity.getActionBar();
|
||||
final Context context;
|
||||
if (actionBar != null) {
|
||||
context = actionBar.getThemedContext();
|
||||
} else {
|
||||
context = mActivity;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNavigationVisible() {
|
||||
final ActionBar actionBar = mActivity.getActionBar();
|
||||
return actionBar != null
|
||||
&& (actionBar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActionBarUpIndicator(Drawable themeImage, int contentDescRes) {
|
||||
mActivity.getActionBar().setDisplayShowHomeEnabled(true);
|
||||
mSetIndicatorInfo = ActionBarDrawerToggleHoneycomb.setActionBarUpIndicator(
|
||||
mSetIndicatorInfo, mActivity, themeImage, contentDescRes);
|
||||
mActivity.getActionBar().setDisplayShowHomeEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActionBarDescription(int contentDescRes) {
|
||||
mSetIndicatorInfo = ActionBarDrawerToggleHoneycomb.setActionBarDescription(
|
||||
mSetIndicatorInfo, mActivity, contentDescRes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate if SDK version is JB MR2 or newer
|
||||
*/
|
||||
private static class JellybeanMr2Delegate implements Delegate {
|
||||
|
||||
final Activity mActivity;
|
||||
|
||||
private JellybeanMr2Delegate(Activity activity) {
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getThemeUpIndicator() {
|
||||
final TypedArray a = getActionBarThemedContext().obtainStyledAttributes(null,
|
||||
new int[]{android.R.attr.homeAsUpIndicator}, android.R.attr.actionBarStyle, 0);
|
||||
final Drawable result = a.getDrawable(0);
|
||||
a.recycle();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getActionBarThemedContext() {
|
||||
final ActionBar actionBar = mActivity.getActionBar();
|
||||
final Context context;
|
||||
if (actionBar != null) {
|
||||
context = actionBar.getThemedContext();
|
||||
} else {
|
||||
context = mActivity;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNavigationVisible() {
|
||||
final ActionBar actionBar = mActivity.getActionBar();
|
||||
return actionBar != null &&
|
||||
(actionBar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActionBarUpIndicator(Drawable drawable, int contentDescRes) {
|
||||
final ActionBar actionBar = mActivity.getActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setHomeAsUpIndicator(drawable);
|
||||
actionBar.setHomeActionContentDescription(contentDescRes);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActionBarDescription(int contentDescRes) {
|
||||
final ActionBar actionBar = mActivity.getActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setHomeActionContentDescription(contentDescRes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when DrawerToggle is initialized with a Toolbar
|
||||
*/
|
||||
static class ToolbarCompatDelegate implements Delegate {
|
||||
|
||||
final Toolbar mToolbar;
|
||||
final Drawable mDefaultUpIndicator;
|
||||
final CharSequence mDefaultContentDescription;
|
||||
|
||||
ToolbarCompatDelegate(Toolbar toolbar) {
|
||||
mToolbar = toolbar;
|
||||
mDefaultUpIndicator = toolbar.getNavigationIcon();
|
||||
mDefaultContentDescription = toolbar.getNavigationContentDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActionBarUpIndicator(Drawable upDrawable, @StringRes int contentDescRes) {
|
||||
mToolbar.setNavigationIcon(upDrawable);
|
||||
setActionBarDescription(contentDescRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActionBarDescription(@StringRes int contentDescRes) {
|
||||
if (contentDescRes == 0) {
|
||||
mToolbar.setNavigationContentDescription(mDefaultContentDescription);
|
||||
} else {
|
||||
mToolbar.setNavigationContentDescription(contentDescRes);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getThemeUpIndicator() {
|
||||
return mDefaultUpIndicator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getActionBarThemedContext() {
|
||||
return mToolbar.getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNavigationVisible() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback delegate
|
||||
*/
|
||||
static class DummyDelegate implements Delegate {
|
||||
final Activity mActivity;
|
||||
|
||||
DummyDelegate(Activity activity) {
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActionBarUpIndicator(Drawable upDrawable, @StringRes int contentDescRes) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActionBarDescription(@StringRes int contentDescRes) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getThemeUpIndicator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getActionBarThemedContext() {
|
||||
return mActivity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNavigationVisible() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
139
android/support/v7/app/ActionBarDrawerToggleHoneycomb.java
Normal file
139
android/support/v7/app/ActionBarDrawerToggleHoneycomb.java
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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.support.v7.app;
|
||||
|
||||
import android.R;
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* This class encapsulates some awful hacks.
|
||||
*
|
||||
* Before JB-MR2 (API 18) it was not possible to change the home-as-up indicator glyph
|
||||
* in an action bar without some really gross hacks. Since the MR2 SDK is not published as of
|
||||
* this writing, the new API is accessed via reflection here if available.
|
||||
*
|
||||
* Moved from Support-v4
|
||||
*/
|
||||
class ActionBarDrawerToggleHoneycomb {
|
||||
private static final String TAG = "ActionBarDrawerToggleHoneycomb";
|
||||
|
||||
private static final int[] THEME_ATTRS = new int[] {
|
||||
R.attr.homeAsUpIndicator
|
||||
};
|
||||
|
||||
public static SetIndicatorInfo setActionBarUpIndicator(SetIndicatorInfo info, Activity activity,
|
||||
Drawable drawable, int contentDescRes) {
|
||||
if (true || info == null) {
|
||||
info = new SetIndicatorInfo(activity);
|
||||
}
|
||||
if (info.setHomeAsUpIndicator != null) {
|
||||
try {
|
||||
final ActionBar actionBar = activity.getActionBar();
|
||||
info.setHomeAsUpIndicator.invoke(actionBar, drawable);
|
||||
info.setHomeActionContentDescription.invoke(actionBar, contentDescRes);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Couldn't set home-as-up indicator via JB-MR2 API", e);
|
||||
}
|
||||
} else if (info.upIndicatorView != null) {
|
||||
info.upIndicatorView.setImageDrawable(drawable);
|
||||
} else {
|
||||
Log.w(TAG, "Couldn't set home-as-up indicator");
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
public static SetIndicatorInfo setActionBarDescription(SetIndicatorInfo info, Activity activity,
|
||||
int contentDescRes) {
|
||||
if (info == null) {
|
||||
info = new SetIndicatorInfo(activity);
|
||||
}
|
||||
if (info.setHomeAsUpIndicator != null) {
|
||||
try {
|
||||
final ActionBar actionBar = activity.getActionBar();
|
||||
info.setHomeActionContentDescription.invoke(actionBar, contentDescRes);
|
||||
if (Build.VERSION.SDK_INT <= 19) {
|
||||
// For API 19 and earlier, we need to manually force the
|
||||
// action bar to generate a new content description.
|
||||
actionBar.setSubtitle(actionBar.getSubtitle());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Couldn't set content description via JB-MR2 API", e);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
public static Drawable getThemeUpIndicator(Activity activity) {
|
||||
final TypedArray a = activity.obtainStyledAttributes(THEME_ATTRS);
|
||||
final Drawable result = a.getDrawable(0);
|
||||
a.recycle();
|
||||
return result;
|
||||
}
|
||||
|
||||
static class SetIndicatorInfo {
|
||||
public Method setHomeAsUpIndicator;
|
||||
public Method setHomeActionContentDescription;
|
||||
public ImageView upIndicatorView;
|
||||
|
||||
SetIndicatorInfo(Activity activity) {
|
||||
try {
|
||||
setHomeAsUpIndicator = ActionBar.class.getDeclaredMethod("setHomeAsUpIndicator",
|
||||
Drawable.class);
|
||||
setHomeActionContentDescription = ActionBar.class.getDeclaredMethod(
|
||||
"setHomeActionContentDescription", Integer.TYPE);
|
||||
|
||||
// If we got the method we won't need the stuff below.
|
||||
return;
|
||||
} catch (NoSuchMethodException e) {
|
||||
// Oh well. We'll use the other mechanism below instead.
|
||||
}
|
||||
|
||||
final View home = activity.findViewById(android.R.id.home);
|
||||
if (home == null) {
|
||||
// Action bar doesn't have a known configuration, an OEM messed with things.
|
||||
return;
|
||||
}
|
||||
|
||||
final ViewGroup parent = (ViewGroup) home.getParent();
|
||||
final int childCount = parent.getChildCount();
|
||||
if (childCount != 2) {
|
||||
// No idea which one will be the right one, an OEM messed with things.
|
||||
return;
|
||||
}
|
||||
|
||||
final View first = parent.getChildAt(0);
|
||||
final View second = parent.getChildAt(1);
|
||||
final View up = first.getId() == android.R.id.home ? second : first;
|
||||
|
||||
if (up instanceof ImageView) {
|
||||
// Jackpot! (Probably...)
|
||||
upIndicatorView = (ImageView) up;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
416
android/support/v7/app/AppCompatActivity.java
Normal file
416
android/support/v7/app/AppCompatActivity.java
Normal file
|
@ -0,0 +1,416 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.support.v7.app;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* Base class for activities that use the
|
||||
* <a href="{@docRoot}tools/extras/support-library.html">support library</a> action bar features.
|
||||
*
|
||||
* <p>You can add an {@link android.support.v7.app.ActionBar} to your activity when running on API level 7 or higher
|
||||
* by extending this class for your activity and setting the activity theme to
|
||||
* {@link android.support.v7.appcompat.R.style#Theme_AppCompat Theme.AppCompat} or a similar theme.
|
||||
*
|
||||
* <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
|
||||
* modes and more, read the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action
|
||||
* Bar</a> API guide.</p>
|
||||
* </div>
|
||||
*/
|
||||
public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
|
||||
TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
|
||||
|
||||
private AppCompatDelegate mDelegate;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
getDelegate().installViewFactory();
|
||||
super.onCreate(savedInstanceState);
|
||||
getDelegate().onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
getDelegate().onPostCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Support library version of {@link android.app.Activity#getActionBar}.
|
||||
*
|
||||
* <p>Retrieve a reference to this activity's ActionBar.
|
||||
*
|
||||
* @return The Activity's ActionBar, or null if it does not have one.
|
||||
*/
|
||||
public ActionBar getSupportActionBar() {
|
||||
return getDelegate().getSupportActionBar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a {@link android.widget.Toolbar Toolbar} to act as the {@link android.support.v7.app.ActionBar} for this
|
||||
* Activity window.
|
||||
*
|
||||
* <p>When set to a non-null value the {@link #getActionBar()} method will return
|
||||
* an {@link android.support.v7.app.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 android.view.Window#FEATURE_ACTION_BAR FEATURE_ACTION_BAR}.</p>
|
||||
*
|
||||
* @param toolbar Toolbar to set as the Activity's action bar
|
||||
*/
|
||||
public void setSupportActionBar(@Nullable Toolbar toolbar) {
|
||||
getDelegate().setSupportActionBar(toolbar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuInflater getMenuInflater() {
|
||||
return getDelegate().getMenuInflater();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(@LayoutRes int layoutResID) {
|
||||
getDelegate().setContentView(layoutResID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view) {
|
||||
getDelegate().setContentView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view, ViewGroup.LayoutParams params) {
|
||||
getDelegate().setContentView(view, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addContentView(View view, ViewGroup.LayoutParams params) {
|
||||
getDelegate().addContentView(view, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
getDelegate().onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getDelegate().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostResume() {
|
||||
super.onPostResume();
|
||||
getDelegate().onPostResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean onMenuItemSelected(int featureId, android.view.MenuItem item) {
|
||||
if (super.onMenuItemSelected(featureId, item)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final ActionBar ab = getSupportActionBar();
|
||||
if (item.getItemId() == android.R.id.home && ab != null &&
|
||||
(ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
|
||||
return onSupportNavigateUp();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
getDelegate().onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTitleChanged(CharSequence title, int color) {
|
||||
super.onTitleChanged(title, color);
|
||||
getDelegate().setTitle(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable extended support library window features.
|
||||
* <p>
|
||||
* This is a convenience for calling
|
||||
* {@link android.view.Window#requestFeature getWindow().requestFeature()}.
|
||||
* </p>
|
||||
*
|
||||
* @param featureId The desired feature as defined in
|
||||
* {@link android.view.Window} or {@link android.support.v4.view.WindowCompat}.
|
||||
* @return Returns true if the requested feature is supported and now enabled.
|
||||
*
|
||||
* @see android.app.Activity#requestWindowFeature
|
||||
* @see android.view.Window#requestFeature
|
||||
*/
|
||||
public boolean supportRequestWindowFeature(int featureId) {
|
||||
return getDelegate().requestWindowFeature(featureId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void supportInvalidateOptionsMenu() {
|
||||
getDelegate().invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void invalidateOptionsMenu() {
|
||||
getDelegate().invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the Activity that a support action mode has been started.
|
||||
* Activity subclasses overriding this method should call the superclass implementation.
|
||||
*
|
||||
* @param mode The new action mode.
|
||||
*/
|
||||
public void onSupportActionModeStarted(ActionMode mode) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the activity that a support action mode has finished.
|
||||
* Activity subclasses overriding this method should call the superclass implementation.
|
||||
*
|
||||
* @param mode The action mode that just finished.
|
||||
*/
|
||||
public void onSupportActionModeFinished(ActionMode mode) {
|
||||
}
|
||||
|
||||
public ActionMode startSupportActionMode(ActionMode.Callback callback) {
|
||||
return getDelegate().startSupportActionMode(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Progress bars are no longer provided in AppCompat.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setSupportProgressBarVisibility(boolean visible) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Progress bars are no longer provided in AppCompat.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setSupportProgressBarIndeterminateVisibility(boolean visible) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Progress bars are no longer provided in AppCompat.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setSupportProgressBarIndeterminate(boolean indeterminate) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Progress bars are no longer provided in AppCompat.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setSupportProgress(int progress) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Support version of {@link #onCreateNavigateUpTaskStack(android.app.TaskStackBuilder)}.
|
||||
* This method will be called on all platform versions.
|
||||
*
|
||||
* Define the synthetic task stack that will be generated during Up navigation from
|
||||
* a different task.
|
||||
*
|
||||
* <p>The default implementation of this method adds the parent chain of this activity
|
||||
* as specified in the manifest to the supplied {@link android.support.v4.app.TaskStackBuilder}. Applications
|
||||
* may choose to override this method to construct the desired task stack in a different
|
||||
* way.</p>
|
||||
*
|
||||
* <p>This method will be invoked by the default implementation of {@link #onNavigateUp()}
|
||||
* if {@link #shouldUpRecreateTask(android.content.Intent)} returns true when supplied with the intent
|
||||
* returned by {@link #getParentActivityIntent()}.</p>
|
||||
*
|
||||
* <p>Applications that wish to supply extra Intent parameters to the parent stack defined
|
||||
* by the manifest should override
|
||||
* {@link #onPrepareSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder)}.</p>
|
||||
*
|
||||
* @param builder An empty TaskStackBuilder - the application should add intents representing
|
||||
* the desired task stack
|
||||
*/
|
||||
public void onCreateSupportNavigateUpTaskStack(TaskStackBuilder builder) {
|
||||
builder.addParentStack(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Support version of {@link #onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder)}.
|
||||
* This method will be called on all platform versions.
|
||||
*
|
||||
* Prepare the synthetic task stack that will be generated during Up navigation
|
||||
* from a different task.
|
||||
*
|
||||
* <p>This method receives the {@link android.support.v4.app.TaskStackBuilder} with the constructed series of
|
||||
* Intents as generated by {@link #onCreateSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder)}.
|
||||
* If any extra data should be added to these intents before launching the new task,
|
||||
* the application should override this method and add that data here.</p>
|
||||
*
|
||||
* @param builder A TaskStackBuilder that has been populated with Intents by
|
||||
* onCreateNavigateUpTaskStack.
|
||||
*/
|
||||
public void onPrepareSupportNavigateUpTaskStack(TaskStackBuilder builder) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called whenever the user chooses to navigate Up within your application's
|
||||
* activity hierarchy from the action bar.
|
||||
*
|
||||
* <p>If a parent was specified in the manifest for this activity or an activity-alias to it,
|
||||
* default Up navigation will be handled automatically. See
|
||||
* {@link #getSupportParentActivityIntent()} for how to specify the parent. If any activity
|
||||
* along the parent chain requires extra Intent arguments, the Activity subclass
|
||||
* should override the method {@link #onPrepareSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder)}
|
||||
* to supply those arguments.</p>
|
||||
*
|
||||
* <p>See <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and
|
||||
* Back Stack</a> from the developer guide and
|
||||
* <a href="{@docRoot}design/patterns/navigation.html">Navigation</a> from the design guide
|
||||
* for more information about navigating within your app.</p>
|
||||
*
|
||||
* <p>See the {@link android.support.v4.app.TaskStackBuilder} class and the Activity methods
|
||||
* {@link #getSupportParentActivityIntent()}, {@link #supportShouldUpRecreateTask(android.content.Intent)}, and
|
||||
* {@link #supportNavigateUpTo(android.content.Intent)} for help implementing custom Up navigation.</p>
|
||||
*
|
||||
* @return true if Up navigation completed successfully and this Activity was finished,
|
||||
* false otherwise.
|
||||
*/
|
||||
public boolean onSupportNavigateUp() {
|
||||
Intent upIntent = getSupportParentActivityIntent();
|
||||
|
||||
if (upIntent != null) {
|
||||
if (supportShouldUpRecreateTask(upIntent)) {
|
||||
TaskStackBuilder b = TaskStackBuilder.create(this);
|
||||
onCreateSupportNavigateUpTaskStack(b);
|
||||
onPrepareSupportNavigateUpTaskStack(b);
|
||||
b.startActivities();
|
||||
|
||||
try {
|
||||
ActivityCompat.finishAffinity(this);
|
||||
} catch (IllegalStateException e) {
|
||||
// This can only happen on 4.1+, when we don't have a parent or a result set.
|
||||
// In that case we should just finish().
|
||||
finish();
|
||||
}
|
||||
} else {
|
||||
// This activity is part of the application's task, so simply
|
||||
// navigate up to the hierarchical parent activity.
|
||||
supportNavigateUpTo(upIntent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain an {@link android.content.Intent} that will launch an explicit target activity
|
||||
* specified by sourceActivity's {@link android.support.v4.app.NavUtils#PARENT_ACTIVITY} <meta-data>
|
||||
* element in the application's manifest. If the device is running
|
||||
* Jellybean or newer, the android:parentActivityName attribute will be preferred
|
||||
* if it is present.
|
||||
*
|
||||
* @return a new Intent targeting the defined parent activity of sourceActivity
|
||||
*/
|
||||
public Intent getSupportParentActivityIntent() {
|
||||
return NavUtils.getParentActivityIntent(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if sourceActivity should recreate the task when navigating 'up'
|
||||
* by using targetIntent.
|
||||
*
|
||||
* <p>If this method returns false the app can trivially call
|
||||
* {@link #supportNavigateUpTo(android.content.Intent)} using the same parameters to correctly perform
|
||||
* up navigation. If this method returns false, the app should synthesize a new task stack
|
||||
* by using {@link android.support.v4.app.TaskStackBuilder} or another similar mechanism to perform up navigation.</p>
|
||||
*
|
||||
* @param targetIntent An intent representing the target destination for up navigation
|
||||
* @return true if navigating up should recreate a new task stack, false if the same task
|
||||
* should be used for the destination
|
||||
*/
|
||||
public boolean supportShouldUpRecreateTask(Intent targetIntent) {
|
||||
return NavUtils.shouldUpRecreateTask(this, targetIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate from sourceActivity to the activity specified by upIntent, finishing sourceActivity
|
||||
* in the process. upIntent will have the flag {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP} set
|
||||
* by this method, along with any others required for proper up navigation as outlined
|
||||
* in the Android Design Guide.
|
||||
*
|
||||
* <p>This method should be used when performing up navigation from within the same task
|
||||
* as the destination. If up navigation should cross tasks in some cases, see
|
||||
* {@link #supportShouldUpRecreateTask(android.content.Intent)}.</p>
|
||||
*
|
||||
* @param upIntent An intent representing the target destination for up navigation
|
||||
*/
|
||||
public void supportNavigateUpTo(Intent upIntent) {
|
||||
NavUtils.navigateUpTo(this, upIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContentChanged() {
|
||||
// Call onSupportContentChanged() for legacy reasons
|
||||
onSupportContentChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #onContentChanged()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void onSupportContentChanged() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() {
|
||||
return getDelegate().getDrawerToggleDelegate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The {@link AppCompatDelegate} being used by this Activity.
|
||||
*/
|
||||
public AppCompatDelegate getDelegate() {
|
||||
if (mDelegate == null) {
|
||||
mDelegate = AppCompatDelegate.create(this, this);
|
||||
}
|
||||
return mDelegate;
|
||||
}
|
||||
}
|
43
android/support/v7/app/AppCompatCallback.java
Normal file
43
android/support/v7/app/AppCompatCallback.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.support.v7.app;
|
||||
|
||||
import android.support.v7.view.ActionMode;
|
||||
|
||||
/**
|
||||
* Implemented this in order for AppCompat to be able to callback in certain situations.
|
||||
* <p>
|
||||
* This should be provided to
|
||||
* {@link AppCompatDelegate#create(android.app.Activity, AppCompatCallback)}.
|
||||
*/
|
||||
public interface AppCompatCallback {
|
||||
|
||||
/**
|
||||
* Called when a support action mode has been started.
|
||||
*
|
||||
* @param mode The new action mode.
|
||||
*/
|
||||
void onSupportActionModeStarted(ActionMode mode);
|
||||
|
||||
/**
|
||||
* Called when a support action mode has finished.
|
||||
*
|
||||
* @param mode The action mode that just finished.
|
||||
*/
|
||||
void onSupportActionModeFinished(ActionMode mode);
|
||||
|
||||
}
|
251
android/support/v7/app/AppCompatDelegate.java
Normal file
251
android/support/v7/app/AppCompatDelegate.java
Normal file
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* 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.support.v7.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* This class represents a delegate which you can use to extend AppCompat's support to any
|
||||
* {@link android.app.Activity}.
|
||||
* <p>
|
||||
* When using an {@link AppCompatDelegate}, you should any methods exposed in it rather than the
|
||||
* {@link android.app.Activity} method of the same name. This applies to:
|
||||
* <ul>
|
||||
* <li>{@link #addContentView(android.view.View, android.view.ViewGroup.LayoutParams)}</li>
|
||||
* <li>{@link #setContentView(int)}</li>
|
||||
* <li>{@link #setContentView(android.view.View)}</li>
|
||||
* <li>{@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}</li>
|
||||
* <li>{@link #requestWindowFeature(int)}</li>
|
||||
* <li>{@link #invalidateOptionsMenu()}</li>
|
||||
* <li>{@link #startSupportActionMode(android.support.v7.view.ActionMode.Callback)}</li>
|
||||
* <li>{@link #setSupportActionBar(android.support.v7.widget.Toolbar)}</li>
|
||||
* <li>{@link #getSupportActionBar()}</li>
|
||||
* <li>{@link #getMenuInflater()}</li>
|
||||
* </ul>
|
||||
* There also some Activity lifecycle methods which should be proxied to the delegate:
|
||||
* <ul>
|
||||
* <li>{@link #onCreate(android.os.Bundle)}</li>
|
||||
* <li>{@link #onPostCreate(android.os.Bundle)}</li>
|
||||
* <li>{@link #onConfigurationChanged(android.content.res.Configuration)}</li>
|
||||
* <li>{@link #setTitle(CharSequence)}</li>
|
||||
* <li>{@link #onStop()}</li>
|
||||
* <li>{@link #onDestroy()}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* An {@link Activity} can only be linked with one {@link AppCompatDelegate} instance,
|
||||
* so the instance returned from {@link #create(Activity, AppCompatCallback)} should be kept
|
||||
* until the Activity is destroyed.
|
||||
*/
|
||||
public abstract class AppCompatDelegate {
|
||||
|
||||
static final String TAG = "AppCompatDelegate";
|
||||
|
||||
/**
|
||||
* Create a {@link android.support.v7.app.AppCompatDelegate} to use with {@code activity}.
|
||||
*
|
||||
* @param callback An optional callback for AppCompat specific events
|
||||
*/
|
||||
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
return new AppCompatDelegateImplV11(activity, activity.getWindow(), callback);
|
||||
} else {
|
||||
return new AppCompatDelegateImplV7(activity, activity.getWindow(), callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link android.support.v7.app.AppCompatDelegate} to use with {@code dialog}.
|
||||
*
|
||||
* @param callback An optional callback for AppCompat specific events
|
||||
*/
|
||||
public static AppCompatDelegate create(Dialog dialog, AppCompatCallback callback) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
return new AppCompatDelegateImplV11(dialog.getContext(), dialog.getWindow(), callback);
|
||||
} else {
|
||||
return new AppCompatDelegateImplV7(dialog.getContext(), dialog.getWindow(), callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor
|
||||
*/
|
||||
AppCompatDelegate() {}
|
||||
|
||||
/**
|
||||
* Support library version of {@link Activity#getActionBar}.
|
||||
*
|
||||
* @return AppCompat's action bar, or null if it does not have one.
|
||||
*/
|
||||
public abstract ActionBar getSupportActionBar();
|
||||
|
||||
/**
|
||||
* Set a {@link Toolbar} to act as the {@link ActionBar} for this delegate.
|
||||
*
|
||||
* <p>When set to a non-null value the {@link #getSupportActionBar()} ()} 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 android.view.Window#FEATURE_ACTION_BAR FEATURE_ACTION_BAR}.</p>
|
||||
*
|
||||
* @param toolbar Toolbar to set as the Activity's action bar
|
||||
*/
|
||||
public abstract void setSupportActionBar(Toolbar toolbar);
|
||||
|
||||
/**
|
||||
* Return the value of this call from your {@link Activity#getMenuInflater()}
|
||||
*/
|
||||
public abstract MenuInflater getMenuInflater();
|
||||
|
||||
/**
|
||||
* Should be called from {@link Activity#onCreate Activity.onCreate()}
|
||||
*/
|
||||
public abstract void onCreate(Bundle savedInstanceState);
|
||||
|
||||
/**
|
||||
* Should be called from {@link Activity#onPostCreate(android.os.Bundle)}
|
||||
*/
|
||||
public abstract void onPostCreate(Bundle savedInstanceState);
|
||||
|
||||
/**
|
||||
* Should be called from
|
||||
* {@link Activity#onConfigurationChanged}
|
||||
*/
|
||||
public abstract void onConfigurationChanged(Configuration newConfig);
|
||||
|
||||
/**
|
||||
* Should be called from {@link Activity#onStop Activity.onStop()}
|
||||
*/
|
||||
public abstract void onStop();
|
||||
|
||||
/**
|
||||
* Should be called from {@link Activity#onPostResume()}
|
||||
*/
|
||||
public abstract void onPostResume();
|
||||
|
||||
/**
|
||||
* Should be called instead of {@link Activity#setContentView(android.view.View)}}
|
||||
*/
|
||||
public abstract void setContentView(View v);
|
||||
|
||||
/**
|
||||
* Should be called instead of {@link Activity#setContentView(int)}}
|
||||
*/
|
||||
public abstract void setContentView(int resId);
|
||||
|
||||
/**
|
||||
* Should be called instead of
|
||||
* {@link Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}}
|
||||
*/
|
||||
public abstract void setContentView(View v, ViewGroup.LayoutParams lp);
|
||||
|
||||
/**
|
||||
* Should be called instead of
|
||||
* {@link Activity#addContentView(android.view.View, android.view.ViewGroup.LayoutParams)}}
|
||||
*/
|
||||
public abstract void addContentView(View v, ViewGroup.LayoutParams lp);
|
||||
|
||||
/**
|
||||
* Should be called from {@link Activity#onTitleChanged(CharSequence, int)}}
|
||||
*/
|
||||
public abstract void setTitle(CharSequence title);
|
||||
|
||||
/**
|
||||
* Should be called from {@link Activity#invalidateOptionsMenu()}} or
|
||||
* {@link FragmentActivity#supportInvalidateOptionsMenu()}.
|
||||
*/
|
||||
public abstract void invalidateOptionsMenu();
|
||||
|
||||
/**
|
||||
* Should be called from {@link Activity#onDestroy()}
|
||||
*/
|
||||
public abstract void onDestroy();
|
||||
|
||||
/**
|
||||
* Returns an {@link ActionBarDrawerToggle.Delegate} which can be returned from your Activity
|
||||
* if it implements {@link ActionBarDrawerToggle.DelegateProvider}.
|
||||
*/
|
||||
public abstract ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
|
||||
|
||||
/**
|
||||
* Enable extended window features. This should be called instead of
|
||||
* {@link android.app.Activity#requestWindowFeature(int)} or
|
||||
* {@link android.view.Window#requestFeature getWindow().requestFeature()}.
|
||||
*
|
||||
* @param featureId The desired feature as defined in {@link android.view.Window}.
|
||||
* @return Returns true if the requested feature is supported and now
|
||||
* enabled.
|
||||
*/
|
||||
public abstract boolean requestWindowFeature(int featureId);
|
||||
|
||||
/**
|
||||
* Start an action mode.
|
||||
*
|
||||
* @param callback Callback that will manage lifecycle events for this context mode
|
||||
* @return The ContextMode that was started, or null if it was canceled
|
||||
*/
|
||||
public abstract ActionMode startSupportActionMode(ActionMode.Callback callback);
|
||||
|
||||
/**
|
||||
* Installs AppCompat's {@link android.view.LayoutInflater} Factory so that it can replace
|
||||
* the framework widgets with compatible tinted versions. This should be called before
|
||||
* {@code super.onCreate()} as so:
|
||||
* <pre class="prettyprint">
|
||||
* protected void onCreate(Bundle savedInstanceState) {
|
||||
* getDelegate().installViewFactory();
|
||||
* super.onCreate(savedInstanceState);
|
||||
* getDelegate().onCreate(savedInstanceState);
|
||||
*
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* If you are using your own {@link android.view.LayoutInflater.Factory Factory} or
|
||||
* {@link android.view.LayoutInflater.Factory2 Factory2} then you can omit this call, and instead call
|
||||
* {@link #createView(android.view.View, String, android.content.Context, android.util.AttributeSet)}
|
||||
* from your factory to return any compatible widgets.
|
||||
*/
|
||||
public abstract void installViewFactory();
|
||||
|
||||
/**
|
||||
* This should be called from a
|
||||
* {@link android.support.v4.view.LayoutInflaterFactory LayoutInflaterFactory} in order
|
||||
* to return tint-aware widgets.
|
||||
* <p>
|
||||
* This is only needed if you are using your own
|
||||
* {@link android.view.LayoutInflater LayoutInflater} factory, and have therefore not
|
||||
* installed the default factory via {@link #installViewFactory()}.
|
||||
*/
|
||||
public abstract View createView(View parent, String name, @NonNull Context context,
|
||||
@NonNull AttributeSet attrs);
|
||||
|
||||
}
|
325
android/support/v7/app/AppCompatDelegateImplBase.java
Normal file
325
android/support/v7/app/AppCompatDelegateImplBase.java
Normal file
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.support.v7.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.internal.view.SupportMenuInflater;
|
||||
import android.support.v7.internal.view.WindowCallbackWrapper;
|
||||
import android.support.v7.internal.view.menu.MenuBuilder;
|
||||
import android.support.v7.internal.widget.TintTypedArray;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
|
||||
abstract class AppCompatDelegateImplBase extends AppCompatDelegate {
|
||||
|
||||
final Context mContext;
|
||||
final Window mWindow;
|
||||
final Window.Callback mOriginalWindowCallback;
|
||||
final AppCompatCallback mAppCompatCallback;
|
||||
|
||||
private ActionBar mActionBar;
|
||||
private MenuInflater mMenuInflater;
|
||||
|
||||
// true if this activity has an action bar.
|
||||
boolean mHasActionBar;
|
||||
// true if this activity's action bar overlays other activity content.
|
||||
boolean mOverlayActionBar;
|
||||
// true if this any action modes should overlay the activity content
|
||||
boolean mOverlayActionMode;
|
||||
// true if this activity is floating (e.g. Dialog)
|
||||
boolean mIsFloating;
|
||||
// true if this activity has no title
|
||||
boolean mWindowNoTitle;
|
||||
|
||||
private CharSequence mTitle;
|
||||
|
||||
private boolean mIsDestroyed;
|
||||
|
||||
AppCompatDelegateImplBase(Context context, Window window, AppCompatCallback callback) {
|
||||
mContext = context;
|
||||
mWindow = window;
|
||||
mAppCompatCallback = callback;
|
||||
|
||||
mOriginalWindowCallback = mWindow.getCallback();
|
||||
if (mOriginalWindowCallback instanceof AppCompatWindowCallback) {
|
||||
throw new IllegalStateException(
|
||||
"AppCompat has already installed itself into the Window");
|
||||
}
|
||||
// Now install the new callback
|
||||
mWindow.setCallback(new AppCompatWindowCallback(mOriginalWindowCallback));
|
||||
}
|
||||
|
||||
abstract ActionBar createSupportActionBar();
|
||||
|
||||
@Override
|
||||
public ActionBar getSupportActionBar() {
|
||||
// The Action Bar should be lazily created as hasActionBar
|
||||
// could change after onCreate
|
||||
if (mHasActionBar) {
|
||||
if (mActionBar == null) {
|
||||
mActionBar = createSupportActionBar();
|
||||
}
|
||||
}
|
||||
return mActionBar;
|
||||
}
|
||||
|
||||
final ActionBar peekSupportActionBar() {
|
||||
return mActionBar;
|
||||
}
|
||||
|
||||
final void setSupportActionBar(ActionBar actionBar) {
|
||||
mActionBar = actionBar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuInflater getMenuInflater() {
|
||||
if (mMenuInflater == null) {
|
||||
mMenuInflater = new SupportMenuInflater(getActionBarThemedContext());
|
||||
}
|
||||
return mMenuInflater;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
TypedArray a = mContext.obtainStyledAttributes(R.styleable.Theme);
|
||||
|
||||
if (!a.hasValue(R.styleable.Theme_windowActionBar)) {
|
||||
a.recycle();
|
||||
throw new IllegalStateException(
|
||||
"You need to use a Theme.AppCompat theme (or descendant) with this activity.");
|
||||
}
|
||||
|
||||
if (a.getBoolean(R.styleable.Theme_windowActionBar, false)) {
|
||||
mHasActionBar = true;
|
||||
}
|
||||
if (a.getBoolean(R.styleable.Theme_windowActionBarOverlay, false)) {
|
||||
mOverlayActionBar = true;
|
||||
}
|
||||
if (a.getBoolean(R.styleable.Theme_windowActionModeOverlay, false)) {
|
||||
mOverlayActionMode = true;
|
||||
}
|
||||
mIsFloating = a.getBoolean(R.styleable.Theme_android_windowIsFloating, false);
|
||||
mWindowNoTitle = a.getBoolean(R.styleable.Theme_windowNoTitle, false);
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
// Methods used to create and respond to options menu
|
||||
abstract boolean onPanelClosed(int featureId, Menu menu);
|
||||
|
||||
abstract boolean onMenuOpened(int featureId, Menu menu);
|
||||
|
||||
abstract boolean dispatchKeyEvent(KeyEvent event);
|
||||
|
||||
abstract boolean onKeyShortcut(int keyCode, KeyEvent event);
|
||||
|
||||
@Override
|
||||
public final ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() {
|
||||
return new ActionBarDrawableToggleImpl();
|
||||
}
|
||||
|
||||
final Context getActionBarThemedContext() {
|
||||
Context context = null;
|
||||
|
||||
// If we have an action bar, let it return a themed context
|
||||
ActionBar ab = getSupportActionBar();
|
||||
if (ab != null) {
|
||||
context = ab.getThemedContext();
|
||||
}
|
||||
|
||||
if (context == null) {
|
||||
context = mContext;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
private class ActionBarDrawableToggleImpl implements ActionBarDrawerToggle.Delegate {
|
||||
@Override
|
||||
public Drawable getThemeUpIndicator() {
|
||||
final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
|
||||
getActionBarThemedContext(), null, new int[]{ R.attr.homeAsUpIndicator });
|
||||
final Drawable result = a.getDrawable(0);
|
||||
a.recycle();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getActionBarThemedContext() {
|
||||
return AppCompatDelegateImplBase.this.getActionBarThemedContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNavigationVisible() {
|
||||
final ActionBar ab = getSupportActionBar();
|
||||
return ab != null && (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
|
||||
ActionBar ab = getSupportActionBar();
|
||||
if (ab != null) {
|
||||
ab.setHomeAsUpIndicator(upDrawable);
|
||||
ab.setHomeActionContentDescription(contentDescRes);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActionBarDescription(int contentDescRes) {
|
||||
ActionBar ab = getSupportActionBar();
|
||||
if (ab != null) {
|
||||
ab.setHomeActionContentDescription(contentDescRes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract ActionMode startSupportActionModeFromWindow(ActionMode.Callback callback);
|
||||
|
||||
@Override
|
||||
public final void onDestroy() {
|
||||
mIsDestroyed = true;
|
||||
}
|
||||
|
||||
final boolean isDestroyed() {
|
||||
return mIsDestroyed;
|
||||
}
|
||||
|
||||
final Window.Callback getWindowCallback() {
|
||||
return mWindow.getCallback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setTitle(CharSequence title) {
|
||||
mTitle = title;
|
||||
onTitleChanged(title);
|
||||
}
|
||||
|
||||
abstract void onTitleChanged(CharSequence title);
|
||||
|
||||
final CharSequence getTitle() {
|
||||
// If the original window callback is an Activity, we'll use it's title
|
||||
if (mOriginalWindowCallback instanceof Activity) {
|
||||
return ((Activity) mOriginalWindowCallback).getTitle();
|
||||
}
|
||||
// Else, we'll return the title we have recorded ourselves
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
private class AppCompatWindowCallback extends WindowCallbackWrapper {
|
||||
AppCompatWindowCallback(Window.Callback callback) {
|
||||
super(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
if (AppCompatDelegateImplBase.this.dispatchKeyEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreatePanelMenu(int featureId, Menu menu) {
|
||||
if (featureId == Window.FEATURE_OPTIONS_PANEL && !(menu instanceof MenuBuilder)) {
|
||||
// If this is an options menu but it's not an AppCompat menu, we eat the event
|
||||
// and return false
|
||||
return false;
|
||||
}
|
||||
return super.onCreatePanelMenu(featureId, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreparePanel(int featureId, View view, Menu menu) {
|
||||
if (featureId == Window.FEATURE_OPTIONS_PANEL && !(menu instanceof MenuBuilder)) {
|
||||
// If this is an options menu but it's not an AppCompat menu, we eat the event
|
||||
// and return false
|
||||
return false;
|
||||
}
|
||||
|
||||
if (featureId == Window.FEATURE_OPTIONS_PANEL && bypassPrepareOptionsPanelIfNeeded()) {
|
||||
// If this is an options menu and we need to bypass onPreparePanel, do so
|
||||
if (mOriginalWindowCallback instanceof Activity) {
|
||||
return ((Activity) mOriginalWindowCallback).onPrepareOptionsMenu(menu);
|
||||
} else if (mOriginalWindowCallback instanceof Dialog) {
|
||||
return ((Dialog) mOriginalWindowCallback).onPrepareOptionsMenu(menu);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Else, defer to the default handling
|
||||
return super.onPreparePanel(featureId, view, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuOpened(int featureId, Menu menu) {
|
||||
if (AppCompatDelegateImplBase.this.onMenuOpened(featureId, menu)) {
|
||||
return true;
|
||||
}
|
||||
return super.onMenuOpened(featureId, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
|
||||
if (AppCompatDelegateImplBase.this.onKeyShortcut(event.getKeyCode(), event)) {
|
||||
return true;
|
||||
}
|
||||
return super.dispatchKeyShortcutEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContentChanged() {
|
||||
// We purposely do not propagate this call as this is called when we install
|
||||
// our sub-decor rather than the user's content
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPanelClosed(int featureId, Menu menu) {
|
||||
if (AppCompatDelegateImplBase.this.onPanelClosed(featureId, menu)) {
|
||||
return;
|
||||
}
|
||||
super.onPanelClosed(featureId, menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* For the options menu, we may need to call onPrepareOptionsMenu() directly,
|
||||
* bypassing onPreparePanel(). This is because onPreparePanel() in certain situations
|
||||
* calls menu.hasVisibleItems(), which interferes with any initial invisible items.
|
||||
*
|
||||
* @return true if onPrepareOptionsMenu should be called directly.
|
||||
*/
|
||||
private boolean bypassPrepareOptionsPanelIfNeeded() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN
|
||||
&& mOriginalWindowCallback instanceof Activity) {
|
||||
// For Activities, we only need to bypass onPreparePanel if we're running pre-JB
|
||||
return true;
|
||||
} else if (mOriginalWindowCallback instanceof Dialog) {
|
||||
// For Dialogs, we always need to bypass onPreparePanel
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
85
android/support/v7/app/AppCompatDelegateImplV11.java
Normal file
85
android/support/v7/app/AppCompatDelegateImplV11.java
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.support.v7.app;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.support.v7.internal.view.SupportActionModeWrapper;
|
||||
import android.support.v7.internal.widget.NativeActionModeAwareLayout;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ActionMode;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
class AppCompatDelegateImplV11 extends AppCompatDelegateImplV7
|
||||
implements NativeActionModeAwareLayout.OnActionModeForChildListener {
|
||||
|
||||
private NativeActionModeAwareLayout mNativeActionModeAwareLayout;
|
||||
|
||||
AppCompatDelegateImplV11(Context context, Window window, AppCompatCallback callback) {
|
||||
super(context, window, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onSubDecorInstalled(ViewGroup subDecor) {
|
||||
// NativeActionModeAwareLayout is used to notify us when a native Action Mode is started
|
||||
mNativeActionModeAwareLayout = (NativeActionModeAwareLayout)
|
||||
subDecor.findViewById(android.R.id.content);
|
||||
|
||||
// Can be null when using FEATURE_ACTION_BAR_OVERLAY
|
||||
if (mNativeActionModeAwareLayout != null) {
|
||||
mNativeActionModeAwareLayout.setActionModeForChildListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
// From NativeActionModeAwareLayout.OnActionModeForChildListener
|
||||
@Override
|
||||
public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
|
||||
Context context = originalView.getContext();
|
||||
|
||||
// Try and start a support action mode, wrapping the callback
|
||||
final android.support.v7.view.ActionMode supportActionMode = startSupportActionMode(
|
||||
new SupportActionModeWrapper.CallbackWrapper(context, callback));
|
||||
|
||||
if (supportActionMode != null) {
|
||||
// If we received a support action mode, wrap and return it
|
||||
return new SupportActionModeWrapper(mContext, supportActionMode);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) {
|
||||
// First let super have a try, this allows FragmentActivity to inflate any support
|
||||
// fragments
|
||||
final View view = super.callActivityOnCreateView(parent, name, context, attrs);
|
||||
if (view != null) {
|
||||
return view;
|
||||
}
|
||||
|
||||
// Now, let the Activity's LayoutInflater.Factory2 method try...
|
||||
if (mOriginalWindowCallback instanceof LayoutInflater.Factory2) {
|
||||
return ((LayoutInflater.Factory2) mOriginalWindowCallback)
|
||||
.onCreateView(parent, name, context, attrs);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
1808
android/support/v7/app/AppCompatDelegateImplV7.java
Normal file
1808
android/support/v7/app/AppCompatDelegateImplV7.java
Normal file
File diff suppressed because it is too large
Load diff
163
android/support/v7/app/AppCompatDialog.java
Normal file
163
android/support/v7/app/AppCompatDialog.java
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.support.v7.app;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* Base class for AppCompat themed {@link android.app.Dialog}s.
|
||||
*/
|
||||
public class AppCompatDialog extends Dialog implements AppCompatCallback {
|
||||
|
||||
private AppCompatDelegate mDelegate;
|
||||
|
||||
public AppCompatDialog(Context context) {
|
||||
this(context, 0);
|
||||
}
|
||||
|
||||
public AppCompatDialog(Context context, int theme) {
|
||||
super(context, getThemeResId(context, theme));
|
||||
|
||||
// This is a bit weird, but Dialog's are typically created and setup before being shown,
|
||||
// which means that we can't rely on onCreate() being called before a content view is set.
|
||||
// To workaround this, we call onCreate(null) in the ctor, and then again as usual in
|
||||
// onCreate().
|
||||
getDelegate().onCreate(null);
|
||||
}
|
||||
|
||||
protected AppCompatDialog(Context context, boolean cancelable,
|
||||
OnCancelListener cancelListener) {
|
||||
super(context, cancelable, cancelListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
getDelegate().installViewFactory();
|
||||
super.onCreate(savedInstanceState);
|
||||
getDelegate().onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Support library version of {@link android.app.Dialog#getActionBar}.
|
||||
*
|
||||
* <p>Retrieve a reference to this dialog's ActionBar.
|
||||
*
|
||||
* @return The Dialog's ActionBar, or null if it does not have one.
|
||||
*/
|
||||
public ActionBar getSupportActionBar() {
|
||||
return getDelegate().getSupportActionBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(@LayoutRes int layoutResID) {
|
||||
getDelegate().setContentView(layoutResID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view) {
|
||||
getDelegate().setContentView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view, ViewGroup.LayoutParams params) {
|
||||
getDelegate().setContentView(view, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(CharSequence title) {
|
||||
super.setTitle(title);
|
||||
getDelegate().setTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(int titleId) {
|
||||
super.setTitle(titleId);
|
||||
getDelegate().setTitle(getContext().getString(titleId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addContentView(View view, ViewGroup.LayoutParams params) {
|
||||
getDelegate().addContentView(view, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getDelegate().onStop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable extended support library window features.
|
||||
* <p>
|
||||
* This is a convenience for calling
|
||||
* {@link android.view.Window#requestFeature getWindow().requestFeature()}.
|
||||
* </p>
|
||||
*
|
||||
* @param featureId The desired feature as defined in {@link android.view.Window} or
|
||||
* {@link android.support.v4.view.WindowCompat}.
|
||||
* @return Returns true if the requested feature is supported and now enabled.
|
||||
*
|
||||
* @see android.app.Dialog#requestWindowFeature
|
||||
* @see android.view.Window#requestFeature
|
||||
*/
|
||||
public boolean supportRequestWindowFeature(int featureId) {
|
||||
return getDelegate().requestWindowFeature(featureId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void invalidateOptionsMenu() {
|
||||
getDelegate().invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The {@link AppCompatDelegate} being used by this Dialog.
|
||||
*/
|
||||
public AppCompatDelegate getDelegate() {
|
||||
if (mDelegate == null) {
|
||||
mDelegate = AppCompatDelegate.create(this, this);
|
||||
}
|
||||
return mDelegate;
|
||||
}
|
||||
|
||||
private static int getThemeResId(Context context, int themeId) {
|
||||
if (themeId == 0) {
|
||||
// If the provided theme is 0, then retrieve the dialogTheme from our theme
|
||||
TypedValue outValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
|
||||
themeId = outValue.resourceId;
|
||||
}
|
||||
return themeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSupportActionModeStarted(ActionMode mode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSupportActionModeFinished(ActionMode mode) {
|
||||
}
|
||||
}
|
207
android/support/v7/app/DrawerArrowDrawable.java
Normal file
207
android/support/v7/app/DrawerArrowDrawable.java
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* 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.support.v7.app;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v7.appcompat.R;
|
||||
|
||||
/**
|
||||
* A drawable that can draw a "Drawer hamburger" menu or an Arrow and animate between them.
|
||||
*/
|
||||
abstract class DrawerArrowDrawable extends Drawable {
|
||||
|
||||
private final Paint mPaint = new Paint();
|
||||
|
||||
// The angle in degress that the arrow head is inclined at.
|
||||
private static final float ARROW_HEAD_ANGLE = (float) Math.toRadians(45);
|
||||
private final float mBarThickness;
|
||||
// The length of top and bottom bars when they merge into an arrow
|
||||
private final float mTopBottomArrowSize;
|
||||
// The length of middle bar
|
||||
private final float mBarSize;
|
||||
// The length of the middle bar when arrow is shaped
|
||||
private final float mMiddleArrowSize;
|
||||
// The space between bars when they are parallel
|
||||
private final float mBarGap;
|
||||
// Whether bars should spin or not during progress
|
||||
private final boolean mSpin;
|
||||
// Use Path instead of canvas operations so that if color has transparency, overlapping sections
|
||||
// wont look different
|
||||
private final Path mPath = new Path();
|
||||
// The reported intrinsic size of the drawable.
|
||||
private final int mSize;
|
||||
// Whether we should mirror animation when animation is reversed.
|
||||
private boolean mVerticalMirror = false;
|
||||
// The interpolated version of the original progress
|
||||
private float mProgress;
|
||||
// the amount that overlaps w/ bar size when rotation is max
|
||||
private float mMaxCutForBarSize;
|
||||
// The distance of arrow's center from top when horizontal
|
||||
private float mCenterOffset;
|
||||
|
||||
/**
|
||||
* @param context used to get the configuration for the drawable from
|
||||
*/
|
||||
DrawerArrowDrawable(Context context) {
|
||||
final TypedArray typedArray = context.getTheme()
|
||||
.obtainStyledAttributes(null, R.styleable.DrawerArrowToggle,
|
||||
R.attr.drawerArrowStyle,
|
||||
R.style.Base_Widget_AppCompat_DrawerArrowToggle);
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setColor(typedArray.getColor(R.styleable.DrawerArrowToggle_color, 0));
|
||||
mSize = typedArray.getDimensionPixelSize(R.styleable.DrawerArrowToggle_drawableSize, 0);
|
||||
// round this because having this floating may cause bad measurements
|
||||
mBarSize = Math.round(typedArray.getDimension(R.styleable.DrawerArrowToggle_barSize, 0));
|
||||
// round this because having this floating may cause bad measurements
|
||||
mTopBottomArrowSize = Math.round(typedArray.getDimension(
|
||||
R.styleable.DrawerArrowToggle_topBottomBarArrowSize, 0));
|
||||
mBarThickness = typedArray.getDimension(R.styleable.DrawerArrowToggle_thickness, 0);
|
||||
// round this because having this floating may cause bad measurements
|
||||
mBarGap = Math.round(typedArray.getDimension(
|
||||
R.styleable.DrawerArrowToggle_gapBetweenBars, 0));
|
||||
mSpin = typedArray.getBoolean(R.styleable.DrawerArrowToggle_spinBars, true);
|
||||
mMiddleArrowSize = typedArray
|
||||
.getDimension(R.styleable.DrawerArrowToggle_middleBarArrowSize, 0);
|
||||
final int remainingSpace = (int) (mSize - mBarThickness * 3 - mBarGap * 2);
|
||||
mCenterOffset = (remainingSpace / 4) * 2; //making sure it is a multiple of 2.
|
||||
mCenterOffset += mBarThickness * 1.5 + mBarGap;
|
||||
typedArray.recycle();
|
||||
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setStrokeJoin(Paint.Join.MITER);
|
||||
mPaint.setStrokeCap(Paint.Cap.BUTT);
|
||||
mPaint.setStrokeWidth(mBarThickness);
|
||||
|
||||
mMaxCutForBarSize = (float) (mBarThickness / 2 * Math.cos(ARROW_HEAD_ANGLE));
|
||||
}
|
||||
|
||||
abstract boolean isLayoutRtl();
|
||||
|
||||
/**
|
||||
* If set, canvas is flipped when progress reached to end and going back to start.
|
||||
*/
|
||||
protected void setVerticalMirror(boolean verticalMirror) {
|
||||
mVerticalMirror = verticalMirror;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
Rect bounds = getBounds();
|
||||
final boolean isRtl = isLayoutRtl();
|
||||
// Interpolated widths of arrow bars
|
||||
final float arrowSize = lerp(mBarSize, mTopBottomArrowSize, mProgress);
|
||||
final float middleBarSize = lerp(mBarSize, mMiddleArrowSize, mProgress);
|
||||
// Interpolated size of middle bar
|
||||
final float middleBarCut = Math.round(lerp(0, mMaxCutForBarSize, mProgress));
|
||||
// The rotation of the top and bottom bars (that make the arrow head)
|
||||
final float rotation = lerp(0, ARROW_HEAD_ANGLE, mProgress);
|
||||
|
||||
// The whole canvas rotates as the transition happens
|
||||
final float canvasRotate = lerp(isRtl ? 0 : -180, isRtl ? 180 : 0, mProgress);
|
||||
final float arrowWidth = Math.round(arrowSize * Math.cos(rotation));
|
||||
final float arrowHeight = Math.round(arrowSize * Math.sin(rotation));
|
||||
|
||||
|
||||
mPath.rewind();
|
||||
final float topBottomBarOffset = lerp(mBarGap + mBarThickness, -mMaxCutForBarSize,
|
||||
mProgress);
|
||||
|
||||
final float arrowEdge = -middleBarSize / 2;
|
||||
// draw middle bar
|
||||
mPath.moveTo(arrowEdge + middleBarCut, 0);
|
||||
mPath.rLineTo(middleBarSize - middleBarCut * 2, 0);
|
||||
|
||||
// bottom bar
|
||||
mPath.moveTo(arrowEdge, topBottomBarOffset);
|
||||
mPath.rLineTo(arrowWidth, arrowHeight);
|
||||
|
||||
// top bar
|
||||
mPath.moveTo(arrowEdge, -topBottomBarOffset);
|
||||
mPath.rLineTo(arrowWidth, -arrowHeight);
|
||||
|
||||
mPath.close();
|
||||
|
||||
canvas.save();
|
||||
// Rotate the whole canvas if spinning, if not, rotate it 180 to get
|
||||
// the arrow pointing the other way for RTL.
|
||||
canvas.translate(bounds.centerX(), mCenterOffset);
|
||||
if (mSpin) {
|
||||
canvas.rotate(canvasRotate * ((mVerticalMirror ^ isRtl) ? -1 : 1));
|
||||
} else if (isRtl) {
|
||||
canvas.rotate(180);
|
||||
}
|
||||
canvas.drawPath(mPath, mPaint);
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int i) {
|
||||
mPaint.setAlpha(i);
|
||||
}
|
||||
|
||||
// override
|
||||
public boolean isAutoMirrored() {
|
||||
// Draws rotated 180 degrees in RTL mode.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter colorFilter) {
|
||||
mPaint.setColorFilter(colorFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicHeight() {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth() {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.TRANSLUCENT;
|
||||
}
|
||||
|
||||
public float getProgress() {
|
||||
return mProgress;
|
||||
}
|
||||
|
||||
public void setProgress(float progress) {
|
||||
mProgress = progress;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Linear interpolate between a and b with parameter t.
|
||||
*/
|
||||
private static float lerp(float a, float b, float t) {
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
}
|
225
android/support/v7/graphics/drawable/DrawableWrapper.java
Normal file
225
android/support/v7/graphics/drawable/DrawableWrapper.java
Normal file
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* 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.support.v7.graphics.drawable;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Drawable which delegates all calls to it's wrapped {@link Drawable}.
|
||||
* <p>
|
||||
* The wrapped {@link Drawable} <em>must</em> be fully released from any {@link View}
|
||||
* before wrapping, otherwise internal {@link Drawable.Callback} may be dropped.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class DrawableWrapper extends Drawable implements Drawable.Callback {
|
||||
|
||||
private Drawable mDrawable;
|
||||
|
||||
public DrawableWrapper(Drawable drawable) {
|
||||
setWrappedDrawable(drawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
mDrawable.draw(canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBoundsChange(Rect bounds) {
|
||||
mDrawable.setBounds(bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChangingConfigurations(int configs) {
|
||||
mDrawable.setChangingConfigurations(configs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChangingConfigurations() {
|
||||
return mDrawable.getChangingConfigurations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDither(boolean dither) {
|
||||
mDrawable.setDither(dither);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilterBitmap(boolean filter) {
|
||||
mDrawable.setFilterBitmap(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
mDrawable.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
mDrawable.setColorFilter(cf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStateful() {
|
||||
return mDrawable.isStateful();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setState(final int[] stateSet) {
|
||||
return mDrawable.setState(stateSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getState() {
|
||||
return mDrawable.getState();
|
||||
}
|
||||
|
||||
public void jumpToCurrentState() {
|
||||
DrawableCompat.jumpToCurrentState(mDrawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getCurrent() {
|
||||
return mDrawable.getCurrent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setVisible(boolean visible, boolean restart) {
|
||||
return super.setVisible(visible, restart) || mDrawable.setVisible(visible, restart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return mDrawable.getOpacity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Region getTransparentRegion() {
|
||||
return mDrawable.getTransparentRegion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth() {
|
||||
return mDrawable.getIntrinsicWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicHeight() {
|
||||
return mDrawable.getIntrinsicHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinimumWidth() {
|
||||
return mDrawable.getMinimumWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinimumHeight() {
|
||||
return mDrawable.getMinimumHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPadding(Rect padding) {
|
||||
return mDrawable.getPadding(padding);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void invalidateDrawable(Drawable who) {
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void scheduleDrawable(Drawable who, Runnable what, long when) {
|
||||
scheduleSelf(what, when);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void unscheduleDrawable(Drawable who, Runnable what) {
|
||||
unscheduleSelf(what);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onLevelChange(int level) {
|
||||
return mDrawable.setLevel(level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoMirrored(boolean mirrored) {
|
||||
DrawableCompat.setAutoMirrored(mDrawable, mirrored);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoMirrored() {
|
||||
return DrawableCompat.isAutoMirrored(mDrawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTint(int tint) {
|
||||
DrawableCompat.setTint(mDrawable, tint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTintList(ColorStateList tint) {
|
||||
DrawableCompat.setTintList(mDrawable, tint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTintMode(PorterDuff.Mode tintMode) {
|
||||
DrawableCompat.setTintMode(mDrawable, tintMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHotspot(float x, float y) {
|
||||
DrawableCompat.setHotspot(mDrawable, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHotspotBounds(int left, int top, int right, int bottom) {
|
||||
DrawableCompat.setHotspotBounds(mDrawable, left, top, right, bottom);
|
||||
}
|
||||
|
||||
public Drawable getWrappedDrawable() {
|
||||
return mDrawable;
|
||||
}
|
||||
|
||||
public void setWrappedDrawable(Drawable drawable) {
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setCallback(null);
|
||||
}
|
||||
|
||||
mDrawable = drawable;
|
||||
|
||||
if (drawable != null) {
|
||||
drawable.setCallback(this);
|
||||
}
|
||||
}
|
||||
}
|
16
android/support/v7/internal/VersionUtils.java
Normal file
16
android/support/v7/internal/VersionUtils.java
Normal file
|
@ -0,0 +1,16 @@
|
|||
package android.support.v7.internal;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class VersionUtils {
|
||||
|
||||
private VersionUtils() {}
|
||||
|
||||
public static boolean isAtLeastL() {
|
||||
return Build.VERSION.SDK_INT >= 21;
|
||||
}
|
||||
|
||||
}
|
48
android/support/v7/internal/app/NavItemSelectedListener.java
Normal file
48
android/support/v7/internal/app/NavItemSelectedListener.java
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.support.v7.internal.app;
|
||||
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.internal.widget.AdapterViewCompat;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Wrapper to adapt the ActionBar.OnNavigationListener in an AdapterView.OnItemSelectedListener
|
||||
* for use in Spinner widgets. Used by action bar implementations.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
class NavItemSelectedListener implements AdapterViewCompat.OnItemSelectedListener {
|
||||
private final ActionBar.OnNavigationListener mListener;
|
||||
|
||||
public NavItemSelectedListener(ActionBar.OnNavigationListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemSelected(AdapterViewCompat<?> parent, View view, int position, long id) {
|
||||
if (mListener != null) {
|
||||
mListener.onNavigationItemSelected(position, id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterViewCompat<?> parent) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
156
android/support/v7/internal/app/TintViewInflater.java
Normal file
156
android/support/v7/internal/app/TintViewInflater.java
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* 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.support.v7.internal.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.internal.widget.TintAutoCompleteTextView;
|
||||
import android.support.v7.internal.widget.TintButton;
|
||||
import android.support.v7.internal.widget.TintCheckBox;
|
||||
import android.support.v7.internal.widget.TintCheckedTextView;
|
||||
import android.support.v7.internal.widget.TintEditText;
|
||||
import android.support.v7.internal.widget.TintMultiAutoCompleteTextView;
|
||||
import android.support.v7.internal.widget.TintRadioButton;
|
||||
import android.support.v7.internal.widget.TintRatingBar;
|
||||
import android.support.v7.internal.widget.TintSpinner;
|
||||
import android.support.v7.internal.widget.ViewUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.InflateException;
|
||||
import android.view.View;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class is responsible for manually inflating our tinted widgets which are used on devices
|
||||
* running {@link android.os.Build.VERSION_CODES#KITKAT KITKAT} or below. As such, this class
|
||||
* should only be used when running on those devices.
|
||||
* <p>This class two main responsibilities: the first is to 'inject' our tinted views in place of
|
||||
* the framework versions in layout inflation; the second is backport the {@code android:theme}
|
||||
* functionality for any inflated widgets. This include theme inheritance from it's parent.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class TintViewInflater {
|
||||
|
||||
static final Class<?>[] sConstructorSignature = new Class[] {
|
||||
Context.class, AttributeSet.class};
|
||||
|
||||
private static final Map<String, Constructor<? extends View>> sConstructorMap = new HashMap<>();
|
||||
|
||||
private final Context mContext;
|
||||
private final Object[] mConstructorArgs = new Object[2];
|
||||
|
||||
public TintViewInflater(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public final View createView(View parent, final String name, @NonNull Context context,
|
||||
@NonNull AttributeSet attrs, boolean inheritContext, boolean themeContext) {
|
||||
final Context originalContext = context;
|
||||
|
||||
// We can emulate Lollipop's android:theme attribute propagating down the view hierarchy
|
||||
// by using the parent's context
|
||||
if (inheritContext && parent != null) {
|
||||
context = parent.getContext();
|
||||
}
|
||||
if (themeContext) {
|
||||
// We then apply the theme on the context, if specified
|
||||
context = ViewUtils.themifyContext(context, attrs, true, true);
|
||||
}
|
||||
|
||||
// We need to 'inject' our tint aware Views in place of the standard framework versions
|
||||
switch (name) {
|
||||
case "EditText":
|
||||
return new TintEditText(context, attrs);
|
||||
case "Spinner":
|
||||
return new TintSpinner(context, attrs);
|
||||
case "CheckBox":
|
||||
return new TintCheckBox(context, attrs);
|
||||
case "RadioButton":
|
||||
return new TintRadioButton(context, attrs);
|
||||
case "CheckedTextView":
|
||||
return new TintCheckedTextView(context, attrs);
|
||||
case "AutoCompleteTextView":
|
||||
return new TintAutoCompleteTextView(context, attrs);
|
||||
case "MultiAutoCompleteTextView":
|
||||
return new TintMultiAutoCompleteTextView(context, attrs);
|
||||
case "RatingBar":
|
||||
return new TintRatingBar(context, attrs);
|
||||
case "Button":
|
||||
return new TintButton(context, attrs);
|
||||
}
|
||||
|
||||
if (originalContext != context) {
|
||||
// If the original context does not equal our themed context, then we need to manually
|
||||
// inflate it using the name so that app:theme takes effect.
|
||||
return createViewFromTag(context, name, attrs);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private View createViewFromTag(Context context, String name, AttributeSet attrs) {
|
||||
if (name.equals("view")) {
|
||||
name = attrs.getAttributeValue(null, "class");
|
||||
}
|
||||
|
||||
try {
|
||||
mConstructorArgs[0] = context;
|
||||
mConstructorArgs[1] = attrs;
|
||||
|
||||
if (-1 == name.indexOf('.')) {
|
||||
// try the android.widget prefix first...
|
||||
return createView(name, "android.widget.");
|
||||
} else {
|
||||
return createView(name, null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// We do not want to catch these, lets return null and let the actual LayoutInflater
|
||||
// try
|
||||
return null;
|
||||
} finally {
|
||||
// Don't retain static reference on context.
|
||||
mConstructorArgs[0] = null;
|
||||
mConstructorArgs[1] = null;
|
||||
}
|
||||
}
|
||||
|
||||
private View createView(String name, String prefix)
|
||||
throws ClassNotFoundException, InflateException {
|
||||
Constructor<? extends View> constructor = sConstructorMap.get(name);
|
||||
|
||||
try {
|
||||
if (constructor == null) {
|
||||
// Class not found in the cache, see if it's real, and try to add it
|
||||
Class<? extends View> clazz = mContext.getClassLoader().loadClass(
|
||||
prefix != null ? (prefix + name) : name).asSubclass(View.class);
|
||||
|
||||
constructor = clazz.getConstructor(sConstructorSignature);
|
||||
sConstructorMap.put(name, constructor);
|
||||
}
|
||||
constructor.setAccessible(true);
|
||||
return constructor.newInstance(mConstructorArgs);
|
||||
} catch (Exception e) {
|
||||
// We do not want to catch these, lets return null and let the actual LayoutInflater
|
||||
// try
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
635
android/support/v7/internal/app/ToolbarActionBar.java
Normal file
635
android/support/v7/internal/app/ToolbarActionBar.java
Normal file
|
@ -0,0 +1,635 @@
|
|||
/*
|
||||
* 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.support.v7.internal.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.view.WindowCompat;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.internal.view.WindowCallbackWrapper;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.internal.view.menu.ListMenuPresenter;
|
||||
import android.support.v7.internal.view.menu.MenuBuilder;
|
||||
import android.support.v7.internal.view.menu.MenuPresenter;
|
||||
import android.support.v7.internal.widget.DecorToolbar;
|
||||
import android.support.v7.internal.widget.ToolbarWidgetWrapper;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.TypedValue;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.SpinnerAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class ToolbarActionBar extends ActionBar {
|
||||
private DecorToolbar mDecorToolbar;
|
||||
private boolean mToolbarMenuPrepared;
|
||||
private Window.Callback mWindowCallback;
|
||||
private boolean mMenuCallbackSet;
|
||||
|
||||
private boolean mLastMenuVisibility;
|
||||
private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners =
|
||||
new ArrayList<OnMenuVisibilityListener>();
|
||||
|
||||
private Window mWindow;
|
||||
private ListMenuPresenter mListMenuPresenter;
|
||||
|
||||
private final Runnable mMenuInvalidator = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
populateOptionsMenu();
|
||||
}
|
||||
};
|
||||
|
||||
private final Toolbar.OnMenuItemClickListener mMenuClicker =
|
||||
new Toolbar.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
return mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);
|
||||
}
|
||||
};
|
||||
|
||||
public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window window) {
|
||||
mDecorToolbar = new ToolbarWidgetWrapper(toolbar, false);
|
||||
mWindowCallback = new ToolbarCallbackWrapper(window.getCallback());
|
||||
mDecorToolbar.setWindowCallback(mWindowCallback);
|
||||
toolbar.setOnMenuItemClickListener(mMenuClicker);
|
||||
mDecorToolbar.setWindowTitle(title);
|
||||
|
||||
mWindow = window;
|
||||
}
|
||||
|
||||
public Window.Callback getWrappedWindowCallback() {
|
||||
return mWindowCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomView(View view) {
|
||||
setCustomView(view, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomView(View view, LayoutParams layoutParams) {
|
||||
view.setLayoutParams(layoutParams);
|
||||
mDecorToolbar.setCustomView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomView(int resId) {
|
||||
final LayoutInflater inflater = LayoutInflater.from(mDecorToolbar.getContext());
|
||||
setCustomView(inflater.inflate(resId, mDecorToolbar.getViewGroup(), false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIcon(int resId) {
|
||||
mDecorToolbar.setIcon(resId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIcon(Drawable icon) {
|
||||
mDecorToolbar.setIcon(icon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogo(int resId) {
|
||||
mDecorToolbar.setLogo(resId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogo(Drawable logo) {
|
||||
mDecorToolbar.setLogo(logo);
|
||||
}
|
||||
|
||||
@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 void setElevation(float elevation) {
|
||||
ViewCompat.setElevation(mDecorToolbar.getViewGroup(), elevation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getElevation() {
|
||||
return ViewCompat.getElevation(mDecorToolbar.getViewGroup());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getThemedContext() {
|
||||
return mDecorToolbar.getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTitleTruncated() {
|
||||
return super.isTitleTruncated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHomeAsUpIndicator(Drawable indicator) {
|
||||
mDecorToolbar.setNavigationIcon(indicator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHomeAsUpIndicator(int resId) {
|
||||
mDecorToolbar.setNavigationIcon(resId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHomeActionContentDescription(CharSequence description) {
|
||||
mDecorToolbar.setNavigationContentDescription(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultDisplayHomeAsUpEnabled(boolean enabled) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHomeActionContentDescription(int resId) {
|
||||
mDecorToolbar.setNavigationContentDescription(resId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShowHideAnimationEnabled(boolean enabled) {
|
||||
// This space for rent; no-op.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration config) {
|
||||
super.onConfigurationChanged(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
|
||||
mDecorToolbar.setDropdownParams(adapter, new NavItemSelectedListener(callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedNavigationItem(int position) {
|
||||
switch (mDecorToolbar.getNavigationMode()) {
|
||||
case NAVIGATION_MODE_LIST:
|
||||
mDecorToolbar.setDropdownSelectedPosition(position);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException(
|
||||
"setSelectedNavigationIndex not valid for current navigation mode");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSelectedNavigationIndex() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNavigationItemCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(CharSequence title) {
|
||||
mDecorToolbar.setTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(int resId) {
|
||||
mDecorToolbar.setTitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindowTitle(CharSequence title) {
|
||||
mDecorToolbar.setWindowTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubtitle(CharSequence subtitle) {
|
||||
mDecorToolbar.setSubtitle(subtitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubtitle(int resId) {
|
||||
mDecorToolbar.setSubtitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayOptions(@DisplayOptions int options) {
|
||||
setDisplayOptions(options, 0xffffffff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayOptions(@DisplayOptions int options, @DisplayOptions int mask) {
|
||||
final int currentOptions = mDecorToolbar.getDisplayOptions();
|
||||
mDecorToolbar.setDisplayOptions(options & mask | currentOptions & ~mask);
|
||||
}
|
||||
|
||||
@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) {
|
||||
mDecorToolbar.setBackgroundDrawable(d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getCustomView() {
|
||||
return mDecorToolbar.getCustomView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
return mDecorToolbar.getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSubtitle() {
|
||||
return mDecorToolbar.getSubtitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNavigationMode() {
|
||||
return NAVIGATION_MODE_STANDARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationMode(@NavigationMode int mode) {
|
||||
if (mode == ActionBar.NAVIGATION_MODE_TABS) {
|
||||
throw new IllegalArgumentException("Tabs not supported in this configuration");
|
||||
}
|
||||
mDecorToolbar.setNavigationMode(mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayOptions() {
|
||||
return mDecorToolbar.getDisplayOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tab newTab() {
|
||||
throw new UnsupportedOperationException(
|
||||
"Tabs are not supported in toolbar action bars");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTab(Tab tab) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Tabs are not supported in toolbar action bars");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTab(Tab tab, boolean setSelected) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Tabs are not supported in toolbar action bars");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTab(Tab tab, int position) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Tabs are not supported in toolbar action bars");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTab(Tab tab, int position, boolean setSelected) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Tabs are not supported in toolbar action bars");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTab(Tab tab) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Tabs are not supported in toolbar action bars");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTabAt(int position) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Tabs are not supported in toolbar action bars");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllTabs() {
|
||||
throw new UnsupportedOperationException(
|
||||
"Tabs are not supported in toolbar action bars");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectTab(Tab tab) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Tabs are not supported in toolbar action bars");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tab getSelectedTab() {
|
||||
throw new UnsupportedOperationException(
|
||||
"Tabs are not supported in toolbar action bars");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tab getTabAt(int index) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Tabs are not supported in toolbar action bars");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTabCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return mDecorToolbar.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.
|
||||
mDecorToolbar.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.
|
||||
mDecorToolbar.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowing() {
|
||||
return mDecorToolbar.getVisibility() == View.VISIBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean openOptionsMenu() {
|
||||
return mDecorToolbar.showOverflowMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean invalidateOptionsMenu() {
|
||||
mDecorToolbar.getViewGroup().removeCallbacks(mMenuInvalidator);
|
||||
ViewCompat.postOnAnimation(mDecorToolbar.getViewGroup(), mMenuInvalidator);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collapseActionView() {
|
||||
if (mDecorToolbar.hasExpandedActionView()) {
|
||||
mDecorToolbar.collapseActionView();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void populateOptionsMenu() {
|
||||
final Menu menu = getMenu();
|
||||
final MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder) menu : null;
|
||||
if (mb != null) {
|
||||
mb.stopDispatchingItemsChanged();
|
||||
}
|
||||
try {
|
||||
menu.clear();
|
||||
if (!mWindowCallback.onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu) ||
|
||||
!mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu)) {
|
||||
menu.clear();
|
||||
}
|
||||
} finally {
|
||||
if (mb != null) {
|
||||
mb.startDispatchingItemsChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuKeyEvent(KeyEvent event) {
|
||||
if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
openOptionsMenu();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyShortcut(int keyCode, KeyEvent ev) {
|
||||
Menu menu = getMenu();
|
||||
return menu != null ? menu.performShortcut(keyCode, ev, 0) : false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private View getListMenuView(Menu menu) {
|
||||
ensureListMenuPresenter(menu);
|
||||
|
||||
if (menu == null || mListMenuPresenter == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mListMenuPresenter.getAdapter().getCount() > 0) {
|
||||
return (View) mListMenuPresenter.getMenuView(mDecorToolbar.getViewGroup());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ensureListMenuPresenter(Menu menu) {
|
||||
if (mListMenuPresenter == null && (menu instanceof MenuBuilder)) {
|
||||
MenuBuilder mb = (MenuBuilder) menu;
|
||||
|
||||
Context context = mDecorToolbar.getContext();
|
||||
final TypedValue outValue = new TypedValue();
|
||||
final Resources.Theme widgetTheme = context.getResources().newTheme();
|
||||
widgetTheme.setTo(context.getTheme());
|
||||
|
||||
// Apply the panelMenuListTheme
|
||||
widgetTheme.resolveAttribute(R.attr.panelMenuListTheme, outValue, true);
|
||||
if (outValue.resourceId != 0) {
|
||||
widgetTheme.applyStyle(outValue.resourceId, true);
|
||||
} else {
|
||||
widgetTheme.applyStyle(R.style.Theme_AppCompat_CompactMenu, true);
|
||||
}
|
||||
|
||||
context = new ContextThemeWrapper(context, 0);
|
||||
context.getTheme().setTo(widgetTheme);
|
||||
|
||||
// Finally create the list menu presenter
|
||||
mListMenuPresenter = new ListMenuPresenter(context, R.layout.abc_list_menu_item_layout);
|
||||
mListMenuPresenter.setCallback(new PanelMenuPresenterCallback());
|
||||
mb.addMenuPresenter(mListMenuPresenter);
|
||||
}
|
||||
}
|
||||
|
||||
private class ToolbarCallbackWrapper extends WindowCallbackWrapper {
|
||||
public ToolbarCallbackWrapper(Window.Callback wrapped) {
|
||||
super(wrapped);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreparePanel(int featureId, View view, Menu menu) {
|
||||
final boolean result = super.onPreparePanel(featureId, view, menu);
|
||||
if (result && !mToolbarMenuPrepared) {
|
||||
mDecorToolbar.setMenuPrepared();
|
||||
mToolbarMenuPrepared = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreatePanelView(int featureId) {
|
||||
switch (featureId) {
|
||||
case Window.FEATURE_OPTIONS_PANEL:
|
||||
final Menu menu = mDecorToolbar.getMenu();
|
||||
if (onPreparePanel(featureId, null, menu) && onMenuOpened(featureId, menu)) {
|
||||
return getListMenuView(menu);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return super.onCreatePanelView(featureId);
|
||||
}
|
||||
}
|
||||
|
||||
private Menu getMenu() {
|
||||
if (!mMenuCallbackSet) {
|
||||
mDecorToolbar.setMenuCallbacks(new ActionMenuPresenterCallback(),
|
||||
new MenuBuilderCallback());
|
||||
mMenuCallbackSet = true;
|
||||
}
|
||||
return mDecorToolbar.getMenu();
|
||||
}
|
||||
|
||||
private final class ActionMenuPresenterCallback implements MenuPresenter.Callback {
|
||||
private boolean mClosingActionMenu;
|
||||
|
||||
@Override
|
||||
public boolean onOpenSubMenu(MenuBuilder subMenu) {
|
||||
if (mWindowCallback != null) {
|
||||
mWindowCallback.onMenuOpened(WindowCompat.FEATURE_ACTION_BAR, subMenu);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
||||
if (mClosingActionMenu) {
|
||||
return;
|
||||
}
|
||||
|
||||
mClosingActionMenu = true;
|
||||
mDecorToolbar.dismissPopupMenus();
|
||||
if (mWindowCallback != null) {
|
||||
mWindowCallback.onPanelClosed(WindowCompat.FEATURE_ACTION_BAR, menu);
|
||||
}
|
||||
mClosingActionMenu = false;
|
||||
}
|
||||
}
|
||||
|
||||
private final class PanelMenuPresenterCallback implements MenuPresenter.Callback {
|
||||
@Override
|
||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
||||
if (mWindowCallback != null) {
|
||||
mWindowCallback.onPanelClosed(Window.FEATURE_OPTIONS_PANEL, menu);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOpenSubMenu(MenuBuilder subMenu) {
|
||||
if (subMenu == null && mWindowCallback != null) {
|
||||
mWindowCallback.onMenuOpened(Window.FEATURE_OPTIONS_PANEL, subMenu);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private final class MenuBuilderCallback implements MenuBuilder.Callback {
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMenuModeChange(MenuBuilder menu) {
|
||||
if (mWindowCallback != null) {
|
||||
if (mDecorToolbar.isOverflowMenuShowing()) {
|
||||
mWindowCallback.onPanelClosed(WindowCompat.FEATURE_ACTION_BAR, menu);
|
||||
} else if (mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL,
|
||||
null, menu)) {
|
||||
mWindowCallback.onMenuOpened(WindowCompat.FEATURE_ACTION_BAR, menu);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1369
android/support/v7/internal/app/WindowDecorActionBar.java
Normal file
1369
android/support/v7/internal/app/WindowDecorActionBar.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.support.v7.internal.text;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.text.method.TransformationMethod;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class AllCapsTransformationMethod implements TransformationMethod {
|
||||
private Locale mLocale;
|
||||
|
||||
public AllCapsTransformationMethod(Context context) {
|
||||
mLocale = context.getResources().getConfiguration().locale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTransformation(CharSequence source, View view) {
|
||||
return source != null ? source.toString().toUpperCase(mLocale) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFocusChanged(View view, CharSequence sourceText, boolean focused,
|
||||
int direction, Rect previouslyFocusedRect) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.support.v7.internal.transition;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class ActionBarTransition {
|
||||
|
||||
private static final boolean TRANSITIONS_ENABLED = false;
|
||||
|
||||
private static final int TRANSITION_DURATION = 120; // ms
|
||||
|
||||
// private static final Transition sTransition;
|
||||
//
|
||||
// static {
|
||||
// if (TRANSITIONS_ENABLED) {
|
||||
//// final ChangeText tc = new ChangeText();
|
||||
//// tc.setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN);
|
||||
// final TransitionSet inner = new TransitionSet();
|
||||
// inner.addTransition(new ChangeBounds());
|
||||
// final TransitionSet tg = new TransitionSet();
|
||||
// tg.addTransition(new Fade(Fade.OUT)).addTransition(inner).
|
||||
// addTransition(new Fade(Fade.IN));
|
||||
// tg.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
|
||||
// tg.setDuration(TRANSITION_DURATION);
|
||||
// sTransition = tg;
|
||||
// } else {
|
||||
// sTransition = null;
|
||||
// }
|
||||
// }
|
||||
|
||||
public static void beginDelayedTransition(ViewGroup sceneRoot) {
|
||||
// if (TRANSITIONS_ENABLED) {
|
||||
// TransitionManager.beginDelayedTransition(sceneRoot, sTransition);
|
||||
// }
|
||||
}
|
||||
}
|
97
android/support/v7/internal/view/ActionBarPolicy.java
Normal file
97
android/support/v7/internal/view/ActionBarPolicy.java
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (C) 2012 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.support.v7.internal.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Build;
|
||||
import android.support.v4.view.ViewConfigurationCompat;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
/**
|
||||
* Allows components to query for various configuration policy decisions about how the action bar
|
||||
* should lay out and behave on the current device.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ActionBarPolicy {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
public static ActionBarPolicy get(Context context) {
|
||||
return new ActionBarPolicy(context);
|
||||
}
|
||||
|
||||
private ActionBarPolicy(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public int getMaxActionButtons() {
|
||||
return mContext.getResources().getInteger(R.integer.abc_max_action_buttons);
|
||||
}
|
||||
|
||||
public boolean showsOverflowMenuButton() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
return true;
|
||||
} else {
|
||||
return !ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(mContext));
|
||||
}
|
||||
}
|
||||
|
||||
public int getEmbeddedMenuWidthLimit() {
|
||||
return mContext.getResources().getDisplayMetrics().widthPixels / 2;
|
||||
}
|
||||
|
||||
public boolean hasEmbeddedTabs() {
|
||||
final int targetSdk = mContext.getApplicationInfo().targetSdkVersion;
|
||||
if (targetSdk >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
return mContext.getResources().getBoolean(R.bool.abc_action_bar_embed_tabs);
|
||||
}
|
||||
|
||||
// The embedded tabs policy changed in Jellybean; give older apps the old policy
|
||||
// so they get what they expect.
|
||||
return mContext.getResources().getBoolean(R.bool.abc_action_bar_embed_tabs_pre_jb);
|
||||
}
|
||||
|
||||
public int getTabContainerHeight() {
|
||||
TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ActionBar,
|
||||
R.attr.actionBarStyle, 0);
|
||||
int height = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
|
||||
Resources r = mContext.getResources();
|
||||
if (!hasEmbeddedTabs()) {
|
||||
// Stacked tabs; limit the height
|
||||
height = Math.min(height,
|
||||
r.getDimensionPixelSize(R.dimen.abc_action_bar_stacked_max_height));
|
||||
}
|
||||
a.recycle();
|
||||
return height;
|
||||
}
|
||||
|
||||
public boolean enableHomeButtonByDefault() {
|
||||
// Older apps get the home button interaction enabled by default.
|
||||
// Newer apps need to enable it explicitly.
|
||||
return mContext.getApplicationInfo().targetSdkVersion <
|
||||
Build.VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
}
|
||||
|
||||
public int getStackedTabMaxWidth() {
|
||||
return mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.abc_action_bar_stacked_tab_max_width);
|
||||
}
|
||||
}
|
103
android/support/v7/internal/view/ContextThemeWrapper.java
Normal file
103
android/support/v7/internal/view/ContextThemeWrapper.java
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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.support.v7.internal.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.res.Resources;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
/**
|
||||
* A ContextWrapper that allows you to modify the theme from what is in the
|
||||
* wrapped context.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ContextThemeWrapper extends ContextWrapper {
|
||||
private int mThemeResource;
|
||||
private Resources.Theme mTheme;
|
||||
private LayoutInflater mInflater;
|
||||
|
||||
public ContextThemeWrapper(Context base, int themeres) {
|
||||
super(base);
|
||||
mThemeResource = themeres;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheme(int resid) {
|
||||
mThemeResource = resid;
|
||||
initializeTheme();
|
||||
}
|
||||
|
||||
public int getThemeResId() {
|
||||
return mThemeResource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources.Theme getTheme() {
|
||||
if (mTheme != null) {
|
||||
return mTheme;
|
||||
}
|
||||
|
||||
if (mThemeResource == 0) {
|
||||
mThemeResource = R.style.Theme_AppCompat_Light;
|
||||
}
|
||||
initializeTheme();
|
||||
|
||||
return mTheme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSystemService(String name) {
|
||||
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
|
||||
if (mInflater == null) {
|
||||
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
|
||||
}
|
||||
return mInflater;
|
||||
}
|
||||
return getBaseContext().getSystemService(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link #setTheme} and {@link #getTheme} to apply a theme
|
||||
* resource to the current Theme object. Can override to change the
|
||||
* default (simple) behavior. This method will not be called in multiple
|
||||
* threads simultaneously.
|
||||
*
|
||||
* @param theme The Theme object being modified.
|
||||
* @param resid The theme style resource being applied to <var>theme</var>.
|
||||
* @param first Set to true if this is the first time a style is being
|
||||
* applied to <var>theme</var>.
|
||||
*/
|
||||
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
|
||||
theme.applyStyle(resid, true);
|
||||
}
|
||||
|
||||
private void initializeTheme() {
|
||||
final boolean first = mTheme == null;
|
||||
if (first) {
|
||||
mTheme = getResources().newTheme();
|
||||
Resources.Theme theme = getBaseContext().getTheme();
|
||||
if (theme != null) {
|
||||
mTheme.setTo(theme);
|
||||
}
|
||||
}
|
||||
onApplyThemeResource(mTheme, mThemeResource, first);
|
||||
}
|
||||
}
|
||||
|
163
android/support/v7/internal/view/StandaloneActionMode.java
Normal file
163
android/support/v7/internal/view/StandaloneActionMode.java
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* 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.support.v7.internal.view;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.support.v7.internal.view.menu.MenuBuilder;
|
||||
import android.support.v7.internal.view.menu.MenuPopupHelper;
|
||||
import android.support.v7.internal.view.menu.SubMenuBuilder;
|
||||
import android.support.v7.internal.widget.ActionBarContextView;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class StandaloneActionMode extends ActionMode implements MenuBuilder.Callback {
|
||||
private Context mContext;
|
||||
private ActionBarContextView mContextView;
|
||||
private ActionMode.Callback mCallback;
|
||||
private WeakReference<View> mCustomView;
|
||||
private boolean mFinished;
|
||||
private boolean mFocusable;
|
||||
|
||||
private MenuBuilder mMenu;
|
||||
|
||||
public StandaloneActionMode(Context context, ActionBarContextView view,
|
||||
ActionMode.Callback callback, boolean isFocusable) {
|
||||
mContext = context;
|
||||
mContextView = view;
|
||||
mCallback = callback;
|
||||
|
||||
mMenu = new MenuBuilder(context).setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
mMenu.setCallback(this);
|
||||
mFocusable = isFocusable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(CharSequence title) {
|
||||
mContextView.setTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubtitle(CharSequence subtitle) {
|
||||
mContextView.setSubtitle(subtitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(int resId) {
|
||||
setTitle(mContext.getString(resId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubtitle(int resId) {
|
||||
setSubtitle(mContext.getString(resId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitleOptionalHint(boolean titleOptional) {
|
||||
super.setTitleOptionalHint(titleOptional);
|
||||
mContextView.setTitleOptional(titleOptional);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTitleOptional() {
|
||||
return mContextView.isTitleOptional();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomView(View view) {
|
||||
mContextView.setCustomView(view);
|
||||
mCustomView = view != null ? new WeakReference<View>(view) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
mCallback.onPrepareActionMode(this, mMenu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
if (mFinished) {
|
||||
return;
|
||||
}
|
||||
mFinished = true;
|
||||
|
||||
mContextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
|
||||
mCallback.onDestroyActionMode(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Menu getMenu() {
|
||||
return mMenu;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
return mContextView.getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSubtitle() {
|
||||
return mContextView.getSubtitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getCustomView() {
|
||||
return mCustomView != null ? mCustomView.get() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuInflater getMenuInflater() {
|
||||
return new MenuInflater(mContext);
|
||||
}
|
||||
|
||||
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
|
||||
return mCallback.onActionItemClicked(this, item);
|
||||
}
|
||||
|
||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
||||
}
|
||||
|
||||
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
|
||||
if (!subMenu.hasVisibleItems()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
new MenuPopupHelper(mContext, subMenu).show();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onCloseSubMenu(SubMenuBuilder menu) {
|
||||
}
|
||||
|
||||
public void onMenuModeChange(MenuBuilder menu) {
|
||||
invalidate();
|
||||
mContextView.showOverflowMenu();
|
||||
}
|
||||
|
||||
public boolean isUiFocusable() {
|
||||
return mFocusable;
|
||||
}
|
||||
}
|
199
android/support/v7/internal/view/SupportActionModeWrapper.java
Normal file
199
android/support/v7/internal/view/SupportActionModeWrapper.java
Normal file
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.support.v7.internal.view;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.support.v4.internal.view.SupportMenu;
|
||||
import android.support.v4.internal.view.SupportMenuItem;
|
||||
import android.support.v4.util.SimpleArrayMap;
|
||||
import android.support.v7.internal.view.menu.MenuWrapperFactory;
|
||||
import android.view.ActionMode;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Wraps a support {@link android.support.v7.view.ActionMode} as a framework
|
||||
* {@link android.view.ActionMode}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public class SupportActionModeWrapper extends ActionMode {
|
||||
|
||||
final Context mContext;
|
||||
final android.support.v7.view.ActionMode mWrappedObject;
|
||||
|
||||
public SupportActionModeWrapper(Context context,
|
||||
android.support.v7.view.ActionMode supportActionMode) {
|
||||
mContext = context;
|
||||
mWrappedObject = supportActionMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTag() {
|
||||
return mWrappedObject.getTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTag(Object tag) {
|
||||
mWrappedObject.setTag(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(CharSequence title) {
|
||||
mWrappedObject.setTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubtitle(CharSequence subtitle) {
|
||||
mWrappedObject.setSubtitle(subtitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
mWrappedObject.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
mWrappedObject.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Menu getMenu() {
|
||||
return MenuWrapperFactory.wrapSupportMenu(mContext, (SupportMenu) mWrappedObject.getMenu());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
return mWrappedObject.getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(int resId) {
|
||||
mWrappedObject.setTitle(resId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSubtitle() {
|
||||
return mWrappedObject.getSubtitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubtitle(int resId) {
|
||||
mWrappedObject.setSubtitle(resId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getCustomView() {
|
||||
return mWrappedObject.getCustomView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomView(View view) {
|
||||
mWrappedObject.setCustomView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuInflater getMenuInflater() {
|
||||
return mWrappedObject.getMenuInflater();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getTitleOptionalHint() {
|
||||
return mWrappedObject.getTitleOptionalHint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitleOptionalHint(boolean titleOptional) {
|
||||
mWrappedObject.setTitleOptionalHint(titleOptional);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTitleOptional() {
|
||||
return mWrappedObject.isTitleOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static class CallbackWrapper implements android.support.v7.view.ActionMode.Callback {
|
||||
final Callback mWrappedCallback;
|
||||
final Context mContext;
|
||||
|
||||
final SimpleArrayMap<android.support.v7.view.ActionMode, SupportActionModeWrapper>
|
||||
mActionModes;
|
||||
final SimpleArrayMap<Menu, Menu> mMenus;
|
||||
|
||||
public CallbackWrapper(Context context, Callback supportCallback) {
|
||||
mContext = context;
|
||||
mWrappedCallback = supportCallback;
|
||||
mActionModes = new SimpleArrayMap<>();
|
||||
mMenus = new SimpleArrayMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(android.support.v7.view.ActionMode mode, Menu menu) {
|
||||
return mWrappedCallback.onCreateActionMode(getActionModeWrapper(mode),
|
||||
getMenuWrapper(menu));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(android.support.v7.view.ActionMode mode, Menu menu) {
|
||||
return mWrappedCallback.onPrepareActionMode(getActionModeWrapper(mode),
|
||||
getMenuWrapper(menu));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(android.support.v7.view.ActionMode mode,
|
||||
android.view.MenuItem item) {
|
||||
return mWrappedCallback.onActionItemClicked(getActionModeWrapper(mode),
|
||||
MenuWrapperFactory.wrapSupportMenuItem(mContext, (SupportMenuItem) item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(android.support.v7.view.ActionMode mode) {
|
||||
mWrappedCallback.onDestroyActionMode(getActionModeWrapper(mode));
|
||||
}
|
||||
|
||||
private Menu getMenuWrapper(Menu menu) {
|
||||
Menu wrapper = mMenus.get(menu);
|
||||
if (wrapper == null) {
|
||||
wrapper = MenuWrapperFactory.wrapSupportMenu(mContext, (SupportMenu) menu);
|
||||
mMenus.put(menu, wrapper);
|
||||
}
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private ActionMode getActionModeWrapper(android.support.v7.view.ActionMode mode) {
|
||||
// First see if we already have a wrapper for this mode
|
||||
SupportActionModeWrapper wrapper = mActionModes.get(mode);
|
||||
if (wrapper != null) {
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
// If we reach here then we haven't seen this mode before. Create a new wrapper and
|
||||
// add it to our collection
|
||||
wrapper = new SupportActionModeWrapper(mContext, mode);
|
||||
mActionModes.put(mode, wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
}
|
506
android/support/v7/internal/view/SupportMenuInflater.java
Normal file
506
android/support/v7/internal/view/SupportMenuInflater.java
Normal file
|
@ -0,0 +1,506 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.support.v7.internal.view;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.res.TypedArray;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.support.v4.internal.view.SupportMenu;
|
||||
import android.support.v4.view.ActionProvider;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.internal.view.menu.MenuItemImpl;
|
||||
import android.support.v7.internal.view.menu.MenuItemWrapperICS;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Xml;
|
||||
import android.view.InflateException;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* This class is used to instantiate menu XML files into Menu objects.
|
||||
* <p>
|
||||
* For performance reasons, menu inflation relies heavily on pre-processing of
|
||||
* XML files that is done at build time. Therefore, it is not currently possible
|
||||
* to use SupportMenuInflater with an XmlPullParser over a plain XML file at runtime;
|
||||
* it only works with an XmlPullParser returned from a compiled resource (R.
|
||||
* <em>something</em> file.)
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class SupportMenuInflater extends MenuInflater {
|
||||
private static final String LOG_TAG = "SupportMenuInflater";
|
||||
|
||||
/** Menu tag name in XML. */
|
||||
private static final String XML_MENU = "menu";
|
||||
|
||||
/** Group tag name in XML. */
|
||||
private static final String XML_GROUP = "group";
|
||||
|
||||
/** Item tag name in XML. */
|
||||
private static final String XML_ITEM = "item";
|
||||
|
||||
private static final int NO_ID = 0;
|
||||
|
||||
private static final Class<?>[] ACTION_VIEW_CONSTRUCTOR_SIGNATURE = new Class[] {Context.class};
|
||||
|
||||
private static final Class<?>[] ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE =
|
||||
ACTION_VIEW_CONSTRUCTOR_SIGNATURE;
|
||||
|
||||
private final Object[] mActionViewConstructorArguments;
|
||||
|
||||
private final Object[] mActionProviderConstructorArguments;
|
||||
|
||||
private Context mContext;
|
||||
private Object mRealOwner;
|
||||
|
||||
/**
|
||||
* Constructs a menu inflater.
|
||||
*
|
||||
* @see Activity#getMenuInflater()
|
||||
*/
|
||||
public SupportMenuInflater(Context context) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
mActionViewConstructorArguments = new Object[] {context};
|
||||
mActionProviderConstructorArguments = mActionViewConstructorArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflate a menu hierarchy from the specified XML resource. Throws
|
||||
* {@link InflateException} if there is an error.
|
||||
*
|
||||
* @param menuRes Resource ID for an XML layout resource to load (e.g.,
|
||||
* <code>R.menu.main_activity</code>)
|
||||
* @param menu The Menu to inflate into. The items and submenus will be
|
||||
* added to this Menu.
|
||||
*/
|
||||
@Override
|
||||
public void inflate(int menuRes, Menu menu) {
|
||||
// If we're not dealing with a SupportMenu instance, let super handle
|
||||
if (!(menu instanceof SupportMenu)) {
|
||||
super.inflate(menuRes, menu);
|
||||
return;
|
||||
}
|
||||
|
||||
XmlResourceParser parser = null;
|
||||
try {
|
||||
parser = mContext.getResources().getLayout(menuRes);
|
||||
AttributeSet attrs = Xml.asAttributeSet(parser);
|
||||
|
||||
parseMenu(parser, attrs, menu);
|
||||
} catch (XmlPullParserException e) {
|
||||
throw new InflateException("Error inflating menu XML", e);
|
||||
} catch (IOException e) {
|
||||
throw new InflateException("Error inflating menu XML", e);
|
||||
} finally {
|
||||
if (parser != null) parser.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called internally to fill the given menu. If a sub menu is seen, it will
|
||||
* call this recursively.
|
||||
*/
|
||||
private void parseMenu(XmlPullParser parser, AttributeSet attrs, Menu menu)
|
||||
throws XmlPullParserException, IOException {
|
||||
MenuState menuState = new MenuState(menu);
|
||||
|
||||
int eventType = parser.getEventType();
|
||||
String tagName;
|
||||
boolean lookingForEndOfUnknownTag = false;
|
||||
String unknownTagName = null;
|
||||
|
||||
// This loop will skip to the menu start tag
|
||||
do {
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
tagName = parser.getName();
|
||||
if (tagName.equals(XML_MENU)) {
|
||||
// Go to next tag
|
||||
eventType = parser.next();
|
||||
break;
|
||||
}
|
||||
|
||||
throw new RuntimeException("Expecting menu, got " + tagName);
|
||||
}
|
||||
eventType = parser.next();
|
||||
} while (eventType != XmlPullParser.END_DOCUMENT);
|
||||
|
||||
boolean reachedEndOfMenu = false;
|
||||
while (!reachedEndOfMenu) {
|
||||
switch (eventType) {
|
||||
case XmlPullParser.START_TAG:
|
||||
if (lookingForEndOfUnknownTag) {
|
||||
break;
|
||||
}
|
||||
|
||||
tagName = parser.getName();
|
||||
if (tagName.equals(XML_GROUP)) {
|
||||
menuState.readGroup(attrs);
|
||||
} else if (tagName.equals(XML_ITEM)) {
|
||||
menuState.readItem(attrs);
|
||||
} else if (tagName.equals(XML_MENU)) {
|
||||
// A menu start tag denotes a submenu for an item
|
||||
SubMenu subMenu = menuState.addSubMenuItem();
|
||||
|
||||
// Parse the submenu into returned SubMenu
|
||||
parseMenu(parser, attrs, subMenu);
|
||||
} else {
|
||||
lookingForEndOfUnknownTag = true;
|
||||
unknownTagName = tagName;
|
||||
}
|
||||
break;
|
||||
|
||||
case XmlPullParser.END_TAG:
|
||||
tagName = parser.getName();
|
||||
if (lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) {
|
||||
lookingForEndOfUnknownTag = false;
|
||||
unknownTagName = null;
|
||||
} else if (tagName.equals(XML_GROUP)) {
|
||||
menuState.resetGroup();
|
||||
} else if (tagName.equals(XML_ITEM)) {
|
||||
// Add the item if it hasn't been added (if the item was
|
||||
// a submenu, it would have been added already)
|
||||
if (!menuState.hasAddedItem()) {
|
||||
if (menuState.itemActionProvider != null &&
|
||||
menuState.itemActionProvider.hasSubMenu()) {
|
||||
menuState.addSubMenuItem();
|
||||
} else {
|
||||
menuState.addItem();
|
||||
}
|
||||
}
|
||||
} else if (tagName.equals(XML_MENU)) {
|
||||
reachedEndOfMenu = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case XmlPullParser.END_DOCUMENT:
|
||||
throw new RuntimeException("Unexpected end of document");
|
||||
}
|
||||
|
||||
eventType = parser.next();
|
||||
}
|
||||
}
|
||||
|
||||
private Object getRealOwner() {
|
||||
if (mRealOwner == null) {
|
||||
mRealOwner = findRealOwner(mContext);
|
||||
}
|
||||
return mRealOwner;
|
||||
}
|
||||
|
||||
private Object findRealOwner(Object owner) {
|
||||
if (owner instanceof Activity) {
|
||||
return owner;
|
||||
}
|
||||
if (owner instanceof ContextWrapper) {
|
||||
return findRealOwner(((ContextWrapper) owner).getBaseContext());
|
||||
}
|
||||
return owner;
|
||||
}
|
||||
|
||||
private static class InflatedOnMenuItemClickListener
|
||||
implements MenuItem.OnMenuItemClickListener {
|
||||
private static final Class<?>[] PARAM_TYPES = new Class[] { MenuItem.class };
|
||||
|
||||
private Object mRealOwner;
|
||||
private Method mMethod;
|
||||
|
||||
public InflatedOnMenuItemClickListener(Object realOwner, String methodName) {
|
||||
mRealOwner = realOwner;
|
||||
Class<?> c = realOwner.getClass();
|
||||
try {
|
||||
mMethod = c.getMethod(methodName, PARAM_TYPES);
|
||||
} catch (Exception e) {
|
||||
InflateException ex = new InflateException(
|
||||
"Couldn't resolve menu item onClick handler " + methodName +
|
||||
" in class " + c.getName());
|
||||
ex.initCause(e);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
try {
|
||||
if (mMethod.getReturnType() == Boolean.TYPE) {
|
||||
return (Boolean) mMethod.invoke(mRealOwner, item);
|
||||
} else {
|
||||
mMethod.invoke(mRealOwner, item);
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* State for the current menu.
|
||||
* <p>
|
||||
* Groups can not be nested unless there is another menu (which will have
|
||||
* its state class).
|
||||
*/
|
||||
private class MenuState {
|
||||
private Menu menu;
|
||||
|
||||
/*
|
||||
* Group state is set on items as they are added, allowing an item to
|
||||
* override its group state. (As opposed to set on items at the group end tag.)
|
||||
*/
|
||||
private int groupId;
|
||||
private int groupCategory;
|
||||
private int groupOrder;
|
||||
private int groupCheckable;
|
||||
private boolean groupVisible;
|
||||
private boolean groupEnabled;
|
||||
|
||||
private boolean itemAdded;
|
||||
private int itemId;
|
||||
private int itemCategoryOrder;
|
||||
private CharSequence itemTitle;
|
||||
private CharSequence itemTitleCondensed;
|
||||
private int itemIconResId;
|
||||
private char itemAlphabeticShortcut;
|
||||
private char itemNumericShortcut;
|
||||
/**
|
||||
* Sync to attrs.xml enum:
|
||||
* - 0: none
|
||||
* - 1: all
|
||||
* - 2: exclusive
|
||||
*/
|
||||
private int itemCheckable;
|
||||
private boolean itemChecked;
|
||||
private boolean itemVisible;
|
||||
private boolean itemEnabled;
|
||||
|
||||
/**
|
||||
* Sync to attrs.xml enum, values in MenuItem:
|
||||
* - 0: never
|
||||
* - 1: ifRoom
|
||||
* - 2: always
|
||||
* - -1: Safe sentinel for "no value".
|
||||
*/
|
||||
private int itemShowAsAction;
|
||||
|
||||
private int itemActionViewLayout;
|
||||
private String itemActionViewClassName;
|
||||
private String itemActionProviderClassName;
|
||||
|
||||
private String itemListenerMethodName;
|
||||
|
||||
private ActionProvider itemActionProvider;
|
||||
|
||||
private static final int defaultGroupId = NO_ID;
|
||||
private static final int defaultItemId = NO_ID;
|
||||
private static final int defaultItemCategory = 0;
|
||||
private static final int defaultItemOrder = 0;
|
||||
private static final int defaultItemCheckable = 0;
|
||||
private static final boolean defaultItemChecked = false;
|
||||
private static final boolean defaultItemVisible = true;
|
||||
private static final boolean defaultItemEnabled = true;
|
||||
|
||||
public MenuState(final Menu menu) {
|
||||
this.menu = menu;
|
||||
|
||||
resetGroup();
|
||||
}
|
||||
|
||||
public void resetGroup() {
|
||||
groupId = defaultGroupId;
|
||||
groupCategory = defaultItemCategory;
|
||||
groupOrder = defaultItemOrder;
|
||||
groupCheckable = defaultItemCheckable;
|
||||
groupVisible = defaultItemVisible;
|
||||
groupEnabled = defaultItemEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the parser is pointing to a group tag.
|
||||
*/
|
||||
public void readGroup(AttributeSet attrs) {
|
||||
TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.MenuGroup);
|
||||
|
||||
groupId = a.getResourceId(R.styleable.MenuGroup_android_id, defaultGroupId);
|
||||
groupCategory = a.getInt(
|
||||
R.styleable.MenuGroup_android_menuCategory, defaultItemCategory);
|
||||
groupOrder = a.getInt(R.styleable.MenuGroup_android_orderInCategory, defaultItemOrder);
|
||||
groupCheckable = a.getInt(
|
||||
R.styleable.MenuGroup_android_checkableBehavior, defaultItemCheckable);
|
||||
groupVisible = a.getBoolean(R.styleable.MenuGroup_android_visible, defaultItemVisible);
|
||||
groupEnabled = a.getBoolean(R.styleable.MenuGroup_android_enabled, defaultItemEnabled);
|
||||
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the parser is pointing to an item tag.
|
||||
*/
|
||||
public void readItem(AttributeSet attrs) {
|
||||
TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.MenuItem);
|
||||
|
||||
// Inherit attributes from the group as default value
|
||||
itemId = a.getResourceId(R.styleable.MenuItem_android_id, defaultItemId);
|
||||
final int category = a.getInt(R.styleable.MenuItem_android_menuCategory, groupCategory);
|
||||
final int order = a.getInt(R.styleable.MenuItem_android_orderInCategory, groupOrder);
|
||||
itemCategoryOrder = (category & SupportMenu.CATEGORY_MASK) |
|
||||
(order & SupportMenu.USER_MASK);
|
||||
itemTitle = a.getText(R.styleable.MenuItem_android_title);
|
||||
itemTitleCondensed = a.getText(R.styleable.MenuItem_android_titleCondensed);
|
||||
itemIconResId = a.getResourceId(R.styleable.MenuItem_android_icon, 0);
|
||||
itemAlphabeticShortcut =
|
||||
getShortcut(a.getString(R.styleable.MenuItem_android_alphabeticShortcut));
|
||||
itemNumericShortcut =
|
||||
getShortcut(a.getString(R.styleable.MenuItem_android_numericShortcut));
|
||||
if (a.hasValue(R.styleable.MenuItem_android_checkable)) {
|
||||
// Item has attribute checkable, use it
|
||||
itemCheckable = a.getBoolean(R.styleable.MenuItem_android_checkable, false) ? 1 : 0;
|
||||
} else {
|
||||
// Item does not have attribute, use the group's (group can have one more state
|
||||
// for checkable that represents the exclusive checkable)
|
||||
itemCheckable = groupCheckable;
|
||||
}
|
||||
itemChecked = a.getBoolean(R.styleable.MenuItem_android_checked, defaultItemChecked);
|
||||
itemVisible = a.getBoolean(R.styleable.MenuItem_android_visible, groupVisible);
|
||||
itemEnabled = a.getBoolean(R.styleable.MenuItem_android_enabled, groupEnabled);
|
||||
itemShowAsAction = a.getInt(R.styleable.MenuItem_showAsAction, -1);
|
||||
itemListenerMethodName = a.getString(R.styleable.MenuItem_android_onClick);
|
||||
itemActionViewLayout = a.getResourceId(R.styleable.MenuItem_actionLayout, 0);
|
||||
itemActionViewClassName = a.getString(R.styleable.MenuItem_actionViewClass);
|
||||
itemActionProviderClassName = a.getString(R.styleable.MenuItem_actionProviderClass);
|
||||
|
||||
final boolean hasActionProvider = itemActionProviderClassName != null;
|
||||
if (hasActionProvider && itemActionViewLayout == 0 && itemActionViewClassName == null) {
|
||||
itemActionProvider = newInstance(itemActionProviderClassName,
|
||||
ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE,
|
||||
mActionProviderConstructorArguments);
|
||||
} else {
|
||||
if (hasActionProvider) {
|
||||
Log.w(LOG_TAG, "Ignoring attribute 'actionProviderClass'."
|
||||
+ " Action view already specified.");
|
||||
}
|
||||
itemActionProvider = null;
|
||||
}
|
||||
|
||||
a.recycle();
|
||||
|
||||
itemAdded = false;
|
||||
}
|
||||
|
||||
private char getShortcut(String shortcutString) {
|
||||
if (shortcutString == null) {
|
||||
return 0;
|
||||
} else {
|
||||
return shortcutString.charAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void setItem(MenuItem item) {
|
||||
item.setChecked(itemChecked)
|
||||
.setVisible(itemVisible)
|
||||
.setEnabled(itemEnabled)
|
||||
.setCheckable(itemCheckable >= 1)
|
||||
.setTitleCondensed(itemTitleCondensed)
|
||||
.setIcon(itemIconResId)
|
||||
.setAlphabeticShortcut(itemAlphabeticShortcut)
|
||||
.setNumericShortcut(itemNumericShortcut);
|
||||
|
||||
if (itemShowAsAction >= 0) {
|
||||
MenuItemCompat.setShowAsAction(item, itemShowAsAction);
|
||||
}
|
||||
|
||||
if (itemListenerMethodName != null) {
|
||||
if (mContext.isRestricted()) {
|
||||
throw new IllegalStateException("The android:onClick attribute cannot "
|
||||
+ "be used within a restricted context");
|
||||
}
|
||||
item.setOnMenuItemClickListener(
|
||||
new InflatedOnMenuItemClickListener(getRealOwner(), itemListenerMethodName));
|
||||
}
|
||||
|
||||
final MenuItemImpl impl = item instanceof MenuItemImpl ? (MenuItemImpl) item : null;
|
||||
if (itemCheckable >= 2) {
|
||||
if (item instanceof MenuItemImpl) {
|
||||
((MenuItemImpl) item).setExclusiveCheckable(true);
|
||||
} else if (item instanceof MenuItemWrapperICS) {
|
||||
((MenuItemWrapperICS) item).setExclusiveCheckable(true);
|
||||
}
|
||||
}
|
||||
|
||||
boolean actionViewSpecified = false;
|
||||
if (itemActionViewClassName != null) {
|
||||
View actionView = (View) newInstance(itemActionViewClassName,
|
||||
ACTION_VIEW_CONSTRUCTOR_SIGNATURE, mActionViewConstructorArguments);
|
||||
MenuItemCompat.setActionView(item, actionView);
|
||||
actionViewSpecified = true;
|
||||
}
|
||||
if (itemActionViewLayout > 0) {
|
||||
if (!actionViewSpecified) {
|
||||
MenuItemCompat.setActionView(item, itemActionViewLayout);
|
||||
actionViewSpecified = true;
|
||||
} else {
|
||||
Log.w(LOG_TAG, "Ignoring attribute 'itemActionViewLayout'."
|
||||
+ " Action view already specified.");
|
||||
}
|
||||
}
|
||||
if (itemActionProvider != null) {
|
||||
MenuItemCompat.setActionProvider(item, itemActionProvider);
|
||||
}
|
||||
}
|
||||
|
||||
public void addItem() {
|
||||
itemAdded = true;
|
||||
setItem(menu.add(groupId, itemId, itemCategoryOrder, itemTitle));
|
||||
}
|
||||
|
||||
public SubMenu addSubMenuItem() {
|
||||
itemAdded = true;
|
||||
SubMenu subMenu = menu.addSubMenu(groupId, itemId, itemCategoryOrder, itemTitle);
|
||||
setItem(subMenu.getItem());
|
||||
return subMenu;
|
||||
}
|
||||
|
||||
public boolean hasAddedItem() {
|
||||
return itemAdded;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T newInstance(String className, Class<?>[] constructorSignature,
|
||||
Object[] arguments) {
|
||||
try {
|
||||
Class<?> clazz = mContext.getClassLoader().loadClass(className);
|
||||
Constructor<?> constructor = clazz.getConstructor(constructorSignature);
|
||||
return (T) constructor.newInstance(arguments);
|
||||
} catch (Exception e) {
|
||||
Log.w(LOG_TAG, "Cannot instantiate class: " + className, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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.support.v7.internal.view;
|
||||
|
||||
import android.support.v4.view.ViewPropertyAnimatorCompat;
|
||||
import android.support.v4.view.ViewPropertyAnimatorListener;
|
||||
import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
|
||||
import android.view.View;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A very naive implementation of a set of
|
||||
* {@link android.support.v4.view.ViewPropertyAnimatorCompat}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ViewPropertyAnimatorCompatSet {
|
||||
|
||||
private final ArrayList<ViewPropertyAnimatorCompat> mAnimators;
|
||||
|
||||
private long mDuration = -1;
|
||||
private Interpolator mInterpolator;
|
||||
private ViewPropertyAnimatorListener mListener;
|
||||
|
||||
private boolean mIsStarted;
|
||||
|
||||
public ViewPropertyAnimatorCompatSet() {
|
||||
mAnimators = new ArrayList<ViewPropertyAnimatorCompat>();
|
||||
}
|
||||
|
||||
public ViewPropertyAnimatorCompatSet play(ViewPropertyAnimatorCompat animator) {
|
||||
if (!mIsStarted) {
|
||||
mAnimators.add(animator);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (mIsStarted) return;
|
||||
for (ViewPropertyAnimatorCompat animator : mAnimators) {
|
||||
if (mDuration >= 0) {
|
||||
animator.setDuration(mDuration);
|
||||
}
|
||||
if (mInterpolator != null) {
|
||||
animator.setInterpolator(mInterpolator);
|
||||
}
|
||||
if (mListener != null) {
|
||||
animator.setListener(mProxyListener);
|
||||
}
|
||||
animator.start();
|
||||
}
|
||||
|
||||
mIsStarted = true;
|
||||
}
|
||||
|
||||
private void onAnimationsEnded() {
|
||||
mIsStarted = false;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
if (!mIsStarted) {
|
||||
return;
|
||||
}
|
||||
for (ViewPropertyAnimatorCompat animator : mAnimators) {
|
||||
animator.cancel();
|
||||
}
|
||||
mIsStarted = false;
|
||||
}
|
||||
|
||||
public ViewPropertyAnimatorCompatSet setDuration(long duration) {
|
||||
if (!mIsStarted) {
|
||||
mDuration = duration;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewPropertyAnimatorCompatSet setInterpolator(Interpolator interpolator) {
|
||||
if (!mIsStarted) {
|
||||
mInterpolator = interpolator;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewPropertyAnimatorCompatSet setListener(ViewPropertyAnimatorListener listener) {
|
||||
if (!mIsStarted) {
|
||||
mListener = listener;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private final ViewPropertyAnimatorListenerAdapter mProxyListener
|
||||
= new ViewPropertyAnimatorListenerAdapter() {
|
||||
private boolean mProxyStarted = false;
|
||||
private int mProxyEndCount = 0;
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(View view) {
|
||||
if (mProxyStarted) {
|
||||
return;
|
||||
}
|
||||
mProxyStarted = true;
|
||||
if (mListener != null) {
|
||||
mListener.onAnimationStart(null);
|
||||
}
|
||||
}
|
||||
|
||||
void onEnd() {
|
||||
mProxyEndCount = 0;
|
||||
mProxyStarted = false;
|
||||
onAnimationsEnded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(View view) {
|
||||
if (++mProxyEndCount == mAnimators.size()) {
|
||||
if (mListener != null) {
|
||||
mListener.onAnimationEnd(null);
|
||||
}
|
||||
onEnd();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
151
android/support/v7/internal/view/WindowCallbackWrapper.java
Normal file
151
android/support/v7/internal/view/WindowCallbackWrapper.java
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* 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.support.v7.internal.view;
|
||||
|
||||
import android.view.ActionMode;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
/**
|
||||
* A simple decorator stub for Window.Callback that passes through any calls
|
||||
* to the wrapped instance as a base implementation. Call super.foo() to call into
|
||||
* the wrapped callback for any subclasses.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class WindowCallbackWrapper implements Window.Callback {
|
||||
|
||||
final Window.Callback mWrapped;
|
||||
|
||||
public WindowCallbackWrapper(Window.Callback wrapped) {
|
||||
if (wrapped == null) {
|
||||
throw new IllegalArgumentException("Window callback may not be null");
|
||||
}
|
||||
mWrapped = wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
return mWrapped.dispatchKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
|
||||
return mWrapped.dispatchKeyShortcutEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent event) {
|
||||
return mWrapped.dispatchTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTrackballEvent(MotionEvent event) {
|
||||
return mWrapped.dispatchTrackballEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchGenericMotionEvent(MotionEvent event) {
|
||||
return mWrapped.dispatchGenericMotionEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
return mWrapped.dispatchPopulateAccessibilityEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreatePanelView(int featureId) {
|
||||
return mWrapped.onCreatePanelView(featureId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreatePanelMenu(int featureId, Menu menu) {
|
||||
return mWrapped.onCreatePanelMenu(featureId, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreparePanel(int featureId, View view, Menu menu) {
|
||||
return mWrapped.onPreparePanel(featureId, view, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuOpened(int featureId, Menu menu) {
|
||||
return mWrapped.onMenuOpened(featureId, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemSelected(int featureId, MenuItem item) {
|
||||
return mWrapped.onMenuItemSelected(featureId, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowAttributesChanged(WindowManager.LayoutParams attrs) {
|
||||
mWrapped.onWindowAttributesChanged(attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContentChanged() {
|
||||
mWrapped.onContentChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
mWrapped.onWindowFocusChanged(hasFocus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
mWrapped.onAttachedToWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
mWrapped.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPanelClosed(int featureId, Menu menu) {
|
||||
mWrapped.onPanelClosed(featureId, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSearchRequested() {
|
||||
return mWrapped.onSearchRequested();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
|
||||
return mWrapped.onWindowStartingActionMode(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActionModeStarted(ActionMode mode) {
|
||||
mWrapped.onActionModeStarted(mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActionModeFinished(ActionMode mode) {
|
||||
mWrapped.onActionModeFinished(mode);
|
||||
}
|
||||
}
|
296
android/support/v7/internal/view/menu/ActionMenuItem.java
Normal file
296
android/support/v7/internal/view/menu/ActionMenuItem.java
Normal file
|
@ -0,0 +1,296 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.view.ActionProvider;
|
||||
import android.support.v4.internal.view.SupportMenuItem;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class ActionMenuItem implements SupportMenuItem {
|
||||
|
||||
private final int mId;
|
||||
private final int mGroup;
|
||||
private final int mCategoryOrder;
|
||||
private final int mOrdering;
|
||||
|
||||
private CharSequence mTitle;
|
||||
private CharSequence mTitleCondensed;
|
||||
private Intent mIntent;
|
||||
private char mShortcutNumericChar;
|
||||
private char mShortcutAlphabeticChar;
|
||||
|
||||
private Drawable mIconDrawable;
|
||||
private int mIconResId = NO_ICON;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private SupportMenuItem.OnMenuItemClickListener mClickListener;
|
||||
|
||||
private static final int NO_ICON = 0;
|
||||
|
||||
private int mFlags = ENABLED;
|
||||
private static final int CHECKABLE = 0x00000001;
|
||||
private static final int CHECKED = 0x00000002;
|
||||
private static final int EXCLUSIVE = 0x00000004;
|
||||
private static final int HIDDEN = 0x00000008;
|
||||
private static final int ENABLED = 0x00000010;
|
||||
|
||||
public ActionMenuItem(Context context, int group, int id, int categoryOrder, int ordering,
|
||||
CharSequence title) {
|
||||
mContext = context;
|
||||
mId = id;
|
||||
mGroup = group;
|
||||
mCategoryOrder = categoryOrder;
|
||||
mOrdering = ordering;
|
||||
mTitle = title;
|
||||
}
|
||||
|
||||
public char getAlphabeticShortcut() {
|
||||
return mShortcutAlphabeticChar;
|
||||
}
|
||||
|
||||
public int getGroupId() {
|
||||
return mGroup;
|
||||
}
|
||||
|
||||
public Drawable getIcon() {
|
||||
return mIconDrawable;
|
||||
}
|
||||
|
||||
public Intent getIntent() {
|
||||
return mIntent;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public ContextMenuInfo getMenuInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public char getNumericShortcut() {
|
||||
return mShortcutNumericChar;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return mOrdering;
|
||||
}
|
||||
|
||||
public SubMenu getSubMenu() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public CharSequence getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
public CharSequence getTitleCondensed() {
|
||||
return mTitleCondensed != null ? mTitleCondensed : mTitle;
|
||||
}
|
||||
|
||||
public boolean hasSubMenu() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isCheckable() {
|
||||
return (mFlags & CHECKABLE) != 0;
|
||||
}
|
||||
|
||||
public boolean isChecked() {
|
||||
return (mFlags & CHECKED) != 0;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return (mFlags & ENABLED) != 0;
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
return (mFlags & HIDDEN) == 0;
|
||||
}
|
||||
|
||||
public MenuItem setAlphabeticShortcut(char alphaChar) {
|
||||
mShortcutAlphabeticChar = alphaChar;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MenuItem setCheckable(boolean checkable) {
|
||||
mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActionMenuItem setExclusiveCheckable(boolean exclusive) {
|
||||
mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MenuItem setChecked(boolean checked) {
|
||||
mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MenuItem setEnabled(boolean enabled) {
|
||||
mFlags = (mFlags & ~ENABLED) | (enabled ? ENABLED : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MenuItem setIcon(Drawable icon) {
|
||||
mIconDrawable = icon;
|
||||
mIconResId = NO_ICON;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MenuItem setIcon(int iconRes) {
|
||||
mIconResId = iconRes;
|
||||
mIconDrawable = ContextCompat.getDrawable(mContext, iconRes);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MenuItem setIntent(Intent intent) {
|
||||
mIntent = intent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MenuItem setNumericShortcut(char numericChar) {
|
||||
mShortcutNumericChar = numericChar;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
|
||||
mClickListener = menuItemClickListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MenuItem setShortcut(char numericChar, char alphaChar) {
|
||||
mShortcutNumericChar = numericChar;
|
||||
mShortcutAlphabeticChar = alphaChar;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MenuItem setTitle(CharSequence title) {
|
||||
mTitle = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MenuItem setTitle(int title) {
|
||||
mTitle = mContext.getResources().getString(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MenuItem setTitleCondensed(CharSequence title) {
|
||||
mTitleCondensed = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MenuItem setVisible(boolean visible) {
|
||||
mFlags = (mFlags & HIDDEN) | (visible ? 0 : HIDDEN);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean invoke() {
|
||||
if (mClickListener != null && mClickListener.onMenuItemClick(this)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mIntent != null) {
|
||||
mContext.startActivity(mIntent);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setShowAsAction(int show) {
|
||||
// Do nothing. ActionMenuItems always show as action buttons.
|
||||
}
|
||||
|
||||
public SupportMenuItem setActionView(View actionView) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public View getActionView() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setActionProvider(android.view.ActionProvider actionProvider) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.view.ActionProvider getActionProvider() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportMenuItem setActionView(int resId) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionProvider getSupportActionProvider() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportMenuItem setSupportActionProvider(ActionProvider actionProvider) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportMenuItem setShowAsActionFlags(int actionEnum) {
|
||||
setShowAsAction(actionEnum);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expandActionView() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collapseActionView() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActionViewExpanded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setOnActionExpandListener(MenuItem.OnActionExpandListener listener) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportMenuItem setSupportOnActionExpandListener(MenuItemCompat.OnActionExpandListener listener) {
|
||||
// No need to save the listener; ActionMenuItem does not support collapsing items.
|
||||
return this;
|
||||
}
|
||||
}
|
334
android/support/v7/internal/view/menu/ActionMenuItemView.java
Normal file
334
android/support/v7/internal/view/menu/ActionMenuItemView.java
Normal file
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.internal.text.AllCapsTransformationMethod;
|
||||
import android.support.v7.internal.widget.CompatTextView;
|
||||
import android.support.v7.widget.ActionMenuView;
|
||||
import android.support.v7.widget.ListPopupWindow;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.TransformationMethod;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class ActionMenuItemView extends CompatTextView
|
||||
implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener,
|
||||
ActionMenuView.ActionMenuChildView {
|
||||
|
||||
private static final String TAG = "ActionMenuItemView";
|
||||
|
||||
private MenuItemImpl mItemData;
|
||||
private CharSequence mTitle;
|
||||
private Drawable mIcon;
|
||||
private MenuBuilder.ItemInvoker mItemInvoker;
|
||||
private ListPopupWindow.ForwardingListener mForwardingListener;
|
||||
private PopupCallback mPopupCallback;
|
||||
|
||||
private boolean mAllowTextWithIcon;
|
||||
private boolean mExpandedFormat;
|
||||
private int mMinWidth;
|
||||
private int mSavedPaddingLeft;
|
||||
|
||||
private static final int MAX_ICON_SIZE = 32; // dp
|
||||
private int mMaxIconSize;
|
||||
|
||||
public ActionMenuItemView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ActionMenuItemView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
final Resources res = context.getResources();
|
||||
mAllowTextWithIcon = res.getBoolean(
|
||||
R.bool.abc_config_allowActionMenuItemTextWithIcon);
|
||||
TypedArray a = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.ActionMenuItemView, defStyle, 0);
|
||||
mMinWidth = a.getDimensionPixelSize(
|
||||
R.styleable.ActionMenuItemView_android_minWidth, 0);
|
||||
a.recycle();
|
||||
|
||||
final float density = res.getDisplayMetrics().density;
|
||||
mMaxIconSize = (int) (MAX_ICON_SIZE * density + 0.5f);
|
||||
|
||||
setOnClickListener(this);
|
||||
setOnLongClickListener(this);
|
||||
|
||||
mSavedPaddingLeft = -1;
|
||||
}
|
||||
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
if (Build.VERSION.SDK_INT >= 8) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
mAllowTextWithIcon = getContext().getResources().getBoolean(
|
||||
R.bool.abc_config_allowActionMenuItemTextWithIcon);
|
||||
updateTextButtonVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPadding(int l, int t, int r, int b) {
|
||||
mSavedPaddingLeft = l;
|
||||
super.setPadding(l, t, r, b);
|
||||
}
|
||||
|
||||
public MenuItemImpl getItemData() {
|
||||
return mItemData;
|
||||
}
|
||||
|
||||
public void initialize(MenuItemImpl itemData, int menuType) {
|
||||
mItemData = itemData;
|
||||
|
||||
setIcon(itemData.getIcon());
|
||||
setTitle(itemData.getTitleForItemView(this)); // Title only takes effect if there is no icon
|
||||
setId(itemData.getItemId());
|
||||
|
||||
setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
|
||||
setEnabled(itemData.isEnabled());
|
||||
if (itemData.hasSubMenu()) {
|
||||
if (mForwardingListener == null) {
|
||||
mForwardingListener = new ActionMenuItemForwardingListener();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent e) {
|
||||
if (mItemData.hasSubMenu() && mForwardingListener != null
|
||||
&& mForwardingListener.onTouch(this, e)) {
|
||||
return true;
|
||||
}
|
||||
return super.onTouchEvent(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mItemInvoker != null) {
|
||||
mItemInvoker.invokeItem(mItemData);
|
||||
}
|
||||
}
|
||||
|
||||
public void setItemInvoker(MenuBuilder.ItemInvoker invoker) {
|
||||
mItemInvoker = invoker;
|
||||
}
|
||||
|
||||
public void setPopupCallback(PopupCallback popupCallback) {
|
||||
mPopupCallback = popupCallback;
|
||||
}
|
||||
|
||||
public boolean prefersCondensedTitle() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setCheckable(boolean checkable) {
|
||||
// TODO Support checkable action items
|
||||
}
|
||||
|
||||
public void setChecked(boolean checked) {
|
||||
// TODO Support checkable action items
|
||||
}
|
||||
|
||||
public void setExpandedFormat(boolean expandedFormat) {
|
||||
if (mExpandedFormat != expandedFormat) {
|
||||
mExpandedFormat = expandedFormat;
|
||||
if (mItemData != null) {
|
||||
mItemData.actionFormatChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTextButtonVisibility() {
|
||||
boolean visible = !TextUtils.isEmpty(mTitle);
|
||||
visible &= mIcon == null ||
|
||||
(mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat));
|
||||
|
||||
setText(visible ? mTitle : null);
|
||||
}
|
||||
|
||||
public void setIcon(Drawable icon) {
|
||||
mIcon = icon;
|
||||
if (icon != null) {
|
||||
int width = icon.getIntrinsicWidth();
|
||||
int height = icon.getIntrinsicHeight();
|
||||
if (width > mMaxIconSize) {
|
||||
final float scale = (float) mMaxIconSize / width;
|
||||
width = mMaxIconSize;
|
||||
height *= scale;
|
||||
}
|
||||
if (height > mMaxIconSize) {
|
||||
final float scale = (float) mMaxIconSize / height;
|
||||
height = mMaxIconSize;
|
||||
width *= scale;
|
||||
}
|
||||
icon.setBounds(0, 0, width, height);
|
||||
}
|
||||
setCompoundDrawables(icon, null, null, null);
|
||||
|
||||
updateTextButtonVisibility();
|
||||
}
|
||||
|
||||
public boolean hasText() {
|
||||
return !TextUtils.isEmpty(getText());
|
||||
}
|
||||
|
||||
public void setShortcut(boolean showShortcut, char shortcutKey) {
|
||||
// Action buttons don't show text for shortcut keys.
|
||||
}
|
||||
|
||||
public void setTitle(CharSequence title) {
|
||||
mTitle = title;
|
||||
|
||||
setContentDescription(mTitle);
|
||||
updateTextButtonVisibility();
|
||||
}
|
||||
|
||||
public boolean showsIcon() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean needsDividerBefore() {
|
||||
return hasText() && mItemData.getIcon() == null;
|
||||
}
|
||||
|
||||
public boolean needsDividerAfter() {
|
||||
return hasText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
if (hasText()) {
|
||||
// Don't show the cheat sheet for items that already show text.
|
||||
return false;
|
||||
}
|
||||
|
||||
final int[] screenPos = new int[2];
|
||||
final Rect displayFrame = new Rect();
|
||||
getLocationOnScreen(screenPos);
|
||||
getWindowVisibleDisplayFrame(displayFrame);
|
||||
|
||||
final Context context = getContext();
|
||||
final int width = getWidth();
|
||||
final int height = getHeight();
|
||||
final int midy = screenPos[1] + height / 2;
|
||||
int referenceX = screenPos[0] + width / 2;
|
||||
if (ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_LTR) {
|
||||
final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
|
||||
referenceX = screenWidth - referenceX; // mirror
|
||||
}
|
||||
Toast cheatSheet = Toast.makeText(context, mItemData.getTitle(), Toast.LENGTH_SHORT);
|
||||
if (midy < displayFrame.height()) {
|
||||
// Show along the top; follow action buttons
|
||||
cheatSheet.setGravity(Gravity.TOP | GravityCompat.END, referenceX, height);
|
||||
} else {
|
||||
// Show along the bottom center
|
||||
cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
|
||||
}
|
||||
cheatSheet.show();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
final boolean textVisible = hasText();
|
||||
if (textVisible && mSavedPaddingLeft >= 0) {
|
||||
super.setPadding(mSavedPaddingLeft, getPaddingTop(),
|
||||
getPaddingRight(), getPaddingBottom());
|
||||
}
|
||||
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
||||
final int oldMeasuredWidth = getMeasuredWidth();
|
||||
final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(widthSize, mMinWidth)
|
||||
: mMinWidth;
|
||||
|
||||
if (widthMode != MeasureSpec.EXACTLY && mMinWidth > 0 && oldMeasuredWidth < targetWidth) {
|
||||
// Remeasure at exactly the minimum width.
|
||||
super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
|
||||
heightMeasureSpec);
|
||||
}
|
||||
|
||||
if (!textVisible && mIcon != null) {
|
||||
// TextView won't center compound drawables in both dimensions without
|
||||
// a little coercion. Pad in to center the icon after we've measured.
|
||||
final int w = getMeasuredWidth();
|
||||
final int dw = mIcon.getBounds().width();
|
||||
super.setPadding((w - dw) / 2, getPaddingTop(), getPaddingRight(), getPaddingBottom());
|
||||
}
|
||||
}
|
||||
|
||||
private class ActionMenuItemForwardingListener extends ListPopupWindow.ForwardingListener {
|
||||
public ActionMenuItemForwardingListener() {
|
||||
super(ActionMenuItemView.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListPopupWindow getPopup() {
|
||||
if (mPopupCallback != null) {
|
||||
return mPopupCallback.getPopup();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onForwardingStarted() {
|
||||
// Call the invoker, then check if the expected popup is showing.
|
||||
if (mItemInvoker != null && mItemInvoker.invokeItem(mItemData)) {
|
||||
final ListPopupWindow popup = getPopup();
|
||||
return popup != null && popup.isShowing();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onForwardingStopped() {
|
||||
final ListPopupWindow popup = getPopup();
|
||||
if (popup != null) {
|
||||
popup.dismiss();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class PopupCallback {
|
||||
public abstract ListPopupWindow getPopup();
|
||||
}
|
||||
}
|
237
android/support/v7/internal/view/menu/BaseMenuPresenter.java
Normal file
237
android/support/v7/internal/view/menu/BaseMenuPresenter.java
Normal file
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* Copyright (C) 2011 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Base class for MenuPresenters that have a consistent container view and item views. Behaves
|
||||
* similarly to an AdapterView in that existing item views will be reused if possible when items
|
||||
* change.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public abstract class BaseMenuPresenter implements MenuPresenter {
|
||||
|
||||
protected Context mSystemContext;
|
||||
protected Context mContext;
|
||||
protected MenuBuilder mMenu;
|
||||
protected LayoutInflater mSystemInflater;
|
||||
protected LayoutInflater mInflater;
|
||||
private Callback mCallback;
|
||||
|
||||
private int mMenuLayoutRes;
|
||||
private int mItemLayoutRes;
|
||||
|
||||
protected MenuView mMenuView;
|
||||
|
||||
private int mId;
|
||||
|
||||
/**
|
||||
* Construct a new BaseMenuPresenter.
|
||||
*
|
||||
* @param context Context for generating system-supplied views
|
||||
* @param menuLayoutRes Layout resource ID for the menu container view
|
||||
* @param itemLayoutRes Layout resource ID for a single item view
|
||||
*/
|
||||
public BaseMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes) {
|
||||
mSystemContext = context;
|
||||
mSystemInflater = LayoutInflater.from(context);
|
||||
mMenuLayoutRes = menuLayoutRes;
|
||||
mItemLayoutRes = itemLayoutRes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initForMenu(Context context, MenuBuilder menu) {
|
||||
mContext = context;
|
||||
mInflater = LayoutInflater.from(mContext);
|
||||
mMenu = menu;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuView getMenuView(ViewGroup root) {
|
||||
if (mMenuView == null) {
|
||||
mMenuView = (MenuView) mSystemInflater.inflate(mMenuLayoutRes, root, false);
|
||||
mMenuView.initialize(mMenu);
|
||||
updateMenuView(true);
|
||||
}
|
||||
|
||||
return mMenuView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reuses item views when it can
|
||||
*/
|
||||
public void updateMenuView(boolean cleared) {
|
||||
final ViewGroup parent = (ViewGroup) mMenuView;
|
||||
if (parent == null) return;
|
||||
|
||||
int childIndex = 0;
|
||||
if (mMenu != null) {
|
||||
mMenu.flagActionItems();
|
||||
ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
|
||||
final int itemCount = visibleItems.size();
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
MenuItemImpl item = visibleItems.get(i);
|
||||
if (shouldIncludeItem(childIndex, item)) {
|
||||
final View convertView = parent.getChildAt(childIndex);
|
||||
final MenuItemImpl oldItem = convertView instanceof MenuView.ItemView ?
|
||||
((MenuView.ItemView) convertView).getItemData() : null;
|
||||
final View itemView = getItemView(item, convertView, parent);
|
||||
if (item != oldItem) {
|
||||
// Don't let old states linger with new data.
|
||||
itemView.setPressed(false);
|
||||
ViewCompat.jumpDrawablesToCurrentState(itemView);
|
||||
}
|
||||
if (itemView != convertView) {
|
||||
addItemView(itemView, childIndex);
|
||||
}
|
||||
childIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove leftover views.
|
||||
while (childIndex < parent.getChildCount()) {
|
||||
if (!filterLeftoverView(parent, childIndex)) {
|
||||
childIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item view at the given index.
|
||||
*
|
||||
* @param itemView View to add
|
||||
* @param childIndex Index within the parent to insert at
|
||||
*/
|
||||
protected void addItemView(View itemView, int childIndex) {
|
||||
final ViewGroup currentParent = (ViewGroup) itemView.getParent();
|
||||
if (currentParent != null) {
|
||||
currentParent.removeView(itemView);
|
||||
}
|
||||
((ViewGroup) mMenuView).addView(itemView, childIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the child view at index and remove it if appropriate.
|
||||
* @param parent Parent to filter from
|
||||
* @param childIndex Index to filter
|
||||
* @return true if the child view at index was removed
|
||||
*/
|
||||
protected boolean filterLeftoverView(ViewGroup parent, int childIndex) {
|
||||
parent.removeViewAt(childIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setCallback(Callback cb) {
|
||||
mCallback = cb;
|
||||
}
|
||||
|
||||
public Callback getCallback() {
|
||||
return mCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new item view that can be re-bound to other item data later.
|
||||
*
|
||||
* @return The new item view
|
||||
*/
|
||||
public MenuView.ItemView createItemView(ViewGroup parent) {
|
||||
return (MenuView.ItemView) mSystemInflater.inflate(mItemLayoutRes, parent, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare an item view for use. See AdapterView for the basic idea at work here.
|
||||
* This may require creating a new item view, but well-behaved implementations will
|
||||
* re-use the view passed as convertView if present. The returned view will be populated
|
||||
* with data from the item parameter.
|
||||
*
|
||||
* @param item Item to present
|
||||
* @param convertView Existing view to reuse
|
||||
* @param parent Intended parent view - use for inflation.
|
||||
* @return View that presents the requested menu item
|
||||
*/
|
||||
public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) {
|
||||
MenuView.ItemView itemView;
|
||||
if (convertView instanceof MenuView.ItemView) {
|
||||
itemView = (MenuView.ItemView) convertView;
|
||||
} else {
|
||||
itemView = createItemView(parent);
|
||||
}
|
||||
bindItemView(item, itemView);
|
||||
return (View) itemView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind item data to an existing item view.
|
||||
*
|
||||
* @param item Item to bind
|
||||
* @param itemView View to populate with item data
|
||||
*/
|
||||
public abstract void bindItemView(MenuItemImpl item, MenuView.ItemView itemView);
|
||||
|
||||
/**
|
||||
* Filter item by child index and item data.
|
||||
*
|
||||
* @param childIndex Indended presentation index of this item
|
||||
* @param item Item to present
|
||||
* @return true if this item should be included in this menu presentation; false otherwise
|
||||
*/
|
||||
public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
||||
if (mCallback != null) {
|
||||
mCallback.onCloseMenu(menu, allMenusAreClosing);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onSubMenuSelected(SubMenuBuilder menu) {
|
||||
if (mCallback != null) {
|
||||
return mCallback.onOpenSubMenu(menu);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean flagActionItems() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
mId = id;
|
||||
}
|
||||
}
|
126
android/support/v7/internal/view/menu/BaseMenuWrapper.java
Normal file
126
android/support/v7/internal/view/menu/BaseMenuWrapper.java
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright (C) 2012 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.internal.view.SupportMenuItem;
|
||||
import android.support.v4.internal.view.SupportSubMenu;
|
||||
import android.support.v4.util.ArrayMap;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SubMenu;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
abstract class BaseMenuWrapper<T> extends BaseWrapper<T> {
|
||||
|
||||
final Context mContext;
|
||||
|
||||
private Map<SupportMenuItem, MenuItem> mMenuItems;
|
||||
private Map<SupportSubMenu, SubMenu> mSubMenus;
|
||||
|
||||
BaseMenuWrapper(Context context, T object) {
|
||||
super(object);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
final MenuItem getMenuItemWrapper(final MenuItem menuItem) {
|
||||
if (menuItem instanceof SupportMenuItem) {
|
||||
final SupportMenuItem supportMenuItem = (SupportMenuItem) menuItem;
|
||||
|
||||
// Instantiate Map if null
|
||||
if (mMenuItems == null) {
|
||||
mMenuItems = new ArrayMap<>();
|
||||
}
|
||||
|
||||
// First check if we already have a wrapper for this item
|
||||
MenuItem wrappedItem = mMenuItems.get(menuItem);
|
||||
|
||||
if (null == wrappedItem) {
|
||||
// ... if not, create one and add it to our map
|
||||
wrappedItem = MenuWrapperFactory.wrapSupportMenuItem(mContext, supportMenuItem);
|
||||
mMenuItems.put(supportMenuItem, wrappedItem);
|
||||
}
|
||||
|
||||
return wrappedItem;
|
||||
}
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
final SubMenu getSubMenuWrapper(final SubMenu subMenu) {
|
||||
if (subMenu instanceof SupportSubMenu) {
|
||||
final SupportSubMenu supportSubMenu = (SupportSubMenu) subMenu;
|
||||
|
||||
// Instantiate Map if null
|
||||
if (mSubMenus == null) {
|
||||
mSubMenus = new ArrayMap<>();
|
||||
}
|
||||
|
||||
SubMenu wrappedMenu = mSubMenus.get(supportSubMenu);
|
||||
|
||||
if (null == wrappedMenu) {
|
||||
wrappedMenu = MenuWrapperFactory.wrapSupportSubMenu(mContext, supportSubMenu);
|
||||
mSubMenus.put(supportSubMenu, wrappedMenu);
|
||||
}
|
||||
return wrappedMenu;
|
||||
}
|
||||
return subMenu;
|
||||
}
|
||||
|
||||
|
||||
final void internalClear() {
|
||||
if (mMenuItems != null) {
|
||||
mMenuItems.clear();
|
||||
}
|
||||
if (mSubMenus != null) {
|
||||
mSubMenus.clear();
|
||||
}
|
||||
}
|
||||
|
||||
final void internalRemoveGroup(final int groupId) {
|
||||
if (mMenuItems == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator<SupportMenuItem> iterator = mMenuItems.keySet().iterator();
|
||||
android.view.MenuItem menuItem;
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
menuItem = iterator.next();
|
||||
if (groupId == menuItem.getGroupId()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final void internalRemoveItem(final int id) {
|
||||
if (mMenuItems == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator<SupportMenuItem> iterator = mMenuItems.keySet().iterator();
|
||||
android.view.MenuItem menuItem;
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
menuItem = iterator.next();
|
||||
if (id == menuItem.getItemId()) {
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
android/support/v7/internal/view/menu/BaseWrapper.java
Normal file
34
android/support/v7/internal/view/menu/BaseWrapper.java
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2012 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.support.v7.internal.view.menu;
|
||||
|
||||
class BaseWrapper<T> {
|
||||
|
||||
final T mWrappedObject;
|
||||
|
||||
BaseWrapper(T object) {
|
||||
if (null == object) {
|
||||
throw new IllegalArgumentException("Wrapped Object can not be null.");
|
||||
}
|
||||
mWrappedObject = object;
|
||||
}
|
||||
|
||||
public T getWrappedObject() {
|
||||
return mWrappedObject;
|
||||
}
|
||||
|
||||
}
|
97
android/support/v7/internal/view/menu/ExpandedMenuView.java
Normal file
97
android/support/v7/internal/view/menu/ExpandedMenuView.java
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v7.internal.view.menu.MenuBuilder;
|
||||
import android.support.v7.internal.view.menu.MenuBuilder.ItemInvoker;
|
||||
import android.support.v7.internal.view.menu.MenuView;
|
||||
import android.support.v7.internal.widget.TintTypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ListView;
|
||||
|
||||
/**
|
||||
* The expanded menu view is a list-like menu with all of the available menu items. It is opened
|
||||
* by the user clicking no the 'More' button on the icon menu view.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class ExpandedMenuView extends ListView
|
||||
implements ItemInvoker, MenuView, OnItemClickListener {
|
||||
|
||||
private static final int[] TINT_ATTRS = {
|
||||
android.R.attr.background,
|
||||
android.R.attr.divider
|
||||
};
|
||||
|
||||
private MenuBuilder mMenu;
|
||||
|
||||
/** Default animations for this menu */
|
||||
private int mAnimations;
|
||||
|
||||
public ExpandedMenuView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, android.R.attr.listViewStyle);
|
||||
}
|
||||
|
||||
public ExpandedMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs);
|
||||
setOnItemClickListener(this);
|
||||
|
||||
TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS,
|
||||
defStyleAttr, 0);
|
||||
if (a.hasValue(0)) {
|
||||
setBackgroundDrawable(a.getDrawable(0));
|
||||
}
|
||||
if (a.hasValue(1)) {
|
||||
setDivider(a.getDrawable(1));
|
||||
}
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(MenuBuilder menu) {
|
||||
mMenu = menu;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
|
||||
// Clear the cached bitmaps of children
|
||||
setChildrenDrawingCacheEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean invokeItem(MenuItemImpl item) {
|
||||
return mMenu.performItemAction(item, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void onItemClick(AdapterView parent, View v, int position, long id) {
|
||||
invokeItem((MenuItemImpl) getAdapter().getItem(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWindowAnimations() {
|
||||
return mAnimations;
|
||||
}
|
||||
|
||||
}
|
282
android/support/v7/internal/view/menu/ListMenuItemView.java
Normal file
282
android/support/v7/internal/view/menu/ListMenuItemView.java
Normal file
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* The item view for each item in the ListView-based MenuViews.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ListMenuItemView extends LinearLayout implements MenuView.ItemView {
|
||||
|
||||
private static final String TAG = "ListMenuItemView";
|
||||
private MenuItemImpl mItemData;
|
||||
|
||||
private ImageView mIconView;
|
||||
private RadioButton mRadioButton;
|
||||
private TextView mTitleView;
|
||||
private CheckBox mCheckBox;
|
||||
private TextView mShortcutView;
|
||||
|
||||
private Drawable mBackground;
|
||||
private int mTextAppearance;
|
||||
private Context mTextAppearanceContext;
|
||||
private boolean mPreserveIconSpacing;
|
||||
|
||||
private int mMenuType;
|
||||
|
||||
private Context mContext;
|
||||
private LayoutInflater mInflater;
|
||||
|
||||
private boolean mForceShowIcon;
|
||||
|
||||
public ListMenuItemView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
|
||||
final TypedArray a = context.obtainStyledAttributes(
|
||||
attrs, R.styleable.MenuView, defStyle, 0);
|
||||
|
||||
mBackground = a.getDrawable(R.styleable.MenuView_android_itemBackground);
|
||||
mTextAppearance = a.getResourceId(R.styleable.
|
||||
MenuView_android_itemTextAppearance, -1);
|
||||
mPreserveIconSpacing = a.getBoolean(
|
||||
R.styleable.MenuView_preserveIconSpacing, false);
|
||||
mTextAppearanceContext = context;
|
||||
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
public ListMenuItemView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
setBackgroundDrawable(mBackground);
|
||||
|
||||
mTitleView = (TextView) findViewById(R.id.title);
|
||||
if (mTextAppearance != -1) {
|
||||
mTitleView.setTextAppearance(mTextAppearanceContext,
|
||||
mTextAppearance);
|
||||
}
|
||||
|
||||
mShortcutView = (TextView) findViewById(R.id.shortcut);
|
||||
}
|
||||
|
||||
public void initialize(MenuItemImpl itemData, int menuType) {
|
||||
mItemData = itemData;
|
||||
mMenuType = menuType;
|
||||
|
||||
setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
|
||||
|
||||
setTitle(itemData.getTitleForItemView(this));
|
||||
setCheckable(itemData.isCheckable());
|
||||
setShortcut(itemData.shouldShowShortcut(), itemData.getShortcut());
|
||||
setIcon(itemData.getIcon());
|
||||
setEnabled(itemData.isEnabled());
|
||||
}
|
||||
|
||||
public void setForceShowIcon(boolean forceShow) {
|
||||
mPreserveIconSpacing = mForceShowIcon = forceShow;
|
||||
}
|
||||
|
||||
public void setTitle(CharSequence title) {
|
||||
if (title != null) {
|
||||
mTitleView.setText(title);
|
||||
|
||||
if (mTitleView.getVisibility() != VISIBLE) mTitleView.setVisibility(VISIBLE);
|
||||
} else {
|
||||
if (mTitleView.getVisibility() != GONE) mTitleView.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public MenuItemImpl getItemData() {
|
||||
return mItemData;
|
||||
}
|
||||
|
||||
public void setCheckable(boolean checkable) {
|
||||
if (!checkable && mRadioButton == null && mCheckBox == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Depending on whether its exclusive check or not, the checkbox or
|
||||
// radio button will be the one in use (and the other will be otherCompoundButton)
|
||||
final CompoundButton compoundButton;
|
||||
final CompoundButton otherCompoundButton;
|
||||
|
||||
if (mItemData.isExclusiveCheckable()) {
|
||||
if (mRadioButton == null) {
|
||||
insertRadioButton();
|
||||
}
|
||||
compoundButton = mRadioButton;
|
||||
otherCompoundButton = mCheckBox;
|
||||
} else {
|
||||
if (mCheckBox == null) {
|
||||
insertCheckBox();
|
||||
}
|
||||
compoundButton = mCheckBox;
|
||||
otherCompoundButton = mRadioButton;
|
||||
}
|
||||
|
||||
if (checkable) {
|
||||
compoundButton.setChecked(mItemData.isChecked());
|
||||
|
||||
final int newVisibility = checkable ? VISIBLE : GONE;
|
||||
if (compoundButton.getVisibility() != newVisibility) {
|
||||
compoundButton.setVisibility(newVisibility);
|
||||
}
|
||||
|
||||
// Make sure the other compound button isn't visible
|
||||
if (otherCompoundButton != null && otherCompoundButton.getVisibility() != GONE) {
|
||||
otherCompoundButton.setVisibility(GONE);
|
||||
}
|
||||
} else {
|
||||
if (mCheckBox != null) {
|
||||
mCheckBox.setVisibility(GONE);
|
||||
}
|
||||
if (mRadioButton != null) {
|
||||
mRadioButton.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setChecked(boolean checked) {
|
||||
CompoundButton compoundButton;
|
||||
|
||||
if (mItemData.isExclusiveCheckable()) {
|
||||
if (mRadioButton == null) {
|
||||
insertRadioButton();
|
||||
}
|
||||
compoundButton = mRadioButton;
|
||||
} else {
|
||||
if (mCheckBox == null) {
|
||||
insertCheckBox();
|
||||
}
|
||||
compoundButton = mCheckBox;
|
||||
}
|
||||
|
||||
compoundButton.setChecked(checked);
|
||||
}
|
||||
|
||||
public void setShortcut(boolean showShortcut, char shortcutKey) {
|
||||
final int newVisibility = (showShortcut && mItemData.shouldShowShortcut())
|
||||
? VISIBLE : GONE;
|
||||
|
||||
if (newVisibility == VISIBLE) {
|
||||
mShortcutView.setText(mItemData.getShortcutLabel());
|
||||
}
|
||||
|
||||
if (mShortcutView.getVisibility() != newVisibility) {
|
||||
mShortcutView.setVisibility(newVisibility);
|
||||
}
|
||||
}
|
||||
|
||||
public void setIcon(Drawable icon) {
|
||||
final boolean showIcon = mItemData.shouldShowIcon() || mForceShowIcon;
|
||||
if (!showIcon && !mPreserveIconSpacing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIconView == null && icon == null && !mPreserveIconSpacing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIconView == null) {
|
||||
insertIconView();
|
||||
}
|
||||
|
||||
if (icon != null || mPreserveIconSpacing) {
|
||||
mIconView.setImageDrawable(showIcon ? icon : null);
|
||||
|
||||
if (mIconView.getVisibility() != VISIBLE) {
|
||||
mIconView.setVisibility(VISIBLE);
|
||||
}
|
||||
} else {
|
||||
mIconView.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
if (mIconView != null && mPreserveIconSpacing) {
|
||||
// Enforce minimum icon spacing
|
||||
ViewGroup.LayoutParams lp = getLayoutParams();
|
||||
LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
|
||||
if (lp.height > 0 && iconLp.width <= 0) {
|
||||
iconLp.width = lp.height;
|
||||
}
|
||||
}
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
private void insertIconView() {
|
||||
LayoutInflater inflater = getInflater();
|
||||
mIconView = (ImageView) inflater.inflate(R.layout.abc_list_menu_item_icon,
|
||||
this, false);
|
||||
addView(mIconView, 0);
|
||||
}
|
||||
|
||||
private void insertRadioButton() {
|
||||
LayoutInflater inflater = getInflater();
|
||||
mRadioButton =
|
||||
(RadioButton) inflater.inflate(R.layout.abc_list_menu_item_radio,
|
||||
this, false);
|
||||
addView(mRadioButton);
|
||||
}
|
||||
|
||||
private void insertCheckBox() {
|
||||
LayoutInflater inflater = getInflater();
|
||||
mCheckBox =
|
||||
(CheckBox) inflater.inflate(R.layout.abc_list_menu_item_checkbox,
|
||||
this, false);
|
||||
addView(mCheckBox);
|
||||
}
|
||||
|
||||
public boolean prefersCondensedTitle() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean showsIcon() {
|
||||
return mForceShowIcon;
|
||||
}
|
||||
|
||||
private LayoutInflater getInflater() {
|
||||
if (mInflater == null) {
|
||||
mInflater = LayoutInflater.from(mContext);
|
||||
}
|
||||
return mInflater;
|
||||
}
|
||||
}
|
||||
|
288
android/support/v7/internal/view/menu/ListMenuPresenter.java
Normal file
288
android/support/v7/internal/view/menu/ListMenuPresenter.java
Normal file
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.DataSetObserver;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.util.SparseArray;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ListAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* MenuPresenter for list-style menus.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClickListener {
|
||||
private static final String TAG = "ListMenuPresenter";
|
||||
|
||||
Context mContext;
|
||||
LayoutInflater mInflater;
|
||||
MenuBuilder mMenu;
|
||||
|
||||
ExpandedMenuView mMenuView;
|
||||
|
||||
private int mItemIndexOffset;
|
||||
int mThemeRes;
|
||||
int mItemLayoutRes;
|
||||
|
||||
private Callback mCallback;
|
||||
MenuAdapter mAdapter;
|
||||
|
||||
private int mId;
|
||||
|
||||
public static final String VIEWS_TAG = "android:menu:list";
|
||||
|
||||
/**
|
||||
* Construct a new ListMenuPresenter.
|
||||
* @param context Context to use for theming. This will supersede the context provided
|
||||
* to initForMenu when this presenter is added.
|
||||
* @param itemLayoutRes Layout resource for individual item views.
|
||||
*/
|
||||
public ListMenuPresenter(Context context, int itemLayoutRes) {
|
||||
this(itemLayoutRes, 0);
|
||||
mContext = context;
|
||||
mInflater = LayoutInflater.from(mContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ListMenuPresenter.
|
||||
* @param itemLayoutRes Layout resource for individual item views.
|
||||
* @param themeRes Resource ID of a theme to use for views.
|
||||
*/
|
||||
public ListMenuPresenter(int itemLayoutRes, int themeRes) {
|
||||
mItemLayoutRes = itemLayoutRes;
|
||||
mThemeRes = themeRes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initForMenu(Context context, MenuBuilder menu) {
|
||||
if (mThemeRes != 0) {
|
||||
mContext = new ContextThemeWrapper(context, mThemeRes);
|
||||
mInflater = LayoutInflater.from(mContext);
|
||||
} else if (mContext != null) {
|
||||
mContext = context;
|
||||
if (mInflater == null) {
|
||||
mInflater = LayoutInflater.from(mContext);
|
||||
}
|
||||
}
|
||||
mMenu = menu;
|
||||
if (mAdapter != null) {
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuView getMenuView(ViewGroup root) {
|
||||
if (mMenuView == null) {
|
||||
mMenuView = (ExpandedMenuView) mInflater.inflate(
|
||||
R.layout.abc_expanded_menu_layout, root, false);
|
||||
if (mAdapter == null) {
|
||||
mAdapter = new MenuAdapter();
|
||||
}
|
||||
mMenuView.setAdapter(mAdapter);
|
||||
mMenuView.setOnItemClickListener(this);
|
||||
}
|
||||
return mMenuView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this instead of getMenuView if you want to manage your own ListView.
|
||||
* For proper operation, the ListView hosting this adapter should add
|
||||
* this presenter as an OnItemClickListener.
|
||||
*
|
||||
* @return A ListAdapter containing the items in the menu.
|
||||
*/
|
||||
public ListAdapter getAdapter() {
|
||||
if (mAdapter == null) {
|
||||
mAdapter = new MenuAdapter();
|
||||
}
|
||||
return mAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMenuView(boolean cleared) {
|
||||
if (mAdapter != null) mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCallback(Callback cb) {
|
||||
mCallback = cb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
|
||||
if (!subMenu.hasVisibleItems()) return false;
|
||||
|
||||
// The window manager will give us a token.
|
||||
new MenuDialogHelper(subMenu).show(null);
|
||||
if (mCallback != null) {
|
||||
mCallback.onOpenSubMenu(subMenu);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
||||
if (mCallback != null) {
|
||||
mCallback.onCloseMenu(menu, allMenusAreClosing);
|
||||
}
|
||||
}
|
||||
|
||||
int getItemIndexOffset() {
|
||||
return mItemIndexOffset;
|
||||
}
|
||||
|
||||
public void setItemIndexOffset(int offset) {
|
||||
mItemIndexOffset = offset;
|
||||
if (mMenuView != null) {
|
||||
updateMenuView(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
mMenu.performItemAction(mAdapter.getItem(position), this, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean flagActionItems() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void saveHierarchyState(Bundle outState) {
|
||||
SparseArray<Parcelable> viewStates = new SparseArray<Parcelable>();
|
||||
if (mMenuView != null) {
|
||||
((View) mMenuView).saveHierarchyState(viewStates);
|
||||
}
|
||||
outState.putSparseParcelableArray(VIEWS_TAG, viewStates);
|
||||
}
|
||||
|
||||
public void restoreHierarchyState(Bundle inState) {
|
||||
SparseArray<Parcelable> viewStates = inState.getSparseParcelableArray(VIEWS_TAG);
|
||||
if (viewStates != null) {
|
||||
((View) mMenuView).restoreHierarchyState(viewStates);
|
||||
}
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
mId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
if (mMenuView == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Bundle state = new Bundle();
|
||||
saveHierarchyState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
restoreHierarchyState((Bundle) state);
|
||||
}
|
||||
|
||||
private class MenuAdapter extends BaseAdapter {
|
||||
private int mExpandedIndex = -1;
|
||||
|
||||
public MenuAdapter() {
|
||||
findExpandedIndex();
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
|
||||
int count = items.size() - mItemIndexOffset;
|
||||
if (mExpandedIndex < 0) {
|
||||
return count;
|
||||
}
|
||||
return count - 1;
|
||||
}
|
||||
|
||||
public MenuItemImpl getItem(int position) {
|
||||
ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
|
||||
position += mItemIndexOffset;
|
||||
if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
|
||||
position++;
|
||||
}
|
||||
return items.get(position);
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
// Since a menu item's ID is optional, we'll use the position as an
|
||||
// ID for the item in the AdapterView
|
||||
return position;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = mInflater.inflate(mItemLayoutRes, parent, false);
|
||||
}
|
||||
|
||||
MenuView.ItemView itemView = (MenuView.ItemView) convertView;
|
||||
itemView.initialize(getItem(position), 0);
|
||||
return convertView;
|
||||
}
|
||||
|
||||
void findExpandedIndex() {
|
||||
final MenuItemImpl expandedItem = mMenu.getExpandedItem();
|
||||
if (expandedItem != null) {
|
||||
final ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
|
||||
final int count = items.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final MenuItemImpl item = items.get(i);
|
||||
if (item == expandedItem) {
|
||||
mExpandedIndex = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
mExpandedIndex = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDataSetChanged() {
|
||||
findExpandedIndex();
|
||||
super.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
1352
android/support/v7/internal/view/menu/MenuBuilder.java
Normal file
1352
android/support/v7/internal/view/menu/MenuBuilder.java
Normal file
File diff suppressed because it is too large
Load diff
172
android/support/v7/internal/view/menu/MenuDialogHelper.java
Normal file
172
android/support/v7/internal/view/menu/MenuDialogHelper.java
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.IBinder;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
/**
|
||||
* Helper for menus that appear as Dialogs (context and submenus).
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class MenuDialogHelper implements DialogInterface.OnKeyListener,
|
||||
DialogInterface.OnClickListener,
|
||||
DialogInterface.OnDismissListener,
|
||||
MenuPresenter.Callback {
|
||||
private MenuBuilder mMenu;
|
||||
private AlertDialog mDialog;
|
||||
ListMenuPresenter mPresenter;
|
||||
private MenuPresenter.Callback mPresenterCallback;
|
||||
|
||||
public MenuDialogHelper(MenuBuilder menu) {
|
||||
mMenu = menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows menu as a dialog.
|
||||
*
|
||||
* @param windowToken Optional token to assign to the window.
|
||||
*/
|
||||
public void show(IBinder windowToken) {
|
||||
// Many references to mMenu, create local reference
|
||||
final MenuBuilder menu = mMenu;
|
||||
|
||||
// Get the builder for the dialog
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(menu.getContext());
|
||||
|
||||
// Need to force Light Menu theme as list_menu_item_layout is usually against a dark bg and
|
||||
// AlertDialog's bg is white
|
||||
mPresenter = new ListMenuPresenter(R.layout.abc_list_menu_item_layout,
|
||||
R.style.Theme_AppCompat_CompactMenu);
|
||||
|
||||
mPresenter.setCallback(this);
|
||||
mMenu.addMenuPresenter(mPresenter);
|
||||
builder.setAdapter(mPresenter.getAdapter(), this);
|
||||
|
||||
// Set the title
|
||||
final View headerView = menu.getHeaderView();
|
||||
if (headerView != null) {
|
||||
// Menu's client has given a custom header view, use it
|
||||
builder.setCustomTitle(headerView);
|
||||
} else {
|
||||
// Otherwise use the (text) title and icon
|
||||
builder.setIcon(menu.getHeaderIcon()).setTitle(menu.getHeaderTitle());
|
||||
}
|
||||
|
||||
// Set the key listener
|
||||
builder.setOnKeyListener(this);
|
||||
|
||||
// Show the menu
|
||||
mDialog = builder.create();
|
||||
mDialog.setOnDismissListener(this);
|
||||
|
||||
WindowManager.LayoutParams lp = mDialog.getWindow().getAttributes();
|
||||
lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
|
||||
if (windowToken != null) {
|
||||
lp.token = windowToken;
|
||||
}
|
||||
lp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
|
||||
|
||||
mDialog.show();
|
||||
}
|
||||
|
||||
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN
|
||||
&& event.getRepeatCount() == 0) {
|
||||
Window win = mDialog.getWindow();
|
||||
if (win != null) {
|
||||
View decor = win.getDecorView();
|
||||
if (decor != null) {
|
||||
KeyEvent.DispatcherState ds = decor.getKeyDispatcherState();
|
||||
if (ds != null) {
|
||||
ds.startTracking(event, this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP && !event.isCanceled()) {
|
||||
Window win = mDialog.getWindow();
|
||||
if (win != null) {
|
||||
View decor = win.getDecorView();
|
||||
if (decor != null) {
|
||||
KeyEvent.DispatcherState ds = decor.getKeyDispatcherState();
|
||||
if (ds != null && ds.isTracking(event)) {
|
||||
mMenu.close(true);
|
||||
dialog.dismiss();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Menu shortcut matching
|
||||
return mMenu.performShortcut(keyCode, event, 0);
|
||||
|
||||
}
|
||||
|
||||
public void setPresenterCallback(MenuPresenter.Callback cb) {
|
||||
mPresenterCallback = cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses the menu's dialog.
|
||||
*
|
||||
* @see Dialog#dismiss()
|
||||
*/
|
||||
public void dismiss() {
|
||||
if (mDialog != null) {
|
||||
mDialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
mPresenter.onCloseMenu(mMenu, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
||||
if (allMenusAreClosing || menu == mMenu) {
|
||||
dismiss();
|
||||
}
|
||||
if (mPresenterCallback != null) {
|
||||
mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOpenSubMenu(MenuBuilder subMenu) {
|
||||
if (mPresenterCallback != null) {
|
||||
return mPresenterCallback.onOpenSubMenu(subMenu);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
mMenu.performItemAction((MenuItemImpl) mPresenter.getAdapter().getItem(which), 0);
|
||||
}
|
||||
}
|
743
android/support/v7/internal/view/menu/MenuItemImpl.java
Normal file
743
android/support/v7/internal/view/menu/MenuItemImpl.java
Normal file
|
@ -0,0 +1,743 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.view.ActionProvider;
|
||||
import android.support.v4.internal.view.SupportMenuItem;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v7.internal.widget.TintManager;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
import android.view.ViewDebug;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final class MenuItemImpl implements SupportMenuItem {
|
||||
|
||||
private static final String TAG = "MenuItemImpl";
|
||||
|
||||
private static final int SHOW_AS_ACTION_MASK = SHOW_AS_ACTION_NEVER |
|
||||
SHOW_AS_ACTION_IF_ROOM |
|
||||
SHOW_AS_ACTION_ALWAYS;
|
||||
|
||||
private final int mId;
|
||||
private final int mGroup;
|
||||
private final int mCategoryOrder;
|
||||
private final int mOrdering;
|
||||
private CharSequence mTitle;
|
||||
private CharSequence mTitleCondensed;
|
||||
private Intent mIntent;
|
||||
private char mShortcutNumericChar;
|
||||
private char mShortcutAlphabeticChar;
|
||||
|
||||
/** The icon's drawable which is only created as needed */
|
||||
private Drawable mIconDrawable;
|
||||
|
||||
/**
|
||||
* The icon's resource ID which is used to get the Drawable when it is
|
||||
* needed (if the Drawable isn't already obtained--only one of the two is
|
||||
* needed).
|
||||
*/
|
||||
private int mIconResId = NO_ICON;
|
||||
|
||||
/** The menu to which this item belongs */
|
||||
private MenuBuilder mMenu;
|
||||
/** If this item should launch a sub menu, this is the sub menu to launch */
|
||||
private SubMenuBuilder mSubMenu;
|
||||
|
||||
private Runnable mItemCallback;
|
||||
private SupportMenuItem.OnMenuItemClickListener mClickListener;
|
||||
|
||||
private int mFlags = ENABLED;
|
||||
private static final int CHECKABLE = 0x00000001;
|
||||
private static final int CHECKED = 0x00000002;
|
||||
private static final int EXCLUSIVE = 0x00000004;
|
||||
private static final int HIDDEN = 0x00000008;
|
||||
private static final int ENABLED = 0x00000010;
|
||||
private static final int IS_ACTION = 0x00000020;
|
||||
|
||||
private int mShowAsAction = SHOW_AS_ACTION_NEVER;
|
||||
|
||||
private View mActionView;
|
||||
private ActionProvider mActionProvider;
|
||||
private MenuItemCompat.OnActionExpandListener mOnActionExpandListener;
|
||||
private boolean mIsActionViewExpanded = false;
|
||||
|
||||
/** Used for the icon resource ID if this item does not have an icon */
|
||||
static final int NO_ICON = 0;
|
||||
|
||||
/**
|
||||
* Current use case is for context menu: Extra information linked to the
|
||||
* View that added this item to the context menu.
|
||||
*/
|
||||
private ContextMenuInfo mMenuInfo;
|
||||
|
||||
private static String sPrependShortcutLabel;
|
||||
private static String sEnterShortcutLabel;
|
||||
private static String sDeleteShortcutLabel;
|
||||
private static String sSpaceShortcutLabel;
|
||||
|
||||
|
||||
/**
|
||||
* Instantiates this menu item.
|
||||
*
|
||||
* @param menu
|
||||
* @param group Item ordering grouping control. The item will be added after
|
||||
* all other items whose order is <= this number, and before any
|
||||
* that are larger than it. This can also be used to define
|
||||
* groups of items for batch state changes. Normally use 0.
|
||||
* @param id Unique item ID. Use 0 if you do not need a unique ID.
|
||||
* @param categoryOrder The ordering for this item.
|
||||
* @param title The text to display for the item.
|
||||
*/
|
||||
MenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering,
|
||||
CharSequence title, int showAsAction) {
|
||||
|
||||
/*if (sPrependShortcutLabel == null) {
|
||||
// This is instantiated from the UI thread, so no chance of sync issues
|
||||
sPrependShortcutLabel = menu.getContext().getResources().getString(
|
||||
com.android.internal.R.string.prepend_shortcut_label);
|
||||
sEnterShortcutLabel = menu.getContext().getResources().getString(
|
||||
com.android.internal.R.string.menu_enter_shortcut_label);
|
||||
sDeleteShortcutLabel = menu.getContext().getResources().getString(
|
||||
com.android.internal.R.string.menu_delete_shortcut_label);
|
||||
sSpaceShortcutLabel = menu.getContext().getResources().getString(
|
||||
com.android.internal.R.string.menu_space_shortcut_label);
|
||||
}*/
|
||||
|
||||
mMenu = menu;
|
||||
mId = id;
|
||||
mGroup = group;
|
||||
mCategoryOrder = categoryOrder;
|
||||
mOrdering = ordering;
|
||||
mTitle = title;
|
||||
mShowAsAction = showAsAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the item by calling various listeners or callbacks.
|
||||
*
|
||||
* @return true if the invocation was handled, false otherwise
|
||||
*/
|
||||
public boolean invoke() {
|
||||
if (mClickListener != null && mClickListener.onMenuItemClick(this)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mMenu.dispatchMenuItemSelected(mMenu.getRootMenu(), this)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mItemCallback != null) {
|
||||
mItemCallback.run();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mIntent != null) {
|
||||
try {
|
||||
mMenu.getContext().startActivity(mIntent);
|
||||
return true;
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.e(TAG, "Can't find activity to handle intent; ignoring", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (mActionProvider != null && mActionProvider.onPerformDefaultAction()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return (mFlags & ENABLED) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setEnabled(boolean enabled) {
|
||||
if (enabled) {
|
||||
mFlags |= ENABLED;
|
||||
} else {
|
||||
mFlags &= ~ENABLED;
|
||||
}
|
||||
|
||||
mMenu.onItemsChanged(false);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGroupId() {
|
||||
return mGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ViewDebug.CapturedViewProperty
|
||||
public int getItemId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return mCategoryOrder;
|
||||
}
|
||||
|
||||
public int getOrdering() {
|
||||
return mOrdering;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
return mIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setIntent(Intent intent) {
|
||||
mIntent = intent;
|
||||
return this;
|
||||
}
|
||||
|
||||
Runnable getCallback() {
|
||||
return mItemCallback;
|
||||
}
|
||||
|
||||
public MenuItem setCallback(Runnable callback) {
|
||||
mItemCallback = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getAlphabeticShortcut() {
|
||||
return mShortcutAlphabeticChar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setAlphabeticShortcut(char alphaChar) {
|
||||
if (mShortcutAlphabeticChar == alphaChar) {
|
||||
return this;
|
||||
}
|
||||
|
||||
mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
|
||||
|
||||
mMenu.onItemsChanged(false);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getNumericShortcut() {
|
||||
return mShortcutNumericChar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setNumericShortcut(char numericChar) {
|
||||
if (mShortcutNumericChar == numericChar) {
|
||||
return this;
|
||||
}
|
||||
|
||||
mShortcutNumericChar = numericChar;
|
||||
|
||||
mMenu.onItemsChanged(false);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setShortcut(char numericChar, char alphaChar) {
|
||||
mShortcutNumericChar = numericChar;
|
||||
mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
|
||||
|
||||
mMenu.onItemsChanged(false);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The active shortcut (based on QWERTY-mode of the menu).
|
||||
*/
|
||||
char getShortcut() {
|
||||
return (mMenu.isQwertyMode() ? mShortcutAlphabeticChar : mShortcutNumericChar);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The label to show for the shortcut. This includes the chording key (for example
|
||||
* 'Menu+a'). Also, any non-human readable characters should be human readable (for
|
||||
* example 'Menu+enter').
|
||||
*/
|
||||
String getShortcutLabel() {
|
||||
|
||||
char shortcut = getShortcut();
|
||||
if (shortcut == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(sPrependShortcutLabel);
|
||||
switch (shortcut) {
|
||||
|
||||
case '\n':
|
||||
sb.append(sEnterShortcutLabel);
|
||||
break;
|
||||
|
||||
case '\b':
|
||||
sb.append(sDeleteShortcutLabel);
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
sb.append(sSpaceShortcutLabel);
|
||||
break;
|
||||
|
||||
default:
|
||||
sb.append(shortcut);
|
||||
break;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this menu item should be showing shortcuts (depends on
|
||||
* whether the menu should show shortcuts and whether this item has
|
||||
* a shortcut defined)
|
||||
*/
|
||||
boolean shouldShowShortcut() {
|
||||
// Show shortcuts if the menu is supposed to show shortcuts AND this item has a shortcut
|
||||
return mMenu.isShortcutsVisible() && (getShortcut() != 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu getSubMenu() {
|
||||
return mSubMenu;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSubMenu() {
|
||||
return mSubMenu != null;
|
||||
}
|
||||
|
||||
void setSubMenu(SubMenuBuilder subMenu) {
|
||||
mSubMenu = subMenu;
|
||||
|
||||
subMenu.setHeaderTitle(getTitle());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ViewDebug.CapturedViewProperty
|
||||
public CharSequence getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the title for a particular {@link MenuView.ItemView}
|
||||
*
|
||||
* @param itemView The ItemView that is receiving the title
|
||||
* @return Either the title or condensed title based on what the ItemView prefers
|
||||
*/
|
||||
CharSequence getTitleForItemView(MenuView.ItemView itemView) {
|
||||
return ((itemView != null) && itemView.prefersCondensedTitle())
|
||||
? getTitleCondensed()
|
||||
: getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setTitle(CharSequence title) {
|
||||
mTitle = title;
|
||||
|
||||
mMenu.onItemsChanged(false);
|
||||
|
||||
if (mSubMenu != null) {
|
||||
mSubMenu.setHeaderTitle(title);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setTitle(int title) {
|
||||
return setTitle(mMenu.getContext().getString(title));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitleCondensed() {
|
||||
final CharSequence ctitle = mTitleCondensed != null ? mTitleCondensed : mTitle;
|
||||
|
||||
if (Build.VERSION.SDK_INT < 18 && ctitle != null && !(ctitle instanceof String)) {
|
||||
// For devices pre-JB-MR2, where we have a non-String CharSequence, we need to
|
||||
// convert this to a String so that EventLog.writeEvent() does not throw an exception
|
||||
// in Activity.onMenuItemSelected()
|
||||
return ctitle.toString();
|
||||
} else {
|
||||
// Else, we just return the condensed title
|
||||
return ctitle;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setTitleCondensed(CharSequence title) {
|
||||
mTitleCondensed = title;
|
||||
|
||||
// Could use getTitle() in the loop below, but just cache what it would do here
|
||||
if (title == null) {
|
||||
title = mTitle;
|
||||
}
|
||||
|
||||
mMenu.onItemsChanged(false);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
if (mIconDrawable != null) {
|
||||
return mIconDrawable;
|
||||
}
|
||||
|
||||
if (mIconResId != NO_ICON) {
|
||||
Drawable icon = TintManager.getDrawable(mMenu.getContext(), mIconResId);
|
||||
mIconResId = NO_ICON;
|
||||
mIconDrawable = icon;
|
||||
return icon;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setIcon(Drawable icon) {
|
||||
mIconResId = NO_ICON;
|
||||
mIconDrawable = icon;
|
||||
mMenu.onItemsChanged(false);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setIcon(int iconResId) {
|
||||
mIconDrawable = null;
|
||||
mIconResId = iconResId;
|
||||
|
||||
// If we have a view, we need to push the Drawable to them
|
||||
mMenu.onItemsChanged(false);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCheckable() {
|
||||
return (mFlags & CHECKABLE) == CHECKABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setCheckable(boolean checkable) {
|
||||
final int oldFlags = mFlags;
|
||||
mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
|
||||
if (oldFlags != mFlags) {
|
||||
mMenu.onItemsChanged(false);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setExclusiveCheckable(boolean exclusive) {
|
||||
mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
|
||||
}
|
||||
|
||||
public boolean isExclusiveCheckable() {
|
||||
return (mFlags & EXCLUSIVE) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return (mFlags & CHECKED) == CHECKED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setChecked(boolean checked) {
|
||||
if ((mFlags & EXCLUSIVE) != 0) {
|
||||
// Call the method on the Menu since it knows about the others in this
|
||||
// exclusive checkable group
|
||||
mMenu.setExclusiveItemChecked(this);
|
||||
} else {
|
||||
setCheckedInt(checked);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
void setCheckedInt(boolean checked) {
|
||||
final int oldFlags = mFlags;
|
||||
mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0);
|
||||
if (oldFlags != mFlags) {
|
||||
mMenu.onItemsChanged(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
if (mActionProvider != null && mActionProvider.overridesItemVisibility()) {
|
||||
return (mFlags & HIDDEN) == 0 && mActionProvider.isVisible();
|
||||
}
|
||||
return (mFlags & HIDDEN) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the visibility of the item. This method DOES NOT notify the parent menu of a change
|
||||
* in this item, so this should only be called from methods that will eventually trigger this
|
||||
* change. If unsure, use {@link #setVisible(boolean)} instead.
|
||||
*
|
||||
* @param shown Whether to show (true) or hide (false).
|
||||
* @return Whether the item's shown state was changed
|
||||
*/
|
||||
boolean setVisibleInt(boolean shown) {
|
||||
final int oldFlags = mFlags;
|
||||
mFlags = (mFlags & ~HIDDEN) | (shown ? 0 : HIDDEN);
|
||||
return oldFlags != mFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setVisible(boolean shown) {
|
||||
// Try to set the shown state to the given state. If the shown state was changed
|
||||
// (i.e. the previous state isn't the same as given state), notify the parent menu that
|
||||
// the shown state has changed for this item
|
||||
if (setVisibleInt(shown)) mMenu.onItemVisibleChanged(this);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener clickListener) {
|
||||
mClickListener = clickListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mTitle.toString();
|
||||
}
|
||||
|
||||
void setMenuInfo(ContextMenuInfo menuInfo) {
|
||||
mMenuInfo = menuInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContextMenuInfo getMenuInfo() {
|
||||
return mMenuInfo;
|
||||
}
|
||||
|
||||
public void actionFormatChanged() {
|
||||
mMenu.onItemActionRequestChanged(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether the menu should show icons for menu items.
|
||||
*/
|
||||
public boolean shouldShowIcon() {
|
||||
return mMenu.getOptionalIconsVisible();
|
||||
}
|
||||
|
||||
public boolean isActionButton() {
|
||||
return (mFlags & IS_ACTION) == IS_ACTION;
|
||||
}
|
||||
|
||||
public boolean requestsActionButton() {
|
||||
return (mShowAsAction & SHOW_AS_ACTION_IF_ROOM) == SHOW_AS_ACTION_IF_ROOM;
|
||||
}
|
||||
|
||||
public boolean requiresActionButton() {
|
||||
return (mShowAsAction & SHOW_AS_ACTION_ALWAYS) == SHOW_AS_ACTION_ALWAYS;
|
||||
}
|
||||
|
||||
public void setIsActionButton(boolean isActionButton) {
|
||||
if (isActionButton) {
|
||||
mFlags |= IS_ACTION;
|
||||
} else {
|
||||
mFlags &= ~IS_ACTION;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean showsTextAsAction() {
|
||||
return (mShowAsAction & SHOW_AS_ACTION_WITH_TEXT) == SHOW_AS_ACTION_WITH_TEXT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShowAsAction(int actionEnum) {
|
||||
switch (actionEnum & SHOW_AS_ACTION_MASK) {
|
||||
case SHOW_AS_ACTION_ALWAYS:
|
||||
case SHOW_AS_ACTION_IF_ROOM:
|
||||
case SHOW_AS_ACTION_NEVER:
|
||||
// Looks good!
|
||||
break;
|
||||
|
||||
default:
|
||||
// Mutually exclusive options selected!
|
||||
throw new IllegalArgumentException("SHOW_AS_ACTION_ALWAYS, SHOW_AS_ACTION_IF_ROOM,"
|
||||
+ " and SHOW_AS_ACTION_NEVER are mutually exclusive.");
|
||||
}
|
||||
mShowAsAction = actionEnum;
|
||||
mMenu.onItemActionRequestChanged(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportMenuItem setActionView(View view) {
|
||||
mActionView = view;
|
||||
mActionProvider = null;
|
||||
if (view != null && view.getId() == View.NO_ID && mId > 0) {
|
||||
view.setId(mId);
|
||||
}
|
||||
mMenu.onItemActionRequestChanged(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportMenuItem setActionView(int resId) {
|
||||
final Context context = mMenu.getContext();
|
||||
final LayoutInflater inflater = LayoutInflater.from(context);
|
||||
setActionView(inflater.inflate(resId, new LinearLayout(context), false));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getActionView() {
|
||||
if (mActionView != null) {
|
||||
return mActionView;
|
||||
} else if (mActionProvider != null) {
|
||||
mActionView = mActionProvider.onCreateActionView(this);
|
||||
return mActionView;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setActionProvider(android.view.ActionProvider actionProvider) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This is not supported, use MenuItemCompat.setActionProvider()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.view.ActionProvider getActionProvider() {
|
||||
throw new UnsupportedOperationException(
|
||||
"This is not supported, use MenuItemCompat.getActionProvider()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionProvider getSupportActionProvider() {
|
||||
return mActionProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportMenuItem setSupportActionProvider(ActionProvider actionProvider) {
|
||||
if (mActionProvider != null) {
|
||||
mActionProvider.setVisibilityListener(null);
|
||||
}
|
||||
mActionView = null;
|
||||
mActionProvider = actionProvider;
|
||||
mMenu.onItemsChanged(true); // Measurement can be changed
|
||||
if (mActionProvider != null) {
|
||||
mActionProvider.setVisibilityListener(new ActionProvider.VisibilityListener() {
|
||||
@Override
|
||||
public void onActionProviderVisibilityChanged(boolean isVisible) {
|
||||
mMenu.onItemVisibleChanged(MenuItemImpl.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportMenuItem setShowAsActionFlags(int actionEnum) {
|
||||
setShowAsAction(actionEnum);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expandActionView() {
|
||||
if (!hasCollapsibleActionView()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mOnActionExpandListener == null ||
|
||||
mOnActionExpandListener.onMenuItemActionExpand(this)) {
|
||||
return mMenu.expandItemActionView(this);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collapseActionView() {
|
||||
if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0) {
|
||||
return false;
|
||||
}
|
||||
if (mActionView == null) {
|
||||
// We're already collapsed if we have no action view.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mOnActionExpandListener == null ||
|
||||
mOnActionExpandListener.onMenuItemActionCollapse(this)) {
|
||||
return mMenu.collapseItemActionView(this);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportMenuItem setSupportOnActionExpandListener(
|
||||
MenuItemCompat.OnActionExpandListener listener) {
|
||||
mOnActionExpandListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasCollapsibleActionView() {
|
||||
if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) != 0) {
|
||||
if (mActionView == null && mActionProvider != null) {
|
||||
mActionView = mActionProvider.onCreateActionView(this);
|
||||
}
|
||||
return mActionView != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setActionViewExpanded(boolean isExpanded) {
|
||||
mIsActionViewExpanded = isExpanded;
|
||||
mMenu.onItemsChanged(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActionViewExpanded() {
|
||||
return mIsActionViewExpanded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setOnActionExpandListener(MenuItem.OnActionExpandListener listener) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This is not supported, use MenuItemCompat.setOnActionExpandListener()");
|
||||
}
|
||||
}
|
401
android/support/v7/internal/view/menu/MenuItemWrapperICS.java
Normal file
401
android/support/v7/internal/view/menu/MenuItemWrapperICS.java
Normal file
|
@ -0,0 +1,401 @@
|
|||
/*
|
||||
* Copyright (C) 2012 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.v4.internal.view.SupportMenuItem;
|
||||
import android.support.v4.view.ActionProvider;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v7.view.CollapsibleActionView;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Wraps a support {@link SupportMenuItem} as a framework {@link android.view.MenuItem}
|
||||
* @hide
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
public class MenuItemWrapperICS extends BaseMenuWrapper<SupportMenuItem> implements MenuItem {
|
||||
static final String LOG_TAG = "MenuItemWrapper";
|
||||
|
||||
// Reflection Method to call setExclusiveCheckable
|
||||
private Method mSetExclusiveCheckableMethod;
|
||||
|
||||
MenuItemWrapperICS(Context context, SupportMenuItem object) {
|
||||
super(context, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemId() {
|
||||
return mWrappedObject.getItemId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGroupId() {
|
||||
return mWrappedObject.getGroupId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return mWrappedObject.getOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setTitle(CharSequence title) {
|
||||
mWrappedObject.setTitle(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setTitle(int title) {
|
||||
mWrappedObject.setTitle(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
return mWrappedObject.getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setTitleCondensed(CharSequence title) {
|
||||
mWrappedObject.setTitleCondensed(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitleCondensed() {
|
||||
return mWrappedObject.getTitleCondensed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setIcon(Drawable icon) {
|
||||
mWrappedObject.setIcon(icon);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setIcon(int iconRes) {
|
||||
mWrappedObject.setIcon(iconRes);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return mWrappedObject.getIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setIntent(Intent intent) {
|
||||
mWrappedObject.setIntent(intent);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getIntent() {
|
||||
return mWrappedObject.getIntent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setShortcut(char numericChar, char alphaChar) {
|
||||
mWrappedObject.setShortcut(numericChar, alphaChar);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setNumericShortcut(char numericChar) {
|
||||
mWrappedObject.setNumericShortcut(numericChar);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getNumericShortcut() {
|
||||
return mWrappedObject.getNumericShortcut();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setAlphabeticShortcut(char alphaChar) {
|
||||
mWrappedObject.setAlphabeticShortcut(alphaChar);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getAlphabeticShortcut() {
|
||||
return mWrappedObject.getAlphabeticShortcut();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setCheckable(boolean checkable) {
|
||||
mWrappedObject.setCheckable(checkable);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCheckable() {
|
||||
return mWrappedObject.isCheckable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setChecked(boolean checked) {
|
||||
mWrappedObject.setChecked(checked);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return mWrappedObject.isChecked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setVisible(boolean visible) {
|
||||
return mWrappedObject.setVisible(visible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return mWrappedObject.isVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setEnabled(boolean enabled) {
|
||||
mWrappedObject.setEnabled(enabled);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return mWrappedObject.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSubMenu() {
|
||||
return mWrappedObject.hasSubMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu getSubMenu() {
|
||||
return getSubMenuWrapper(mWrappedObject.getSubMenu());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
|
||||
mWrappedObject.setOnMenuItemClickListener(menuItemClickListener != null ?
|
||||
new OnMenuItemClickListenerWrapper(menuItemClickListener) : null);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContextMenu.ContextMenuInfo getMenuInfo() {
|
||||
return mWrappedObject.getMenuInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShowAsAction(int actionEnum) {
|
||||
mWrappedObject.setShowAsAction(actionEnum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setShowAsActionFlags(int actionEnum) {
|
||||
mWrappedObject.setShowAsActionFlags(actionEnum);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setActionView(View view) {
|
||||
if (view instanceof android.view.CollapsibleActionView) {
|
||||
view = new CollapsibleActionViewWrapper(view);
|
||||
}
|
||||
mWrappedObject.setActionView(view);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setActionView(int resId) {
|
||||
// Make framework menu item inflate the view
|
||||
mWrappedObject.setActionView(resId);
|
||||
|
||||
View actionView = mWrappedObject.getActionView();
|
||||
if (actionView instanceof android.view.CollapsibleActionView) {
|
||||
// If the inflated Action View is support-collapsible, wrap it
|
||||
mWrappedObject.setActionView(new CollapsibleActionViewWrapper(actionView));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getActionView() {
|
||||
View actionView = mWrappedObject.getActionView();
|
||||
if (actionView instanceof CollapsibleActionViewWrapper) {
|
||||
return ((CollapsibleActionViewWrapper) actionView).getWrappedView();
|
||||
}
|
||||
return actionView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setActionProvider(android.view.ActionProvider provider) {
|
||||
mWrappedObject.setSupportActionProvider(
|
||||
provider != null ? createActionProviderWrapper(provider) : null);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.view.ActionProvider getActionProvider() {
|
||||
ActionProvider provider = mWrappedObject.getSupportActionProvider();
|
||||
if (provider instanceof ActionProviderWrapper) {
|
||||
return ((ActionProviderWrapper) provider).mInner;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expandActionView() {
|
||||
return mWrappedObject.expandActionView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collapseActionView() {
|
||||
return mWrappedObject.collapseActionView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActionViewExpanded() {
|
||||
return mWrappedObject.isActionViewExpanded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem setOnActionExpandListener(MenuItem.OnActionExpandListener listener) {
|
||||
mWrappedObject.setSupportOnActionExpandListener(listener != null ?
|
||||
new OnActionExpandListenerWrapper(listener) : null);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setExclusiveCheckable(boolean checkable) {
|
||||
try {
|
||||
if (mSetExclusiveCheckableMethod == null) {
|
||||
mSetExclusiveCheckableMethod = mWrappedObject.getClass()
|
||||
.getDeclaredMethod("setExclusiveCheckable", Boolean.TYPE);
|
||||
}
|
||||
mSetExclusiveCheckableMethod.invoke(mWrappedObject, checkable);
|
||||
} catch (Exception e) {
|
||||
Log.w(LOG_TAG, "Error while calling setExclusiveCheckable", e);
|
||||
}
|
||||
}
|
||||
|
||||
ActionProviderWrapper createActionProviderWrapper(android.view.ActionProvider provider) {
|
||||
return new ActionProviderWrapper(mContext, provider);
|
||||
}
|
||||
|
||||
private class OnMenuItemClickListenerWrapper extends BaseWrapper<OnMenuItemClickListener>
|
||||
implements android.view.MenuItem.OnMenuItemClickListener {
|
||||
|
||||
OnMenuItemClickListenerWrapper(OnMenuItemClickListener object) {
|
||||
super(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(android.view.MenuItem item) {
|
||||
return mWrappedObject.onMenuItemClick(getMenuItemWrapper(item));
|
||||
}
|
||||
}
|
||||
|
||||
private class OnActionExpandListenerWrapper extends BaseWrapper<MenuItem.OnActionExpandListener>
|
||||
implements MenuItemCompat.OnActionExpandListener {
|
||||
|
||||
OnActionExpandListenerWrapper(MenuItem.OnActionExpandListener object) {
|
||||
super(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemActionExpand(android.view.MenuItem item) {
|
||||
return mWrappedObject.onMenuItemActionExpand(getMenuItemWrapper(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemActionCollapse(android.view.MenuItem item) {
|
||||
return mWrappedObject.onMenuItemActionCollapse(getMenuItemWrapper(item));
|
||||
}
|
||||
}
|
||||
|
||||
class ActionProviderWrapper extends android.support.v4.view.ActionProvider {
|
||||
final android.view.ActionProvider mInner;
|
||||
|
||||
public ActionProviderWrapper(Context context, android.view.ActionProvider inner) {
|
||||
super(context);
|
||||
mInner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateActionView() {
|
||||
return mInner.onCreateActionView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPerformDefaultAction() {
|
||||
return mInner.onPerformDefaultAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSubMenu() {
|
||||
return mInner.hasSubMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareSubMenu(android.view.SubMenu subMenu) {
|
||||
mInner.onPrepareSubMenu(getSubMenuWrapper(subMenu));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a support {@link android.support.v7.view.CollapsibleActionView} into a framework
|
||||
* {@link android.view.CollapsibleActionView}.
|
||||
*/
|
||||
static class CollapsibleActionViewWrapper extends FrameLayout
|
||||
implements CollapsibleActionView {
|
||||
|
||||
final android.view.CollapsibleActionView mWrappedView;
|
||||
|
||||
CollapsibleActionViewWrapper(View actionView) {
|
||||
super(actionView.getContext());
|
||||
mWrappedView = (android.view.CollapsibleActionView) actionView;
|
||||
addView(actionView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActionViewExpanded() {
|
||||
mWrappedView.onActionViewExpanded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActionViewCollapsed() {
|
||||
mWrappedView.onActionViewCollapsed();
|
||||
}
|
||||
|
||||
View getWrappedView() {
|
||||
return (View) mWrappedView;
|
||||
}
|
||||
}
|
||||
}
|
84
android/support/v7/internal/view/menu/MenuItemWrapperJB.java
Normal file
84
android/support/v7/internal/view/menu/MenuItemWrapperJB.java
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.support.v4.internal.view.SupportMenuItem;
|
||||
import android.support.v4.view.ActionProvider;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Wraps a support {@link SupportMenuItem} as a framework {@link android.view.MenuItem}
|
||||
* @hide
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
class MenuItemWrapperJB extends MenuItemWrapperICS {
|
||||
|
||||
MenuItemWrapperJB(Context context, SupportMenuItem object) {
|
||||
super(context, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
ActionProviderWrapper createActionProviderWrapper(android.view.ActionProvider provider) {
|
||||
return new ActionProviderWrapperJB(mContext, provider);
|
||||
}
|
||||
|
||||
class ActionProviderWrapperJB extends ActionProviderWrapper
|
||||
implements android.view.ActionProvider.VisibilityListener {
|
||||
ActionProvider.VisibilityListener mListener;
|
||||
|
||||
public ActionProviderWrapperJB(Context context, android.view.ActionProvider inner) {
|
||||
super(context, inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateActionView(MenuItem forItem) {
|
||||
return mInner.onCreateActionView(forItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean overridesItemVisibility() {
|
||||
return mInner.overridesItemVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return mInner.isVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshVisibility() {
|
||||
mInner.refreshVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibilityListener(ActionProvider.VisibilityListener listener) {
|
||||
mListener = listener;
|
||||
mInner.setVisibilityListener(listener != null ? this : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActionProviderVisibilityChanged(boolean isVisible) {
|
||||
if (mListener != null) {
|
||||
mListener.onActionProviderVisibilityChanged(isVisible);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
404
android/support/v7/internal/view/menu/MenuPopupHelper.java
Normal file
404
android/support/v7/internal/view/menu/MenuPopupHelper.java
Normal file
|
@ -0,0 +1,404 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.widget.ListPopupWindow;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Presents a menu as a small, simple popup anchored to another view.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener,
|
||||
ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener,
|
||||
MenuPresenter {
|
||||
|
||||
private static final String TAG = "MenuPopupHelper";
|
||||
|
||||
static final int ITEM_LAYOUT = R.layout.abc_popup_menu_item_layout;
|
||||
|
||||
private final Context mContext;
|
||||
private final LayoutInflater mInflater;
|
||||
private final MenuBuilder mMenu;
|
||||
private final MenuAdapter mAdapter;
|
||||
private final boolean mOverflowOnly;
|
||||
private final int mPopupMaxWidth;
|
||||
private final int mPopupStyleAttr;
|
||||
private final int mPopupStyleRes;
|
||||
|
||||
private View mAnchorView;
|
||||
private ListPopupWindow mPopup;
|
||||
private ViewTreeObserver mTreeObserver;
|
||||
private Callback mPresenterCallback;
|
||||
|
||||
boolean mForceShowIcon;
|
||||
|
||||
private ViewGroup mMeasureParent;
|
||||
|
||||
/** Whether the cached content width value is valid. */
|
||||
private boolean mHasContentWidth;
|
||||
|
||||
/** Cached content width from {@link #measureContentWidth}. */
|
||||
private int mContentWidth;
|
||||
|
||||
private int mDropDownGravity = Gravity.NO_GRAVITY;
|
||||
|
||||
public MenuPopupHelper(Context context, MenuBuilder menu) {
|
||||
this(context, menu, null, false, R.attr.popupMenuStyle);
|
||||
}
|
||||
|
||||
public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) {
|
||||
this(context, menu, anchorView, false, R.attr.popupMenuStyle);
|
||||
}
|
||||
|
||||
public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView,
|
||||
boolean overflowOnly, int popupStyleAttr) {
|
||||
this(context, menu, anchorView, overflowOnly, popupStyleAttr, 0);
|
||||
}
|
||||
|
||||
public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView,
|
||||
boolean overflowOnly, int popupStyleAttr, int popupStyleRes) {
|
||||
mContext = context;
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mMenu = menu;
|
||||
mAdapter = new MenuAdapter(mMenu);
|
||||
mOverflowOnly = overflowOnly;
|
||||
mPopupStyleAttr = popupStyleAttr;
|
||||
mPopupStyleRes = popupStyleRes;
|
||||
|
||||
final Resources res = context.getResources();
|
||||
mPopupMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2,
|
||||
res.getDimensionPixelSize(R.dimen.abc_config_prefDialogWidth));
|
||||
|
||||
mAnchorView = anchorView;
|
||||
|
||||
// Present the menu using our context, not the menu builder's context.
|
||||
menu.addMenuPresenter(this, context);
|
||||
}
|
||||
|
||||
public void setAnchorView(View anchor) {
|
||||
mAnchorView = anchor;
|
||||
}
|
||||
|
||||
public void setForceShowIcon(boolean forceShow) {
|
||||
mForceShowIcon = forceShow;
|
||||
}
|
||||
|
||||
public void setGravity(int gravity) {
|
||||
mDropDownGravity = gravity;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if (!tryShow()) {
|
||||
throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
|
||||
}
|
||||
}
|
||||
|
||||
public ListPopupWindow getPopup() {
|
||||
return mPopup;
|
||||
}
|
||||
|
||||
public boolean tryShow() {
|
||||
mPopup = new ListPopupWindow(mContext, null, mPopupStyleAttr, mPopupStyleRes);
|
||||
mPopup.setOnDismissListener(this);
|
||||
mPopup.setOnItemClickListener(this);
|
||||
mPopup.setAdapter(mAdapter);
|
||||
mPopup.setModal(true);
|
||||
|
||||
View anchor = mAnchorView;
|
||||
if (anchor != null) {
|
||||
final boolean addGlobalListener = mTreeObserver == null;
|
||||
mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
|
||||
if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this);
|
||||
mPopup.setAnchorView(anchor);
|
||||
mPopup.setDropDownGravity(mDropDownGravity);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mHasContentWidth) {
|
||||
mContentWidth = measureContentWidth();
|
||||
mHasContentWidth = true;
|
||||
}
|
||||
|
||||
mPopup.setContentWidth(mContentWidth);
|
||||
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
|
||||
mPopup.show();
|
||||
mPopup.getListView().setOnKeyListener(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
if (isShowing()) {
|
||||
mPopup.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
public void onDismiss() {
|
||||
mPopup = null;
|
||||
mMenu.close();
|
||||
if (mTreeObserver != null) {
|
||||
if (!mTreeObserver.isAlive()) mTreeObserver = mAnchorView.getViewTreeObserver();
|
||||
mTreeObserver.removeGlobalOnLayoutListener(this);
|
||||
mTreeObserver = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isShowing() {
|
||||
return mPopup != null && mPopup.isShowing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
MenuAdapter adapter = mAdapter;
|
||||
adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0);
|
||||
}
|
||||
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) {
|
||||
dismiss();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int measureContentWidth() {
|
||||
// Menus don't tend to be long, so this is more sane than it looks.
|
||||
int maxWidth = 0;
|
||||
View itemView = null;
|
||||
int itemType = 0;
|
||||
|
||||
final ListAdapter adapter = mAdapter;
|
||||
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
final int count = adapter.getCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int positionType = adapter.getItemViewType(i);
|
||||
if (positionType != itemType) {
|
||||
itemType = positionType;
|
||||
itemView = null;
|
||||
}
|
||||
|
||||
if (mMeasureParent == null) {
|
||||
mMeasureParent = new FrameLayout(mContext);
|
||||
}
|
||||
|
||||
itemView = adapter.getView(i, itemView, mMeasureParent);
|
||||
itemView.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
final int itemWidth = itemView.getMeasuredWidth();
|
||||
if (itemWidth >= mPopupMaxWidth) {
|
||||
return mPopupMaxWidth;
|
||||
} else if (itemWidth > maxWidth) {
|
||||
maxWidth = itemWidth;
|
||||
}
|
||||
}
|
||||
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
if (isShowing()) {
|
||||
final View anchor = mAnchorView;
|
||||
if (anchor == null || !anchor.isShown()) {
|
||||
dismiss();
|
||||
} else if (isShowing()) {
|
||||
// Recompute window size and position
|
||||
mPopup.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initForMenu(Context context, MenuBuilder menu) {
|
||||
// Don't need to do anything; we added as a presenter in the constructor.
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuView getMenuView(ViewGroup root) {
|
||||
throw new UnsupportedOperationException("MenuPopupHelpers manage their own views");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMenuView(boolean cleared) {
|
||||
mHasContentWidth = false;
|
||||
|
||||
if (mAdapter != null) {
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCallback(Callback cb) {
|
||||
mPresenterCallback = cb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
|
||||
if (subMenu.hasVisibleItems()) {
|
||||
MenuPopupHelper subPopup = new MenuPopupHelper(mContext, subMenu, mAnchorView);
|
||||
subPopup.setCallback(mPresenterCallback);
|
||||
|
||||
boolean preserveIconSpacing = false;
|
||||
final int count = subMenu.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
MenuItem childItem = subMenu.getItem(i);
|
||||
if (childItem.isVisible() && childItem.getIcon() != null) {
|
||||
preserveIconSpacing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
subPopup.setForceShowIcon(preserveIconSpacing);
|
||||
|
||||
if (subPopup.tryShow()) {
|
||||
if (mPresenterCallback != null) {
|
||||
mPresenterCallback.onOpenSubMenu(subMenu);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
||||
// Only care about the (sub)menu we're presenting.
|
||||
if (menu != mMenu) return;
|
||||
|
||||
dismiss();
|
||||
if (mPresenterCallback != null) {
|
||||
mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean flagActionItems() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
}
|
||||
|
||||
private class MenuAdapter extends BaseAdapter {
|
||||
private MenuBuilder mAdapterMenu;
|
||||
private int mExpandedIndex = -1;
|
||||
|
||||
public MenuAdapter(MenuBuilder menu) {
|
||||
mAdapterMenu = menu;
|
||||
findExpandedIndex();
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
ArrayList<MenuItemImpl> items = mOverflowOnly ?
|
||||
mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
|
||||
if (mExpandedIndex < 0) {
|
||||
return items.size();
|
||||
}
|
||||
return items.size() - 1;
|
||||
}
|
||||
|
||||
public MenuItemImpl getItem(int position) {
|
||||
ArrayList<MenuItemImpl> items = mOverflowOnly ?
|
||||
mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
|
||||
if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
|
||||
position++;
|
||||
}
|
||||
return items.get(position);
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
// Since a menu item's ID is optional, we'll use the position as an
|
||||
// ID for the item in the AdapterView
|
||||
return position;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = mInflater.inflate(ITEM_LAYOUT, parent, false);
|
||||
}
|
||||
|
||||
MenuView.ItemView itemView = (MenuView.ItemView) convertView;
|
||||
if (mForceShowIcon) {
|
||||
((ListMenuItemView) convertView).setForceShowIcon(true);
|
||||
}
|
||||
itemView.initialize(getItem(position), 0);
|
||||
return convertView;
|
||||
}
|
||||
|
||||
void findExpandedIndex() {
|
||||
final MenuItemImpl expandedItem = mMenu.getExpandedItem();
|
||||
if (expandedItem != null) {
|
||||
final ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
|
||||
final int count = items.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final MenuItemImpl item = items.get(i);
|
||||
if (item == expandedItem) {
|
||||
mExpandedIndex = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
mExpandedIndex = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDataSetChanged() {
|
||||
findExpandedIndex();
|
||||
super.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
153
android/support/v7/internal/view/menu/MenuPresenter.java
Normal file
153
android/support/v7/internal/view/menu/MenuPresenter.java
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (C) 2011 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcelable;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* A MenuPresenter is responsible for building views for a Menu object. It takes over some
|
||||
* responsibility from the old style monolithic MenuBuilder class.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public interface MenuPresenter {
|
||||
|
||||
/**
|
||||
* Called by menu implementation to notify another component of open/close events.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public interface Callback {
|
||||
/**
|
||||
* Called when a menu is closing.
|
||||
* @param menu
|
||||
* @param allMenusAreClosing
|
||||
*/
|
||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
|
||||
|
||||
/**
|
||||
* Called when a submenu opens. Useful for notifying the application
|
||||
* of menu state so that it does not attempt to hide the action bar
|
||||
* while a submenu is open or similar.
|
||||
*
|
||||
* @param subMenu Submenu currently being opened
|
||||
* @return true if the Callback will handle presenting the submenu, false if
|
||||
* the presenter should attempt to do so.
|
||||
*/
|
||||
public boolean onOpenSubMenu(MenuBuilder subMenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this presenter for the given context and menu.
|
||||
* This method is called by MenuBuilder when a presenter is
|
||||
* added. See {@link MenuBuilder#addMenuPresenter(MenuPresenter)}
|
||||
*
|
||||
* @param context Context for this presenter; used for view creation and resource management
|
||||
* @param menu Menu to host
|
||||
*/
|
||||
public void initForMenu(Context context, MenuBuilder menu);
|
||||
|
||||
/**
|
||||
* Retrieve a MenuView to display the menu specified in
|
||||
* {@link #initForMenu(Context, MenuBuilder)}.
|
||||
*
|
||||
* @param root Intended parent of the MenuView.
|
||||
* @return A freshly created MenuView.
|
||||
*/
|
||||
public MenuView getMenuView(ViewGroup root);
|
||||
|
||||
/**
|
||||
* Update the menu UI in response to a change. Called by
|
||||
* MenuBuilder during the normal course of operation.
|
||||
*
|
||||
* @param cleared true if the menu was entirely cleared
|
||||
*/
|
||||
public void updateMenuView(boolean cleared);
|
||||
|
||||
/**
|
||||
* Set a callback object that will be notified of menu events
|
||||
* related to this specific presentation.
|
||||
* @param cb Callback that will be notified of future events
|
||||
*/
|
||||
public void setCallback(Callback cb);
|
||||
|
||||
/**
|
||||
* Called by Menu implementations to indicate that a submenu item
|
||||
* has been selected. An active Callback should be notified, and
|
||||
* if applicable the presenter should present the submenu.
|
||||
*
|
||||
* @param subMenu SubMenu being opened
|
||||
* @return true if the the event was handled, false otherwise.
|
||||
*/
|
||||
public boolean onSubMenuSelected(SubMenuBuilder subMenu);
|
||||
|
||||
/**
|
||||
* Called by Menu implementations to indicate that a menu or submenu is
|
||||
* closing. Presenter implementations should close the representation
|
||||
* of the menu indicated as necessary and notify a registered callback.
|
||||
*
|
||||
* @param menu Menu or submenu that is closing.
|
||||
* @param allMenusAreClosing True if all associated menus are closing.
|
||||
*/
|
||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
|
||||
|
||||
/**
|
||||
* Called by Menu implementations to flag items that will be shown as actions.
|
||||
* @return true if this presenter changed the action status of any items.
|
||||
*/
|
||||
public boolean flagActionItems();
|
||||
|
||||
/**
|
||||
* Called when a menu item with a collapsable action view should expand its action view.
|
||||
*
|
||||
* @param menu Menu containing the item to be expanded
|
||||
* @param item Item to be expanded
|
||||
* @return true if this presenter expanded the action view, false otherwise.
|
||||
*/
|
||||
public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item);
|
||||
|
||||
/**
|
||||
* Called when a menu item with a collapsable action view should collapse its action view.
|
||||
*
|
||||
* @param menu Menu containing the item to be collapsed
|
||||
* @param item Item to be collapsed
|
||||
* @return true if this presenter collapsed the action view, false otherwise.
|
||||
*/
|
||||
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item);
|
||||
|
||||
/**
|
||||
* Returns an ID for determining how to save/restore instance state.
|
||||
* @return a valid ID value.
|
||||
*/
|
||||
public int getId();
|
||||
|
||||
/**
|
||||
* Returns a Parcelable describing the current state of the presenter.
|
||||
* It will be passed to the {@link #onRestoreInstanceState(Parcelable)}
|
||||
* method of the presenter sharing the same ID later.
|
||||
* @return The saved instance state
|
||||
*/
|
||||
public Parcelable onSaveInstanceState();
|
||||
|
||||
/**
|
||||
* Supplies the previously saved instance state to be restored.
|
||||
* @param state The previously saved instance state
|
||||
*/
|
||||
public void onRestoreInstanceState(Parcelable state);
|
||||
}
|
120
android/support/v7/internal/view/menu/MenuView.java
Normal file
120
android/support/v7/internal/view/menu/MenuView.java
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
/**
|
||||
* Minimal interface for a menu view. {@link #initialize(MenuBuilder)} must be called for the
|
||||
* menu to be functional.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public interface MenuView {
|
||||
/**
|
||||
* Initializes the menu to the given menu. This should be called after the
|
||||
* view is inflated.
|
||||
*
|
||||
* @param menu The menu that this MenuView should display.
|
||||
*/
|
||||
public void initialize(MenuBuilder menu);
|
||||
|
||||
/**
|
||||
* Returns the default animations to be used for this menu when entering/exiting.
|
||||
* @return A resource ID for the default animations to be used for this menu.
|
||||
*/
|
||||
public int getWindowAnimations();
|
||||
|
||||
/**
|
||||
* Minimal interface for a menu item view. {@link #initialize(MenuItemImpl, int)} must be called
|
||||
* for the item to be functional.
|
||||
*/
|
||||
public interface ItemView {
|
||||
/**
|
||||
* Initializes with the provided MenuItemData. This should be called after the view is
|
||||
* inflated.
|
||||
* @param itemData The item that this ItemView should display.
|
||||
* @param menuType The type of this menu, one of
|
||||
* {@link MenuBuilder#TYPE_ICON}, {@link MenuBuilder#TYPE_EXPANDED},
|
||||
* {@link MenuBuilder#TYPE_DIALOG}).
|
||||
*/
|
||||
public void initialize(MenuItemImpl itemData, int menuType);
|
||||
|
||||
/**
|
||||
* Gets the item data that this view is displaying.
|
||||
* @return the item data, or null if there is not one
|
||||
*/
|
||||
public MenuItemImpl getItemData();
|
||||
|
||||
/**
|
||||
* Sets the title of the item view.
|
||||
* @param title The title to set.
|
||||
*/
|
||||
public void setTitle(CharSequence title);
|
||||
|
||||
/**
|
||||
* Sets the enabled state of the item view.
|
||||
* @param enabled Whether the item view should be enabled.
|
||||
*/
|
||||
public void setEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Displays the checkbox for the item view. This does not ensure the item view will be
|
||||
* checked, for that use {@link #setChecked}.
|
||||
* @param checkable Whether to display the checkbox or to hide it
|
||||
*/
|
||||
public void setCheckable(boolean checkable);
|
||||
|
||||
/**
|
||||
* Checks the checkbox for the item view. If the checkbox is hidden, it will NOT be
|
||||
* made visible, call {@link #setCheckable(boolean)} for that.
|
||||
* @param checked Whether the checkbox should be checked
|
||||
*/
|
||||
public void setChecked(boolean checked);
|
||||
|
||||
/**
|
||||
* Sets the shortcut for the item.
|
||||
* @param showShortcut Whether a shortcut should be shown(if false, the value of
|
||||
* shortcutKey should be ignored).
|
||||
* @param shortcutKey The shortcut key that should be shown on the ItemView.
|
||||
*/
|
||||
public void setShortcut(boolean showShortcut, char shortcutKey);
|
||||
|
||||
/**
|
||||
* Set the icon of this item view.
|
||||
* @param icon The icon of this item. null to hide the icon.
|
||||
*/
|
||||
public void setIcon(Drawable icon);
|
||||
|
||||
/**
|
||||
* Whether this item view prefers displaying the condensed title rather
|
||||
* than the normal title. If a condensed title is not available, the
|
||||
* normal title will be used.
|
||||
*
|
||||
* @return Whether this item view prefers displaying the condensed
|
||||
* title.
|
||||
*/
|
||||
public boolean prefersCondensedTitle();
|
||||
|
||||
/**
|
||||
* Whether this item view shows an icon.
|
||||
*
|
||||
* @return Whether this item view shows an icon.
|
||||
*/
|
||||
public boolean showsIcon();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) 2012 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.support.v4.internal.view.SupportMenu;
|
||||
import android.support.v4.internal.view.SupportMenuItem;
|
||||
import android.support.v4.internal.view.SupportSubMenu;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SubMenu;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final class MenuWrapperFactory {
|
||||
private MenuWrapperFactory() {
|
||||
}
|
||||
|
||||
public static Menu wrapSupportMenu(Context context, SupportMenu supportMenu) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
return new MenuWrapperICS(context, supportMenu);
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static MenuItem wrapSupportMenuItem(Context context, SupportMenuItem supportMenuItem) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
return new MenuItemWrapperJB(context, supportMenuItem);
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
return new MenuItemWrapperICS(context, supportMenuItem);
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static SubMenu wrapSupportSubMenu(Context context, SupportSubMenu supportSubMenu) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
return new SubMenuWrapperICS(context, supportSubMenu);
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
177
android/support/v7/internal/view/menu/MenuWrapperICS.java
Normal file
177
android/support/v7/internal/view/menu/MenuWrapperICS.java
Normal file
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Copyright (C) 2012 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.v4.internal.view.SupportMenu;
|
||||
import android.support.v4.internal.view.SupportMenuItem;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SubMenu;
|
||||
|
||||
/**
|
||||
* Wraps a support {@link SupportMenu} as a framework {@link android.view.Menu}
|
||||
* @hide
|
||||
*/
|
||||
class MenuWrapperICS extends BaseMenuWrapper<SupportMenu> implements Menu {
|
||||
|
||||
MenuWrapperICS(Context context, SupportMenu object) {
|
||||
super(context, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem add(CharSequence title) {
|
||||
return getMenuItemWrapper(mWrappedObject.add(title));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem add(int titleRes) {
|
||||
return getMenuItemWrapper(mWrappedObject.add(titleRes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
|
||||
return getMenuItemWrapper(mWrappedObject.add(groupId, itemId, order, title));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem add(int groupId, int itemId, int order, int titleRes) {
|
||||
return getMenuItemWrapper(mWrappedObject.add(groupId, itemId, order, titleRes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu addSubMenu(CharSequence title) {
|
||||
return getSubMenuWrapper(mWrappedObject.addSubMenu(title));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu addSubMenu(int titleRes) {
|
||||
return getSubMenuWrapper(mWrappedObject.addSubMenu(titleRes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) {
|
||||
return getSubMenuWrapper(mWrappedObject.addSubMenu(groupId, itemId, order, title));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
|
||||
return getSubMenuWrapper(
|
||||
mWrappedObject.addSubMenu(groupId, itemId, order, titleRes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addIntentOptions(int groupId, int itemId, int order, ComponentName caller,
|
||||
Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) {
|
||||
android.view.MenuItem[] items = null;
|
||||
if (outSpecificItems != null) {
|
||||
items = new android.view.MenuItem[outSpecificItems.length];
|
||||
}
|
||||
|
||||
int result = mWrappedObject
|
||||
.addIntentOptions(groupId, itemId, order, caller, specifics, intent, flags, items);
|
||||
|
||||
if (items != null) {
|
||||
for (int i = 0, z = items.length; i < z; i++) {
|
||||
outSpecificItems[i] = getMenuItemWrapper(items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeItem(int id) {
|
||||
internalRemoveItem(id);
|
||||
mWrappedObject.removeItem(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeGroup(int groupId) {
|
||||
internalRemoveGroup(groupId);
|
||||
mWrappedObject.removeGroup(groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
internalClear();
|
||||
mWrappedObject.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupCheckable(int group, boolean checkable, boolean exclusive) {
|
||||
mWrappedObject.setGroupCheckable(group, checkable, exclusive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupVisible(int group, boolean visible) {
|
||||
mWrappedObject.setGroupVisible(group, visible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroupEnabled(int group, boolean enabled) {
|
||||
mWrappedObject.setGroupEnabled(group, enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasVisibleItems() {
|
||||
return mWrappedObject.hasVisibleItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem findItem(int id) {
|
||||
return getMenuItemWrapper(mWrappedObject.findItem(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return mWrappedObject.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem getItem(int index) {
|
||||
return getMenuItemWrapper(mWrappedObject.getItem(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mWrappedObject.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
|
||||
return mWrappedObject.performShortcut(keyCode, event, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShortcutKey(int keyCode, KeyEvent event) {
|
||||
return mWrappedObject.isShortcutKey(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performIdentifierAction(int id, int flags) {
|
||||
return mWrappedObject.performIdentifierAction(id, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQwertyMode(boolean isQwerty) {
|
||||
mWrappedObject.setQwertyMode(isQwerty);
|
||||
}
|
||||
}
|
141
android/support/v7/internal/view/menu/SubMenuBuilder.java
Normal file
141
android/support/v7/internal/view/menu/SubMenuBuilder.java
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* The model for a sub menu, which is an extension of the menu. Most methods are proxied to the
|
||||
* parent menu.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class SubMenuBuilder extends MenuBuilder implements SubMenu {
|
||||
private MenuBuilder mParentMenu;
|
||||
private MenuItemImpl mItem;
|
||||
|
||||
public SubMenuBuilder(Context context, MenuBuilder parentMenu, MenuItemImpl item) {
|
||||
super(context);
|
||||
|
||||
mParentMenu = parentMenu;
|
||||
mItem = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQwertyMode(boolean isQwerty) {
|
||||
mParentMenu.setQwertyMode(isQwerty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isQwertyMode() {
|
||||
return mParentMenu.isQwertyMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShortcutsVisible(boolean shortcutsVisible) {
|
||||
mParentMenu.setShortcutsVisible(shortcutsVisible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShortcutsVisible() {
|
||||
return mParentMenu.isShortcutsVisible();
|
||||
}
|
||||
|
||||
public Menu getParentMenu() {
|
||||
return mParentMenu;
|
||||
}
|
||||
|
||||
public MenuItem getItem() {
|
||||
return mItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCallback(Callback callback) {
|
||||
mParentMenu.setCallback(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuBuilder getRootMenu() {
|
||||
return mParentMenu;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
|
||||
return super.dispatchMenuItemSelected(menu, item) ||
|
||||
mParentMenu.dispatchMenuItemSelected(menu, item);
|
||||
}
|
||||
|
||||
public SubMenu setIcon(Drawable icon) {
|
||||
mItem.setIcon(icon);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SubMenu setIcon(int iconRes) {
|
||||
mItem.setIcon(iconRes);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SubMenu setHeaderIcon(Drawable icon) {
|
||||
super.setHeaderIconInt(icon);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SubMenu setHeaderIcon(int iconRes) {
|
||||
super.setHeaderIconInt(ContextCompat.getDrawable(getContext(), iconRes));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SubMenu setHeaderTitle(CharSequence title) {
|
||||
super.setHeaderTitleInt(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SubMenu setHeaderTitle(int titleRes) {
|
||||
super.setHeaderTitleInt(getContext().getResources().getString(titleRes));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SubMenu setHeaderView(View view) {
|
||||
super.setHeaderViewInt(view);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expandItemActionView(MenuItemImpl item) {
|
||||
return mParentMenu.expandItemActionView(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collapseItemActionView(MenuItemImpl item) {
|
||||
return mParentMenu.collapseItemActionView(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActionViewStatesKey() {
|
||||
final int itemId = mItem != null ? mItem.getItemId() : 0;
|
||||
if (itemId == 0) {
|
||||
return null;
|
||||
}
|
||||
return super.getActionViewStatesKey() + ":" + itemId;
|
||||
}
|
||||
}
|
92
android/support/v7/internal/view/menu/SubMenuWrapperICS.java
Normal file
92
android/support/v7/internal/view/menu/SubMenuWrapperICS.java
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (C) 2012 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.support.v7.internal.view.menu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.internal.view.SupportSubMenu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Wraps a support {@link SupportSubMenu} as a framework {@link android.view.SubMenu}
|
||||
* @hide
|
||||
*/
|
||||
class SubMenuWrapperICS extends MenuWrapperICS implements SubMenu {
|
||||
|
||||
SubMenuWrapperICS(Context context, SupportSubMenu subMenu) {
|
||||
super(context, subMenu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportSubMenu getWrappedObject() {
|
||||
return (SupportSubMenu) mWrappedObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu setHeaderTitle(int titleRes) {
|
||||
getWrappedObject().setHeaderTitle(titleRes);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu setHeaderTitle(CharSequence title) {
|
||||
getWrappedObject().setHeaderTitle(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu setHeaderIcon(int iconRes) {
|
||||
getWrappedObject().setHeaderIcon(iconRes);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu setHeaderIcon(Drawable icon) {
|
||||
getWrappedObject().setHeaderIcon(icon);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu setHeaderView(View view) {
|
||||
getWrappedObject().setHeaderView(view);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearHeader() {
|
||||
getWrappedObject().clearHeader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu setIcon(int iconRes) {
|
||||
getWrappedObject().setIcon(iconRes);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu setIcon(Drawable icon) {
|
||||
getWrappedObject().setIcon(icon);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItem getItem() {
|
||||
return getMenuItemWrapper(getWrappedObject().getItem());
|
||||
}
|
||||
}
|
288
android/support/v7/internal/widget/AbsActionBarView.java
Normal file
288
android/support/v7/internal/widget/AbsActionBarView.java
Normal file
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* Copyright (C) 2011 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Build;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.view.ViewPropertyAnimatorCompat;
|
||||
import android.support.v4.view.ViewPropertyAnimatorListener;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.internal.view.ViewPropertyAnimatorCompatSet;
|
||||
import android.support.v7.widget.ActionMenuPresenter;
|
||||
import android.support.v7.widget.ActionMenuView;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
abstract class AbsActionBarView extends ViewGroup {
|
||||
private static final Interpolator sAlphaInterpolator = new DecelerateInterpolator();
|
||||
|
||||
private static final int FADE_DURATION = 200;
|
||||
|
||||
protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
|
||||
|
||||
/** Context against which to inflate popup menus. */
|
||||
protected final Context mPopupContext;
|
||||
|
||||
protected ActionMenuView mMenuView;
|
||||
protected ActionMenuPresenter mActionMenuPresenter;
|
||||
protected ViewGroup mSplitView;
|
||||
protected boolean mSplitActionBar;
|
||||
protected boolean mSplitWhenNarrow;
|
||||
protected int mContentHeight;
|
||||
|
||||
protected ViewPropertyAnimatorCompat mVisibilityAnim;
|
||||
|
||||
AbsActionBarView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
AbsActionBarView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
AbsActionBarView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
final TypedValue tv = new TypedValue();
|
||||
if (context.getTheme().resolveAttribute(R.attr.actionBarPopupTheme, tv, true)
|
||||
&& tv.resourceId != 0) {
|
||||
mPopupContext = new ContextThemeWrapper(context, tv.resourceId);
|
||||
} else {
|
||||
mPopupContext = context;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
if (Build.VERSION.SDK_INT >= 8) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
// Action bar can change size on configuration changes.
|
||||
// Reread the desired height from the theme-specified style.
|
||||
TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.ActionBar,
|
||||
R.attr.actionBarStyle, 0);
|
||||
setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0));
|
||||
a.recycle();
|
||||
|
||||
if (mActionMenuPresenter != null) {
|
||||
mActionMenuPresenter.onConfigurationChanged(newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the bar should be split right now, no questions asked.
|
||||
* @param split true if the bar should split
|
||||
*/
|
||||
public void setSplitToolbar(boolean split) {
|
||||
mSplitActionBar = split;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the bar should split if we enter a narrow screen configuration.
|
||||
* @param splitWhenNarrow true if the bar should check to split after a config change
|
||||
*/
|
||||
public void setSplitWhenNarrow(boolean splitWhenNarrow) {
|
||||
mSplitWhenNarrow = splitWhenNarrow;
|
||||
}
|
||||
|
||||
public void setContentHeight(int height) {
|
||||
mContentHeight = height;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public int getContentHeight() {
|
||||
return mContentHeight;
|
||||
}
|
||||
|
||||
public void setSplitView(ViewGroup splitView) {
|
||||
mSplitView = splitView;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current visibility or if animating, the visibility being animated to.
|
||||
*/
|
||||
public int getAnimatedVisibility() {
|
||||
if (mVisibilityAnim != null) {
|
||||
return mVisAnimListener.mFinalVisibility;
|
||||
}
|
||||
return getVisibility();
|
||||
}
|
||||
|
||||
public void animateToVisibility(int visibility) {
|
||||
if (mVisibilityAnim != null) {
|
||||
mVisibilityAnim.cancel();
|
||||
}
|
||||
if (visibility == VISIBLE) {
|
||||
if (getVisibility() != VISIBLE) {
|
||||
ViewCompat.setAlpha(this, 0f);
|
||||
if (mSplitView != null && mMenuView != null) {
|
||||
ViewCompat.setAlpha(mMenuView, 0f);
|
||||
}
|
||||
}
|
||||
ViewPropertyAnimatorCompat anim = ViewCompat.animate(this).alpha(1f);
|
||||
anim.setDuration(FADE_DURATION);
|
||||
anim.setInterpolator(sAlphaInterpolator);
|
||||
if (mSplitView != null && mMenuView != null) {
|
||||
ViewPropertyAnimatorCompatSet set = new ViewPropertyAnimatorCompatSet();
|
||||
ViewPropertyAnimatorCompat splitAnim = ViewCompat.animate(mMenuView).alpha(1f);
|
||||
splitAnim.setDuration(FADE_DURATION);
|
||||
set.setListener(mVisAnimListener.withFinalVisibility(anim, visibility));
|
||||
set.play(anim).play(splitAnim);
|
||||
set.start();
|
||||
} else {
|
||||
anim.setListener(mVisAnimListener.withFinalVisibility(anim, visibility));
|
||||
anim.start();
|
||||
}
|
||||
} else {
|
||||
ViewPropertyAnimatorCompat anim = ViewCompat.animate(this).alpha(0f);
|
||||
anim.setDuration(FADE_DURATION);
|
||||
anim.setInterpolator(sAlphaInterpolator);
|
||||
if (mSplitView != null && mMenuView != null) {
|
||||
ViewPropertyAnimatorCompatSet set = new ViewPropertyAnimatorCompatSet();
|
||||
ViewPropertyAnimatorCompat splitAnim = ViewCompat.animate(mMenuView).alpha(0f);
|
||||
splitAnim.setDuration(FADE_DURATION);
|
||||
set.setListener(mVisAnimListener.withFinalVisibility(anim, visibility));
|
||||
set.play(anim).play(splitAnim);
|
||||
set.start();
|
||||
} else {
|
||||
anim.setListener(mVisAnimListener.withFinalVisibility(anim, visibility));
|
||||
anim.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean showOverflowMenu() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
return mActionMenuPresenter.showOverflowMenu();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void postShowOverflowMenu() {
|
||||
post(new Runnable() {
|
||||
public void run() {
|
||||
showOverflowMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean hideOverflowMenu() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
return mActionMenuPresenter.hideOverflowMenu();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isOverflowMenuShowing() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
return mActionMenuPresenter.isOverflowMenuShowing();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isOverflowMenuShowPending() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
return mActionMenuPresenter.isOverflowMenuShowPending();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isOverflowReserved() {
|
||||
return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
|
||||
}
|
||||
|
||||
public boolean canShowOverflowMenu() {
|
||||
return isOverflowReserved() && getVisibility() == VISIBLE;
|
||||
}
|
||||
|
||||
public void dismissPopupMenus() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
mActionMenuPresenter.dismissPopupMenus();
|
||||
}
|
||||
}
|
||||
|
||||
protected int measureChildView(View child, int availableWidth, int childSpecHeight,
|
||||
int spacing) {
|
||||
child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
|
||||
childSpecHeight);
|
||||
|
||||
availableWidth -= child.getMeasuredWidth();
|
||||
availableWidth -= spacing;
|
||||
|
||||
return Math.max(0, availableWidth);
|
||||
}
|
||||
|
||||
static protected int next(int x, int val, boolean isRtl) {
|
||||
return isRtl ? x - val : x + val;
|
||||
}
|
||||
|
||||
protected int positionChild(View child, int x, int y, int contentHeight, boolean reverse) {
|
||||
int childWidth = child.getMeasuredWidth();
|
||||
int childHeight = child.getMeasuredHeight();
|
||||
int childTop = y + (contentHeight - childHeight) / 2;
|
||||
|
||||
if (reverse) {
|
||||
child.layout(x - childWidth, childTop, x, childTop + childHeight);
|
||||
} else {
|
||||
child.layout(x, childTop, x + childWidth, childTop + childHeight);
|
||||
}
|
||||
|
||||
return (reverse ? -childWidth : childWidth);
|
||||
}
|
||||
|
||||
protected class VisibilityAnimListener implements ViewPropertyAnimatorListener {
|
||||
private boolean mCanceled = false;
|
||||
int mFinalVisibility;
|
||||
|
||||
public VisibilityAnimListener withFinalVisibility(ViewPropertyAnimatorCompat animation,
|
||||
int visibility) {
|
||||
mVisibilityAnim = animation;
|
||||
mFinalVisibility = visibility;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(View view) {
|
||||
setVisibility(VISIBLE);
|
||||
mCanceled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(View view) {
|
||||
if (mCanceled) return;
|
||||
|
||||
mVisibilityAnim = null;
|
||||
setVisibility(mFinalVisibility);
|
||||
if (mSplitView != null && mMenuView != null) {
|
||||
mMenuView.setVisibility(mFinalVisibility);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(View view) {
|
||||
mCanceled = true;
|
||||
}
|
||||
}
|
||||
}
|
451
android/support/v7/internal/widget/AbsSpinnerCompat.java
Normal file
451
android/support/v7/internal/widget/AbsSpinnerCompat.java
Normal file
|
@ -0,0 +1,451 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.DataSetObserver;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.SpinnerAdapter;
|
||||
|
||||
/**
|
||||
* An abstract base class for spinner widgets. SDK users will probably not
|
||||
* need to use this class.
|
||||
*/
|
||||
abstract class AbsSpinnerCompat extends AdapterViewCompat<SpinnerAdapter> {
|
||||
SpinnerAdapter mAdapter;
|
||||
|
||||
int mHeightMeasureSpec;
|
||||
int mWidthMeasureSpec;
|
||||
|
||||
int mSelectionLeftPadding = 0;
|
||||
int mSelectionTopPadding = 0;
|
||||
int mSelectionRightPadding = 0;
|
||||
int mSelectionBottomPadding = 0;
|
||||
final Rect mSpinnerPadding = new Rect();
|
||||
|
||||
final RecycleBin mRecycler = new RecycleBin();
|
||||
private DataSetObserver mDataSetObserver;
|
||||
|
||||
/** Temporary frame to hold a child View's frame rectangle */
|
||||
private Rect mTouchFrame;
|
||||
|
||||
AbsSpinnerCompat(Context context) {
|
||||
super(context);
|
||||
initAbsSpinner();
|
||||
}
|
||||
|
||||
AbsSpinnerCompat(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
AbsSpinnerCompat(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
initAbsSpinner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Common code for different constructor flavors
|
||||
*/
|
||||
private void initAbsSpinner() {
|
||||
setFocusable(true);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Adapter is used to provide the data which backs this Spinner.
|
||||
* It also provides methods to transform spinner items based on their position
|
||||
* relative to the selected item.
|
||||
* @param adapter The SpinnerAdapter to use for this Spinner
|
||||
*/
|
||||
@Override
|
||||
public void setAdapter(SpinnerAdapter adapter) {
|
||||
if (null != mAdapter) {
|
||||
mAdapter.unregisterDataSetObserver(mDataSetObserver);
|
||||
resetList();
|
||||
}
|
||||
|
||||
mAdapter = adapter;
|
||||
|
||||
mOldSelectedPosition = INVALID_POSITION;
|
||||
mOldSelectedRowId = INVALID_ROW_ID;
|
||||
|
||||
if (mAdapter != null) {
|
||||
mOldItemCount = mItemCount;
|
||||
mItemCount = mAdapter.getCount();
|
||||
checkFocus();
|
||||
|
||||
mDataSetObserver = new AdapterDataSetObserver();
|
||||
mAdapter.registerDataSetObserver(mDataSetObserver);
|
||||
|
||||
int position = mItemCount > 0 ? 0 : INVALID_POSITION;
|
||||
|
||||
setSelectedPositionInt(position);
|
||||
setNextSelectedPositionInt(position);
|
||||
|
||||
if (mItemCount == 0) {
|
||||
// Nothing selected
|
||||
checkSelectionChanged();
|
||||
}
|
||||
|
||||
} else {
|
||||
checkFocus();
|
||||
resetList();
|
||||
// Nothing selected
|
||||
checkSelectionChanged();
|
||||
}
|
||||
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear out all children from the list
|
||||
*/
|
||||
void resetList() {
|
||||
mDataChanged = false;
|
||||
mNeedSync = false;
|
||||
|
||||
removeAllViewsInLayout();
|
||||
mOldSelectedPosition = INVALID_POSITION;
|
||||
mOldSelectedRowId = INVALID_ROW_ID;
|
||||
|
||||
setSelectedPositionInt(INVALID_POSITION);
|
||||
setNextSelectedPositionInt(INVALID_POSITION);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see android.view.View#measure(int, int)
|
||||
*
|
||||
* Figure out the dimensions of this Spinner. The width comes from
|
||||
* the widthMeasureSpec as Spinnners can't have their width set to
|
||||
* UNSPECIFIED. The height is based on the height of the selected item
|
||||
* plus padding.
|
||||
*/
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
int widthSize;
|
||||
int heightSize;
|
||||
|
||||
final int paddingLeft = getPaddingLeft();
|
||||
final int paddingTop = getPaddingTop();
|
||||
final int paddingRight = getPaddingRight();
|
||||
final int paddingBottom = getPaddingBottom();
|
||||
|
||||
mSpinnerPadding.left = paddingLeft > mSelectionLeftPadding ? paddingLeft
|
||||
: mSelectionLeftPadding;
|
||||
mSpinnerPadding.top = paddingTop > mSelectionTopPadding ? paddingTop
|
||||
: mSelectionTopPadding;
|
||||
mSpinnerPadding.right = paddingRight > mSelectionRightPadding ? paddingRight
|
||||
: mSelectionRightPadding;
|
||||
mSpinnerPadding.bottom = paddingBottom > mSelectionBottomPadding ? paddingBottom
|
||||
: mSelectionBottomPadding;
|
||||
|
||||
if (mDataChanged) {
|
||||
handleDataChanged();
|
||||
}
|
||||
|
||||
int preferredHeight = 0;
|
||||
int preferredWidth = 0;
|
||||
boolean needsMeasuring = true;
|
||||
|
||||
int selectedPosition = getSelectedItemPosition();
|
||||
if (selectedPosition >= 0 && mAdapter != null && selectedPosition < mAdapter.getCount()) {
|
||||
// Try looking in the recycler. (Maybe we were measured once already)
|
||||
View view = mRecycler.get(selectedPosition);
|
||||
if (view == null) {
|
||||
// Make a new one
|
||||
view = mAdapter.getView(selectedPosition, null, this);
|
||||
}
|
||||
|
||||
if (view != null) {
|
||||
// Put in recycler for re-measuring and/or layout
|
||||
mRecycler.put(selectedPosition, view);
|
||||
|
||||
if (view.getLayoutParams() == null) {
|
||||
mBlockLayoutRequests = true;
|
||||
view.setLayoutParams(generateDefaultLayoutParams());
|
||||
mBlockLayoutRequests = false;
|
||||
}
|
||||
measureChild(view, widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
preferredHeight = getChildHeight(view) + mSpinnerPadding.top + mSpinnerPadding.bottom;
|
||||
preferredWidth = getChildWidth(view) + mSpinnerPadding.left + mSpinnerPadding.right;
|
||||
|
||||
needsMeasuring = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsMeasuring) {
|
||||
// No views -- just use padding
|
||||
preferredHeight = mSpinnerPadding.top + mSpinnerPadding.bottom;
|
||||
if (widthMode == MeasureSpec.UNSPECIFIED) {
|
||||
preferredWidth = mSpinnerPadding.left + mSpinnerPadding.right;
|
||||
}
|
||||
}
|
||||
|
||||
preferredHeight = Math.max(preferredHeight, getSuggestedMinimumHeight());
|
||||
preferredWidth = Math.max(preferredWidth, getSuggestedMinimumWidth());
|
||||
|
||||
heightSize = ViewCompat.resolveSizeAndState(preferredHeight, heightMeasureSpec, 0);
|
||||
widthSize = ViewCompat.resolveSizeAndState(preferredWidth, widthMeasureSpec, 0);
|
||||
|
||||
setMeasuredDimension(widthSize, heightSize);
|
||||
mHeightMeasureSpec = heightMeasureSpec;
|
||||
mWidthMeasureSpec = widthMeasureSpec;
|
||||
}
|
||||
|
||||
int getChildHeight(View child) {
|
||||
return child.getMeasuredHeight();
|
||||
}
|
||||
|
||||
int getChildWidth(View child) {
|
||||
return child.getMeasuredWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
|
||||
return new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
void recycleAllViews() {
|
||||
final int childCount = getChildCount();
|
||||
final AbsSpinnerCompat.RecycleBin recycleBin = mRecycler;
|
||||
final int position = mFirstPosition;
|
||||
|
||||
// All views go in recycler
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View v = getChildAt(i);
|
||||
int index = position + i;
|
||||
recycleBin.put(index, v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Jump directly to a specific item in the adapter data.
|
||||
*/
|
||||
public void setSelection(int position, boolean animate) {
|
||||
// Animate only if requested position is already on screen somewhere
|
||||
boolean shouldAnimate = animate && mFirstPosition <= position &&
|
||||
position <= mFirstPosition + getChildCount() - 1;
|
||||
setSelectionInt(position, shouldAnimate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelection(int position) {
|
||||
setNextSelectedPositionInt(position);
|
||||
requestLayout();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Makes the item at the supplied position selected.
|
||||
*
|
||||
* @param position Position to select
|
||||
* @param animate Should the transition be animated
|
||||
*
|
||||
*/
|
||||
void setSelectionInt(int position, boolean animate) {
|
||||
if (position != mOldSelectedPosition) {
|
||||
mBlockLayoutRequests = true;
|
||||
int delta = position - mSelectedPosition;
|
||||
setNextSelectedPositionInt(position);
|
||||
layout(delta, animate);
|
||||
mBlockLayoutRequests = false;
|
||||
}
|
||||
}
|
||||
|
||||
abstract void layout(int delta, boolean animate);
|
||||
|
||||
@Override
|
||||
public View getSelectedView() {
|
||||
if (mItemCount > 0 && mSelectedPosition >= 0) {
|
||||
return getChildAt(mSelectedPosition - mFirstPosition);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to prevent spamming ourselves with layout requests
|
||||
* as we place views
|
||||
*
|
||||
* @see android.view.View#requestLayout()
|
||||
*/
|
||||
@Override
|
||||
public void requestLayout() {
|
||||
if (!mBlockLayoutRequests) {
|
||||
super.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpinnerAdapter getAdapter() {
|
||||
return mAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mItemCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a point to a position in the list.
|
||||
*
|
||||
* @param x X in local coordinate
|
||||
* @param y Y in local coordinate
|
||||
* @return The position of the item which contains the specified point, or
|
||||
* {@link #INVALID_POSITION} if the point does not intersect an item.
|
||||
*/
|
||||
public int pointToPosition(int x, int y) {
|
||||
Rect frame = mTouchFrame;
|
||||
if (frame == null) {
|
||||
mTouchFrame = new Rect();
|
||||
frame = mTouchFrame;
|
||||
}
|
||||
|
||||
final int count = getChildCount();
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
View child = getChildAt(i);
|
||||
if (child.getVisibility() == View.VISIBLE) {
|
||||
child.getHitRect(frame);
|
||||
if (frame.contains(x, y)) {
|
||||
return mFirstPosition + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return INVALID_POSITION;
|
||||
}
|
||||
|
||||
static class SavedState extends BaseSavedState {
|
||||
long selectedId;
|
||||
int position;
|
||||
|
||||
/**
|
||||
* Constructor called from {@link AbsSpinnerCompat#onSaveInstanceState()}
|
||||
*/
|
||||
SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor called from {@link #CREATOR}
|
||||
*/
|
||||
SavedState(Parcel in) {
|
||||
super(in);
|
||||
selectedId = in.readLong();
|
||||
position = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
super.writeToParcel(out, flags);
|
||||
out.writeLong(selectedId);
|
||||
out.writeInt(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AbsSpinner.SavedState{"
|
||||
+ Integer.toHexString(System.identityHashCode(this))
|
||||
+ " selectedId=" + selectedId
|
||||
+ " position=" + position + "}";
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SavedState> CREATOR
|
||||
= new Parcelable.Creator<SavedState>() {
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
Parcelable superState = super.onSaveInstanceState();
|
||||
SavedState ss = new SavedState(superState);
|
||||
ss.selectedId = getSelectedItemId();
|
||||
if (ss.selectedId >= 0) {
|
||||
ss.position = getSelectedItemPosition();
|
||||
} else {
|
||||
ss.position = INVALID_POSITION;
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
SavedState ss = (SavedState) state;
|
||||
|
||||
super.onRestoreInstanceState(ss.getSuperState());
|
||||
|
||||
if (ss.selectedId >= 0) {
|
||||
mDataChanged = true;
|
||||
mNeedSync = true;
|
||||
mSyncRowId = ss.selectedId;
|
||||
mSyncPosition = ss.position;
|
||||
mSyncMode = SYNC_SELECTED_POSITION;
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
class RecycleBin {
|
||||
private final SparseArray<View> mScrapHeap = new SparseArray<View>();
|
||||
|
||||
public void put(int position, View v) {
|
||||
mScrapHeap.put(position, v);
|
||||
}
|
||||
|
||||
View get(int position) {
|
||||
// System.out.print("Looking for " + position);
|
||||
View result = mScrapHeap.get(position);
|
||||
if (result != null) {
|
||||
// System.out.println(" HIT");
|
||||
mScrapHeap.delete(position);
|
||||
} else {
|
||||
// System.out.println(" MISS");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
final SparseArray<View> scrapHeap = mScrapHeap;
|
||||
final int count = scrapHeap.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final View view = scrapHeap.valueAt(i);
|
||||
if (view != null) {
|
||||
removeDetachedView(view, true);
|
||||
}
|
||||
}
|
||||
scrapHeap.clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package android.support.v7.internal.widget;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
class ActionBarBackgroundDrawable extends Drawable {
|
||||
|
||||
final ActionBarContainer mContainer;
|
||||
|
||||
public ActionBarBackgroundDrawable(ActionBarContainer container) {
|
||||
mContainer = container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (mContainer.mIsSplit) {
|
||||
if (mContainer.mSplitBackground != null) {
|
||||
mContainer.mSplitBackground.draw(canvas);
|
||||
}
|
||||
} else {
|
||||
if (mContainer.mBackground != null) {
|
||||
mContainer.mBackground.draw(canvas);
|
||||
}
|
||||
if (mContainer.mStackedBackground != null && mContainer.mIsStacked) {
|
||||
mContainer.mStackedBackground.draw(canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package android.support.v7.internal.widget;
|
||||
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
class ActionBarBackgroundDrawableV21 extends ActionBarBackgroundDrawable {
|
||||
|
||||
public ActionBarBackgroundDrawableV21(ActionBarContainer container) {
|
||||
super(container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getOutline(@NonNull Outline outline) {
|
||||
if (mContainer.mIsSplit) {
|
||||
if (mContainer.mSplitBackground != null) {
|
||||
mContainer.mSplitBackground.getOutline(outline);
|
||||
}
|
||||
} else {
|
||||
// ignore the stacked background for shadow casting
|
||||
if (mContainer.mBackground != null) {
|
||||
mContainer.mBackground.getOutline(outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
326
android/support/v7/internal/widget/ActionBarContainer.java
Normal file
326
android/support/v7/internal/widget/ActionBarContainer.java
Normal file
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.internal.VersionUtils;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
/**
|
||||
* This class acts as a container for the action bar view and action mode context views.
|
||||
* It applies special styles as needed to help handle animated transitions between them.
|
||||
* @hide
|
||||
*/
|
||||
public class ActionBarContainer extends FrameLayout {
|
||||
private boolean mIsTransitioning;
|
||||
private View mTabContainer;
|
||||
private View mActionBarView;
|
||||
private View mContextView;
|
||||
|
||||
Drawable mBackground;
|
||||
Drawable mStackedBackground;
|
||||
Drawable mSplitBackground;
|
||||
boolean mIsSplit;
|
||||
boolean mIsStacked;
|
||||
private int mHeight;
|
||||
|
||||
public ActionBarContainer(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ActionBarContainer(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
// Set a transparent background so that we project appropriately.
|
||||
final Drawable bg = VersionUtils.isAtLeastL()
|
||||
? new ActionBarBackgroundDrawableV21(this)
|
||||
: new ActionBarBackgroundDrawable(this);
|
||||
setBackgroundDrawable(bg);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.ActionBar);
|
||||
mBackground = a.getDrawable(R.styleable.ActionBar_background);
|
||||
mStackedBackground = a.getDrawable(
|
||||
R.styleable.ActionBar_backgroundStacked);
|
||||
mHeight = a.getDimensionPixelSize(R.styleable.ActionBar_height, -1);
|
||||
|
||||
if (getId() == R.id.split_action_bar) {
|
||||
mIsSplit = true;
|
||||
mSplitBackground = a.getDrawable(R.styleable.ActionBar_backgroundSplit);
|
||||
}
|
||||
a.recycle();
|
||||
|
||||
setWillNotDraw(mIsSplit ? mSplitBackground == null :
|
||||
mBackground == null && mStackedBackground == null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mActionBarView = findViewById(R.id.action_bar);
|
||||
mContextView = findViewById(R.id.action_context_bar);
|
||||
}
|
||||
|
||||
public void setPrimaryBackground(Drawable bg) {
|
||||
if (mBackground != null) {
|
||||
mBackground.setCallback(null);
|
||||
unscheduleDrawable(mBackground);
|
||||
}
|
||||
mBackground = bg;
|
||||
if (bg != null) {
|
||||
bg.setCallback(this);
|
||||
if (mActionBarView != null) {
|
||||
mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
|
||||
mActionBarView.getRight(), mActionBarView.getBottom());
|
||||
}
|
||||
}
|
||||
setWillNotDraw(mIsSplit ? mSplitBackground == null :
|
||||
mBackground == null && mStackedBackground == null);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setStackedBackground(Drawable bg) {
|
||||
if (mStackedBackground != null) {
|
||||
mStackedBackground.setCallback(null);
|
||||
unscheduleDrawable(mStackedBackground);
|
||||
}
|
||||
mStackedBackground = bg;
|
||||
if (bg != null) {
|
||||
bg.setCallback(this);
|
||||
if ((mIsStacked && mStackedBackground != null)) {
|
||||
mStackedBackground.setBounds(mTabContainer.getLeft(), mTabContainer.getTop(),
|
||||
mTabContainer.getRight(), mTabContainer.getBottom());
|
||||
}
|
||||
}
|
||||
setWillNotDraw(mIsSplit ? mSplitBackground == null :
|
||||
mBackground == null && mStackedBackground == null);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setSplitBackground(Drawable bg) {
|
||||
if (mSplitBackground != null) {
|
||||
mSplitBackground.setCallback(null);
|
||||
unscheduleDrawable(mSplitBackground);
|
||||
}
|
||||
mSplitBackground = bg;
|
||||
if (bg != null) {
|
||||
bg.setCallback(this);
|
||||
if (mIsSplit && mSplitBackground != null) {
|
||||
mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
setWillNotDraw(mIsSplit ? mSplitBackground == null :
|
||||
mBackground == null && mStackedBackground == null);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibility(int visibility) {
|
||||
super.setVisibility(visibility);
|
||||
final boolean isVisible = visibility == VISIBLE;
|
||||
if (mBackground != null) mBackground.setVisible(isVisible, false);
|
||||
if (mStackedBackground != null) mStackedBackground.setVisible(isVisible, false);
|
||||
if (mSplitBackground != null) mSplitBackground.setVisible(isVisible, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean verifyDrawable(Drawable who) {
|
||||
return (who == mBackground && !mIsSplit) || (who == mStackedBackground && mIsStacked) ||
|
||||
(who == mSplitBackground && mIsSplit) || super.verifyDrawable(who);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawableStateChanged() {
|
||||
super.drawableStateChanged();
|
||||
if (mBackground != null && mBackground.isStateful()) {
|
||||
mBackground.setState(getDrawableState());
|
||||
}
|
||||
if (mStackedBackground != null && mStackedBackground.isStateful()) {
|
||||
mStackedBackground.setState(getDrawableState());
|
||||
}
|
||||
if (mSplitBackground != null && mSplitBackground.isStateful()) {
|
||||
mSplitBackground.setState(getDrawableState());
|
||||
}
|
||||
}
|
||||
|
||||
public void jumpDrawablesToCurrentState() {
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
super.jumpDrawablesToCurrentState();
|
||||
if (mBackground != null) {
|
||||
mBackground.jumpToCurrentState();
|
||||
}
|
||||
if (mStackedBackground != null) {
|
||||
mStackedBackground.jumpToCurrentState();
|
||||
}
|
||||
if (mSplitBackground != null) {
|
||||
mSplitBackground.jumpToCurrentState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the action bar into a "transitioning" state. While transitioning the bar will block focus
|
||||
* and touch from all of its descendants. This prevents the user from interacting with the bar
|
||||
* while it is animating in or out.
|
||||
*
|
||||
* @param isTransitioning true if the bar is currently transitioning, false otherwise.
|
||||
*/
|
||||
public void setTransitioning(boolean isTransitioning) {
|
||||
mIsTransitioning = isTransitioning;
|
||||
setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS
|
||||
: FOCUS_AFTER_DESCENDANTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
return mIsTransitioning || super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
super.onTouchEvent(ev);
|
||||
|
||||
// An action bar always eats touch events.
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setTabContainer(ScrollingTabContainerView tabView) {
|
||||
if (mTabContainer != null) {
|
||||
removeView(mTabContainer);
|
||||
}
|
||||
mTabContainer = tabView;
|
||||
if (tabView != null) {
|
||||
addView(tabView);
|
||||
final ViewGroup.LayoutParams lp = tabView.getLayoutParams();
|
||||
lp.width = LayoutParams.MATCH_PARENT;
|
||||
lp.height = LayoutParams.WRAP_CONTENT;
|
||||
tabView.setAllowCollapse(false);
|
||||
}
|
||||
}
|
||||
|
||||
public View getTabContainer() {
|
||||
return mTabContainer;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
|
||||
// No starting an action mode for an action bar child! (Where would it go?)
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.view.ActionMode startActionModeForChild(View originalView,
|
||||
android.view.ActionMode.Callback callback) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isCollapsed(View view) {
|
||||
return view == null || view.getVisibility() == GONE || view.getMeasuredHeight() == 0;
|
||||
}
|
||||
|
||||
private int getMeasuredHeightWithMargins(View view) {
|
||||
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
|
||||
return view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
final int mode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
if (mTabContainer != null && mTabContainer.getVisibility() != GONE
|
||||
&& mode != MeasureSpec.EXACTLY) {
|
||||
final int topMarginForTabs;
|
||||
if (!isCollapsed(mActionBarView)) {
|
||||
topMarginForTabs = getMeasuredHeightWithMargins(mActionBarView);
|
||||
} else if (!isCollapsed(mContextView)) {
|
||||
topMarginForTabs = getMeasuredHeightWithMargins(mContextView);
|
||||
} else {
|
||||
topMarginForTabs = 0;
|
||||
}
|
||||
final int maxHeight = mode == MeasureSpec.AT_MOST ?
|
||||
MeasureSpec.getSize(heightMeasureSpec) : Integer.MAX_VALUE;
|
||||
setMeasuredDimension(getMeasuredWidth(),
|
||||
Math.min(topMarginForTabs + getMeasuredHeightWithMargins(mTabContainer),
|
||||
maxHeight));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
|
||||
final View tabContainer = mTabContainer;
|
||||
final boolean hasTabs = tabContainer != null && tabContainer.getVisibility() != GONE;
|
||||
|
||||
if (tabContainer != null && tabContainer.getVisibility() != GONE) {
|
||||
final int containerHeight = getMeasuredHeight();
|
||||
final LayoutParams lp = (LayoutParams) tabContainer.getLayoutParams();
|
||||
final int tabHeight = tabContainer.getMeasuredHeight();
|
||||
tabContainer.layout(l, containerHeight - tabHeight - lp.bottomMargin, r,
|
||||
containerHeight - lp.bottomMargin);
|
||||
}
|
||||
|
||||
boolean needsInvalidate = false;
|
||||
if (mIsSplit) {
|
||||
if (mSplitBackground != null) {
|
||||
mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
|
||||
needsInvalidate = true;
|
||||
}
|
||||
} else {
|
||||
if (mBackground != null) {
|
||||
if (mActionBarView.getVisibility() == View.VISIBLE) {
|
||||
mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
|
||||
mActionBarView.getRight(), mActionBarView.getBottom());
|
||||
} else if (mContextView != null &&
|
||||
mContextView.getVisibility() == View.VISIBLE) {
|
||||
mBackground.setBounds(mContextView.getLeft(), mContextView.getTop(),
|
||||
mContextView.getRight(), mContextView.getBottom());
|
||||
} else {
|
||||
mBackground.setBounds(0, 0, 0, 0);
|
||||
}
|
||||
needsInvalidate = true;
|
||||
}
|
||||
mIsStacked = hasTabs;
|
||||
if (hasTabs && mStackedBackground != null) {
|
||||
mStackedBackground.setBounds(tabContainer.getLeft(), tabContainer.getTop(),
|
||||
tabContainer.getRight(), tabContainer.getBottom());
|
||||
needsInvalidate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsInvalidate) {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
546
android/support/v7/internal/widget/ActionBarContextView.java
Normal file
546
android/support/v7/internal/widget/ActionBarContextView.java
Normal file
|
@ -0,0 +1,546 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.view.ViewPropertyAnimatorCompat;
|
||||
import android.support.v4.view.ViewPropertyAnimatorListener;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.internal.view.ViewPropertyAnimatorCompatSet;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.support.v7.widget.ActionMenuPresenter;
|
||||
import android.support.v7.widget.ActionMenuView;
|
||||
import android.support.v7.internal.view.menu.MenuBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class ActionBarContextView extends AbsActionBarView implements ViewPropertyAnimatorListener {
|
||||
private static final String TAG = "ActionBarContextView";
|
||||
|
||||
private CharSequence mTitle;
|
||||
private CharSequence mSubtitle;
|
||||
|
||||
private View mClose;
|
||||
private View mCustomView;
|
||||
private LinearLayout mTitleLayout;
|
||||
private TextView mTitleView;
|
||||
private TextView mSubtitleView;
|
||||
private int mTitleStyleRes;
|
||||
private int mSubtitleStyleRes;
|
||||
private Drawable mSplitBackground;
|
||||
private boolean mTitleOptional;
|
||||
private int mCloseItemLayout;
|
||||
|
||||
private ViewPropertyAnimatorCompatSet mCurrentAnimation;
|
||||
private boolean mAnimateInOnLayout;
|
||||
private int mAnimationMode;
|
||||
|
||||
private static final int ANIMATE_IDLE = 0;
|
||||
private static final int ANIMATE_IN = 1;
|
||||
private static final int ANIMATE_OUT = 2;
|
||||
|
||||
public ActionBarContextView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ActionBarContextView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.actionModeStyle);
|
||||
}
|
||||
|
||||
public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
|
||||
R.styleable.ActionMode, defStyle, 0);
|
||||
setBackgroundDrawable(a.getDrawable(
|
||||
R.styleable.ActionMode_background));
|
||||
mTitleStyleRes = a.getResourceId(
|
||||
R.styleable.ActionMode_titleTextStyle, 0);
|
||||
mSubtitleStyleRes = a.getResourceId(
|
||||
R.styleable.ActionMode_subtitleTextStyle, 0);
|
||||
|
||||
mContentHeight = a.getLayoutDimension(
|
||||
R.styleable.ActionMode_height, 0);
|
||||
|
||||
mSplitBackground = a.getDrawable(
|
||||
R.styleable.ActionMode_backgroundSplit);
|
||||
|
||||
mCloseItemLayout = a.getResourceId(
|
||||
R.styleable.ActionMode_closeItemLayout,
|
||||
R.layout.abc_action_mode_close_item_material);
|
||||
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
if (mActionMenuPresenter != null) {
|
||||
mActionMenuPresenter.hideOverflowMenu();
|
||||
mActionMenuPresenter.hideSubMenus();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSplitToolbar(boolean split) {
|
||||
if (mSplitActionBar != split) {
|
||||
if (mActionMenuPresenter != null) {
|
||||
// Mode is already active; move everything over and adjust the menu itself.
|
||||
final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.MATCH_PARENT);
|
||||
if (!split) {
|
||||
mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
|
||||
mMenuView.setBackgroundDrawable(null);
|
||||
final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
|
||||
if (oldParent != null) oldParent.removeView(mMenuView);
|
||||
addView(mMenuView, layoutParams);
|
||||
} else {
|
||||
// Allow full screen width in split mode.
|
||||
mActionMenuPresenter.setWidthLimit(
|
||||
getContext().getResources().getDisplayMetrics().widthPixels, true);
|
||||
// No limit to the item count; use whatever will fit.
|
||||
mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
|
||||
// Span the whole width
|
||||
layoutParams.width = LayoutParams.MATCH_PARENT;
|
||||
layoutParams.height = mContentHeight;
|
||||
mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
|
||||
mMenuView.setBackgroundDrawable(mSplitBackground);
|
||||
final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
|
||||
if (oldParent != null) oldParent.removeView(mMenuView);
|
||||
mSplitView.addView(mMenuView, layoutParams);
|
||||
}
|
||||
}
|
||||
super.setSplitToolbar(split);
|
||||
}
|
||||
}
|
||||
|
||||
public void setContentHeight(int height) {
|
||||
mContentHeight = height;
|
||||
}
|
||||
|
||||
public void setCustomView(View view) {
|
||||
if (mCustomView != null) {
|
||||
removeView(mCustomView);
|
||||
}
|
||||
mCustomView = view;
|
||||
if (mTitleLayout != null) {
|
||||
removeView(mTitleLayout);
|
||||
mTitleLayout = null;
|
||||
}
|
||||
if (view != null) {
|
||||
addView(view);
|
||||
}
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void setTitle(CharSequence title) {
|
||||
mTitle = title;
|
||||
initTitle();
|
||||
}
|
||||
|
||||
public void setSubtitle(CharSequence subtitle) {
|
||||
mSubtitle = subtitle;
|
||||
initTitle();
|
||||
}
|
||||
|
||||
public CharSequence getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
public CharSequence getSubtitle() {
|
||||
return mSubtitle;
|
||||
}
|
||||
|
||||
private void initTitle() {
|
||||
if (mTitleLayout == null) {
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
inflater.inflate(R.layout.abc_action_bar_title_item, this);
|
||||
mTitleLayout = (LinearLayout) getChildAt(getChildCount() - 1);
|
||||
mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
|
||||
mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
|
||||
if (mTitleStyleRes != 0) {
|
||||
mTitleView.setTextAppearance(getContext(), mTitleStyleRes);
|
||||
}
|
||||
if (mSubtitleStyleRes != 0) {
|
||||
mSubtitleView.setTextAppearance(getContext(), mSubtitleStyleRes);
|
||||
}
|
||||
}
|
||||
|
||||
mTitleView.setText(mTitle);
|
||||
mSubtitleView.setText(mSubtitle);
|
||||
|
||||
final boolean hasTitle = !TextUtils.isEmpty(mTitle);
|
||||
final boolean hasSubtitle = !TextUtils.isEmpty(mSubtitle);
|
||||
mSubtitleView.setVisibility(hasSubtitle ? VISIBLE : GONE);
|
||||
mTitleLayout.setVisibility(hasTitle || hasSubtitle ? VISIBLE : GONE);
|
||||
if (mTitleLayout.getParent() == null) {
|
||||
addView(mTitleLayout);
|
||||
}
|
||||
}
|
||||
|
||||
public void initForMode(final ActionMode mode) {
|
||||
if (mClose == null) {
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
mClose = inflater.inflate(mCloseItemLayout, this, false);
|
||||
addView(mClose);
|
||||
} else if (mClose.getParent() == null) {
|
||||
addView(mClose);
|
||||
}
|
||||
|
||||
View closeButton = mClose.findViewById(R.id.action_mode_close_button);
|
||||
closeButton.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
mode.finish();
|
||||
}
|
||||
});
|
||||
|
||||
final MenuBuilder menu = (MenuBuilder) mode.getMenu();
|
||||
if (mActionMenuPresenter != null) {
|
||||
mActionMenuPresenter.dismissPopupMenus();
|
||||
}
|
||||
mActionMenuPresenter = new ActionMenuPresenter(getContext());
|
||||
mActionMenuPresenter.setReserveOverflow(true);
|
||||
|
||||
final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.MATCH_PARENT);
|
||||
if (!mSplitActionBar) {
|
||||
menu.addMenuPresenter(mActionMenuPresenter, mPopupContext);
|
||||
mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
|
||||
mMenuView.setBackgroundDrawable(null);
|
||||
addView(mMenuView, layoutParams);
|
||||
} else {
|
||||
// Allow full screen width in split mode.
|
||||
mActionMenuPresenter.setWidthLimit(
|
||||
getContext().getResources().getDisplayMetrics().widthPixels, true);
|
||||
// No limit to the item count; use whatever will fit.
|
||||
mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
|
||||
// Span the whole width
|
||||
layoutParams.width = LayoutParams.MATCH_PARENT;
|
||||
layoutParams.height = mContentHeight;
|
||||
menu.addMenuPresenter(mActionMenuPresenter, mPopupContext);
|
||||
mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
|
||||
mMenuView.setBackgroundDrawable(mSplitBackground);
|
||||
mSplitView.addView(mMenuView, layoutParams);
|
||||
}
|
||||
|
||||
mAnimateInOnLayout = true;
|
||||
}
|
||||
|
||||
public void closeMode() {
|
||||
if (mAnimationMode == ANIMATE_OUT) {
|
||||
// Called again during close; just finish what we were doing.
|
||||
return;
|
||||
}
|
||||
if (mClose == null) {
|
||||
killMode();
|
||||
return;
|
||||
}
|
||||
|
||||
finishAnimation();
|
||||
mAnimationMode = ANIMATE_OUT;
|
||||
mCurrentAnimation = makeOutAnimation();
|
||||
mCurrentAnimation.start();
|
||||
}
|
||||
|
||||
private void finishAnimation() {
|
||||
final ViewPropertyAnimatorCompatSet a = mCurrentAnimation;
|
||||
if (a != null) {
|
||||
mCurrentAnimation = null;
|
||||
a.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public void killMode() {
|
||||
finishAnimation();
|
||||
removeAllViews();
|
||||
if (mSplitView != null) {
|
||||
mSplitView.removeView(mMenuView);
|
||||
}
|
||||
mCustomView = null;
|
||||
mMenuView = null;
|
||||
mAnimateInOnLayout = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showOverflowMenu() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
return mActionMenuPresenter.showOverflowMenu();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hideOverflowMenu() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
return mActionMenuPresenter.hideOverflowMenu();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverflowMenuShowing() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
return mActionMenuPresenter.isOverflowMenuShowing();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
|
||||
// Used by custom views if they don't supply layout params. Everything else
|
||||
// added to an ActionBarContextView should have them already.
|
||||
return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
|
||||
return new MarginLayoutParams(getContext(), attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
if (widthMode != MeasureSpec.EXACTLY) {
|
||||
throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
|
||||
"with android:layout_width=\"match_parent\" (or fill_parent)");
|
||||
}
|
||||
|
||||
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
if (heightMode == MeasureSpec.UNSPECIFIED) {
|
||||
throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
|
||||
"with android:layout_height=\"wrap_content\"");
|
||||
}
|
||||
|
||||
final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
|
||||
|
||||
int maxHeight = mContentHeight > 0 ?
|
||||
mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
final int verticalPadding = getPaddingTop() + getPaddingBottom();
|
||||
int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
|
||||
final int height = maxHeight - verticalPadding;
|
||||
final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
|
||||
|
||||
if (mClose != null) {
|
||||
availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
|
||||
MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
|
||||
availableWidth -= lp.leftMargin + lp.rightMargin;
|
||||
}
|
||||
|
||||
if (mMenuView != null && mMenuView.getParent() == this) {
|
||||
availableWidth = measureChildView(mMenuView, availableWidth,
|
||||
childSpecHeight, 0);
|
||||
}
|
||||
|
||||
if (mTitleLayout != null && mCustomView == null) {
|
||||
if (mTitleOptional) {
|
||||
final int titleWidthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
mTitleLayout.measure(titleWidthSpec, childSpecHeight);
|
||||
final int titleWidth = mTitleLayout.getMeasuredWidth();
|
||||
final boolean titleFits = titleWidth <= availableWidth;
|
||||
if (titleFits) {
|
||||
availableWidth -= titleWidth;
|
||||
}
|
||||
mTitleLayout.setVisibility(titleFits ? VISIBLE : GONE);
|
||||
} else {
|
||||
availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (mCustomView != null) {
|
||||
ViewGroup.LayoutParams lp = mCustomView.getLayoutParams();
|
||||
final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
|
||||
MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
|
||||
final int customWidth = lp.width >= 0 ?
|
||||
Math.min(lp.width, availableWidth) : availableWidth;
|
||||
final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
|
||||
MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
|
||||
final int customHeight = lp.height >= 0 ?
|
||||
Math.min(lp.height, height) : height;
|
||||
mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode),
|
||||
MeasureSpec.makeMeasureSpec(customHeight, customHeightMode));
|
||||
}
|
||||
|
||||
if (mContentHeight <= 0) {
|
||||
int measuredHeight = 0;
|
||||
final int count = getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
View v = getChildAt(i);
|
||||
int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
|
||||
if (paddedViewHeight > measuredHeight) {
|
||||
measuredHeight = paddedViewHeight;
|
||||
}
|
||||
}
|
||||
setMeasuredDimension(contentWidth, measuredHeight);
|
||||
} else {
|
||||
setMeasuredDimension(contentWidth, maxHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private ViewPropertyAnimatorCompatSet makeInAnimation() {
|
||||
ViewCompat.setTranslationX(mClose, -mClose.getWidth() -
|
||||
((MarginLayoutParams) mClose.getLayoutParams()).leftMargin);
|
||||
ViewPropertyAnimatorCompat buttonAnimator = ViewCompat.animate(mClose).translationX(0);
|
||||
buttonAnimator.setDuration(200);
|
||||
buttonAnimator.setListener(this);
|
||||
buttonAnimator.setInterpolator(new DecelerateInterpolator());
|
||||
|
||||
ViewPropertyAnimatorCompatSet set = new ViewPropertyAnimatorCompatSet();
|
||||
set.play(buttonAnimator);
|
||||
|
||||
if (mMenuView != null) {
|
||||
final int count = mMenuView.getChildCount();
|
||||
if (count > 0) {
|
||||
for (int i = count - 1, j = 0; i >= 0; i--, j++) {
|
||||
View child = mMenuView.getChildAt(i);
|
||||
ViewCompat.setScaleY(child, 0);
|
||||
ViewPropertyAnimatorCompat a = ViewCompat.animate(child).scaleY(1);
|
||||
a.setDuration(300);
|
||||
set.play(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
private ViewPropertyAnimatorCompatSet makeOutAnimation() {
|
||||
ViewPropertyAnimatorCompat buttonAnimator = ViewCompat.animate(mClose)
|
||||
.translationX(-mClose.getWidth() -
|
||||
((MarginLayoutParams) mClose.getLayoutParams()).leftMargin);
|
||||
buttonAnimator.setDuration(200);
|
||||
buttonAnimator.setListener(this);
|
||||
buttonAnimator.setInterpolator(new DecelerateInterpolator());
|
||||
|
||||
ViewPropertyAnimatorCompatSet set = new ViewPropertyAnimatorCompatSet();
|
||||
set.play(buttonAnimator);
|
||||
|
||||
if (mMenuView != null) {
|
||||
final int count = mMenuView.getChildCount();
|
||||
if (count > 0) {
|
||||
for (int i = 0; i < 0; i++) {
|
||||
View child = mMenuView.getChildAt(i);
|
||||
ViewCompat.setScaleY(child, 1);
|
||||
ViewPropertyAnimatorCompat a = ViewCompat.animate(child).scaleY(0);
|
||||
a.setDuration(300);
|
||||
set.play(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
final boolean isLayoutRtl = ViewUtils.isLayoutRtl(this);
|
||||
int x = isLayoutRtl ? r - l - getPaddingRight() : getPaddingLeft();
|
||||
final int y = getPaddingTop();
|
||||
final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
|
||||
|
||||
if (mClose != null && mClose.getVisibility() != GONE) {
|
||||
MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
|
||||
final int startMargin = (isLayoutRtl ? lp.rightMargin : lp.leftMargin);
|
||||
final int endMargin = (isLayoutRtl ? lp.leftMargin : lp.rightMargin);
|
||||
x = next(x, startMargin, isLayoutRtl);
|
||||
x += positionChild(mClose, x, y, contentHeight, isLayoutRtl);
|
||||
x = next(x, endMargin, isLayoutRtl);
|
||||
|
||||
if (mAnimateInOnLayout) {
|
||||
mAnimationMode = ANIMATE_IN;
|
||||
mCurrentAnimation = makeInAnimation();
|
||||
mCurrentAnimation.start();
|
||||
mAnimateInOnLayout = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mTitleLayout != null && mCustomView == null && mTitleLayout.getVisibility() != GONE) {
|
||||
x += positionChild(mTitleLayout, x, y, contentHeight, isLayoutRtl);
|
||||
}
|
||||
|
||||
if (mCustomView != null) {
|
||||
x += positionChild(mCustomView, x, y, contentHeight, isLayoutRtl);
|
||||
}
|
||||
|
||||
x = isLayoutRtl ? getPaddingLeft() : r - l - getPaddingRight();
|
||||
|
||||
if (mMenuView != null) {
|
||||
x += positionChild(mMenuView, x, y, contentHeight, !isLayoutRtl);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(View view) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(View view) {
|
||||
if (mAnimationMode == ANIMATE_OUT) {
|
||||
killMode();
|
||||
}
|
||||
mAnimationMode = ANIMATE_IDLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(View view) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDelayChildPressedState() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
|
||||
// Action mode started
|
||||
event.setSource(this);
|
||||
event.setClassName(getClass().getName());
|
||||
event.setPackageName(getContext().getPackageName());
|
||||
event.setContentDescription(mTitle);
|
||||
} else {
|
||||
super.onInitializeAccessibilityEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setTitleOptional(boolean titleOptional) {
|
||||
if (titleOptional != mTitleOptional) {
|
||||
requestLayout();
|
||||
}
|
||||
mTitleOptional = titleOptional;
|
||||
}
|
||||
|
||||
public boolean isTitleOptional() {
|
||||
return mTitleOptional;
|
||||
}
|
||||
}
|
822
android/support/v7/internal/widget/ActionBarOverlayLayout.java
Normal file
822
android/support/v7/internal/widget/ActionBarOverlayLayout.java
Normal file
|
@ -0,0 +1,822 @@
|
|||
/*
|
||||
* Copyright (C) 2012 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.view.ViewPropertyAnimatorCompat;
|
||||
import android.support.v4.view.ViewPropertyAnimatorListener;
|
||||
import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
|
||||
import android.support.v4.widget.ScrollerCompat;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.internal.VersionUtils;
|
||||
import android.support.v7.internal.view.menu.MenuPresenter;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Menu;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
|
||||
/**
|
||||
* Special layout for the containing of an overlay action bar (and its content) to correctly handle
|
||||
* fitting system windows when the content has request that its layout ignore them.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ActionBarOverlayLayout extends ViewGroup implements DecorContentParent {
|
||||
private static final String TAG = "ActionBarOverlayLayout";
|
||||
|
||||
private int mActionBarHeight;
|
||||
//private WindowDecorActionBar mActionBar;
|
||||
private int mWindowVisibility = View.VISIBLE;
|
||||
|
||||
// The main UI elements that we handle the layout of.
|
||||
private ContentFrameLayout mContent;
|
||||
private ActionBarContainer mActionBarBottom;
|
||||
private ActionBarContainer mActionBarTop;
|
||||
|
||||
// Some interior UI elements.
|
||||
private DecorToolbar mDecorToolbar;
|
||||
|
||||
// Content overlay drawable - generally the action bar's shadow
|
||||
private Drawable mWindowContentOverlay;
|
||||
private boolean mIgnoreWindowContentOverlay;
|
||||
|
||||
private boolean mOverlayMode;
|
||||
private boolean mHasNonEmbeddedTabs;
|
||||
private boolean mHideOnContentScroll;
|
||||
private boolean mAnimatingForFling;
|
||||
private int mHideOnContentScrollReference;
|
||||
private int mLastSystemUiVisibility;
|
||||
private final Rect mBaseContentInsets = new Rect();
|
||||
private final Rect mLastBaseContentInsets = new Rect();
|
||||
private final Rect mContentInsets = new Rect();
|
||||
private final Rect mBaseInnerInsets = new Rect();
|
||||
private final Rect mInnerInsets = new Rect();
|
||||
private final Rect mLastInnerInsets = new Rect();
|
||||
|
||||
private ActionBarVisibilityCallback mActionBarVisibilityCallback;
|
||||
|
||||
private final int ACTION_BAR_ANIMATE_DELAY = 600; // ms
|
||||
|
||||
private ScrollerCompat mFlingEstimator;
|
||||
|
||||
private ViewPropertyAnimatorCompat mCurrentActionBarTopAnimator;
|
||||
private ViewPropertyAnimatorCompat mCurrentActionBarBottomAnimator;
|
||||
|
||||
private final ViewPropertyAnimatorListener mTopAnimatorListener
|
||||
= new ViewPropertyAnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(View view) {
|
||||
mCurrentActionBarTopAnimator = null;
|
||||
mAnimatingForFling = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(View view) {
|
||||
mCurrentActionBarTopAnimator = null;
|
||||
mAnimatingForFling = false;
|
||||
}
|
||||
};
|
||||
|
||||
private final ViewPropertyAnimatorListener mBottomAnimatorListener =
|
||||
new ViewPropertyAnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(View view) {
|
||||
mCurrentActionBarBottomAnimator = null;
|
||||
mAnimatingForFling = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(View view) {
|
||||
mCurrentActionBarBottomAnimator = null;
|
||||
mAnimatingForFling = false;
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable mRemoveActionBarHideOffset = new Runnable() {
|
||||
public void run() {
|
||||
haltActionBarHideOffsetAnimations();
|
||||
mCurrentActionBarTopAnimator = ViewCompat.animate(mActionBarTop).translationY(0)
|
||||
.setListener(mTopAnimatorListener);
|
||||
if (mActionBarBottom != null && mActionBarBottom.getVisibility() != GONE) {
|
||||
mCurrentActionBarBottomAnimator = ViewCompat.animate(mActionBarBottom).translationY(0)
|
||||
.setListener(mBottomAnimatorListener);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable mAddActionBarHideOffset = new Runnable() {
|
||||
public void run() {
|
||||
haltActionBarHideOffsetAnimations();
|
||||
mCurrentActionBarTopAnimator = ViewCompat.animate(mActionBarTop)
|
||||
.translationY(-mActionBarTop.getHeight())
|
||||
.setListener(mTopAnimatorListener);
|
||||
if (mActionBarBottom != null && mActionBarBottom.getVisibility() != GONE) {
|
||||
mCurrentActionBarBottomAnimator = ViewCompat.animate(mActionBarBottom)
|
||||
.translationY(mActionBarBottom.getHeight())
|
||||
.setListener(mBottomAnimatorListener);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// public static final Property<ActionBarOverlayLayout, Integer> ACTION_BAR_HIDE_OFFSET =
|
||||
// new IntProperty<ActionBarOverlayLayout>("actionBarHideOffset") {
|
||||
//
|
||||
// @Override
|
||||
// public void setValue(ActionBarOverlayLayout object, int value) {
|
||||
// object.setActionBarHideOffset(value);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Integer get(ActionBarOverlayLayout object) {
|
||||
// return object.getActionBarHideOffset();
|
||||
// }
|
||||
// };
|
||||
|
||||
static final int[] ATTRS = new int [] {
|
||||
R.attr.actionBarSize,
|
||||
android.R.attr.windowContentOverlay
|
||||
};
|
||||
|
||||
public ActionBarOverlayLayout(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public ActionBarOverlayLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
TypedArray ta = getContext().getTheme().obtainStyledAttributes(ATTRS);
|
||||
mActionBarHeight = ta.getDimensionPixelSize(0, 0);
|
||||
mWindowContentOverlay = ta.getDrawable(1);
|
||||
setWillNotDraw(mWindowContentOverlay == null);
|
||||
ta.recycle();
|
||||
|
||||
mIgnoreWindowContentOverlay = context.getApplicationInfo().targetSdkVersion <
|
||||
Build.VERSION_CODES.KITKAT;
|
||||
|
||||
mFlingEstimator = ScrollerCompat.create(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
haltActionBarHideOffsetAnimations();
|
||||
}
|
||||
|
||||
public void setActionBarVisibilityCallback(ActionBarVisibilityCallback cb) {
|
||||
mActionBarVisibilityCallback = cb;
|
||||
if (getWindowToken() != null) {
|
||||
// This is being initialized after being added to a window;
|
||||
// make sure to update all state now.
|
||||
mActionBarVisibilityCallback.onWindowVisibilityChanged(mWindowVisibility);
|
||||
if (mLastSystemUiVisibility != 0) {
|
||||
int newVis = mLastSystemUiVisibility;
|
||||
onWindowSystemUiVisibilityChanged(newVis);
|
||||
ViewCompat.requestApplyInsets(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setOverlayMode(boolean overlayMode) {
|
||||
mOverlayMode = overlayMode;
|
||||
|
||||
/*
|
||||
* Drawing the window content overlay was broken before K so starting to draw it
|
||||
* again unexpectedly will cause artifacts in some apps. They should fix it.
|
||||
*/
|
||||
mIgnoreWindowContentOverlay = overlayMode &&
|
||||
getContext().getApplicationInfo().targetSdkVersion <
|
||||
Build.VERSION_CODES.KITKAT;
|
||||
}
|
||||
|
||||
public boolean isInOverlayMode() {
|
||||
return mOverlayMode;
|
||||
}
|
||||
|
||||
public void setHasNonEmbeddedTabs(boolean hasNonEmbeddedTabs) {
|
||||
mHasNonEmbeddedTabs = hasNonEmbeddedTabs;
|
||||
}
|
||||
|
||||
public void setShowingForActionMode(boolean showing) {
|
||||
// TODO: Add workaround for this
|
||||
// if (showing) {
|
||||
// // Here's a fun hack: if the status bar is currently being hidden,
|
||||
// // and the application has asked for stable content insets, then
|
||||
// // we will end up with the action mode action bar being shown
|
||||
// // without the status bar, but moved below where the status bar
|
||||
// // would be. Not nice. Trying to have this be positioned
|
||||
// // correctly is not easy (basically we need yet *another* content
|
||||
// // inset from the window manager to know where to put it), so
|
||||
// // instead we will just temporarily force the status bar to be shown.
|
||||
// if ((getWindowSystemUiVisibility() & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
// | SYSTEM_UI_FLAG_LAYOUT_STABLE))
|
||||
// == (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_STABLE)) {
|
||||
// setDisabledSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
|
||||
// }
|
||||
// } else {
|
||||
// setDisabledSystemUiVisibility(0);
|
||||
// }
|
||||
}
|
||||
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
if (Build.VERSION.SDK_INT >= 8) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
init(getContext());
|
||||
ViewCompat.requestApplyInsets(this);
|
||||
}
|
||||
|
||||
public void onWindowSystemUiVisibilityChanged(int visible) {
|
||||
if (Build.VERSION.SDK_INT >= 16) {
|
||||
super.onWindowSystemUiVisibilityChanged(visible);
|
||||
}
|
||||
pullChildren();
|
||||
final int diff = mLastSystemUiVisibility ^ visible;
|
||||
mLastSystemUiVisibility = visible;
|
||||
final boolean barVisible = (visible & SYSTEM_UI_FLAG_FULLSCREEN) == 0;
|
||||
final boolean stable = (visible & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
|
||||
if (mActionBarVisibilityCallback != null) {
|
||||
// We want the bar to be visible if it is not being hidden,
|
||||
// or the app has not turned on a stable UI mode (meaning they
|
||||
// are performing explicit layout around the action bar).
|
||||
mActionBarVisibilityCallback.enableContentAnimations(!stable);
|
||||
if (barVisible || !stable) mActionBarVisibilityCallback.showForSystem();
|
||||
else mActionBarVisibilityCallback.hideForSystem();
|
||||
}
|
||||
if ((diff & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
|
||||
if (mActionBarVisibilityCallback != null) {
|
||||
ViewCompat.requestApplyInsets(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onWindowVisibilityChanged(int visibility) {
|
||||
super.onWindowVisibilityChanged(visibility);
|
||||
mWindowVisibility = visibility;
|
||||
if (mActionBarVisibilityCallback != null) {
|
||||
mActionBarVisibilityCallback.onWindowVisibilityChanged(visibility);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean applyInsets(View view, Rect insets, boolean left, boolean top,
|
||||
boolean bottom, boolean right) {
|
||||
boolean changed = false;
|
||||
LayoutParams lp = (LayoutParams)view.getLayoutParams();
|
||||
if (left && lp.leftMargin != insets.left) {
|
||||
changed = true;
|
||||
lp.leftMargin = insets.left;
|
||||
}
|
||||
if (top && lp.topMargin != insets.top) {
|
||||
changed = true;
|
||||
lp.topMargin = insets.top;
|
||||
}
|
||||
if (right && lp.rightMargin != insets.right) {
|
||||
changed = true;
|
||||
lp.rightMargin = insets.right;
|
||||
}
|
||||
if (bottom && lp.bottomMargin != insets.bottom) {
|
||||
changed = true;
|
||||
lp.bottomMargin = insets.bottom;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean fitSystemWindows(Rect insets) {
|
||||
pullChildren();
|
||||
|
||||
final int vis = ViewCompat.getWindowSystemUiVisibility(this);
|
||||
final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
|
||||
final Rect systemInsets = insets;
|
||||
|
||||
// Since we're not the top level view in the window decor, we do not need to
|
||||
// inset the Action Bars
|
||||
|
||||
boolean changed = false;
|
||||
mBaseInnerInsets.set(systemInsets);
|
||||
ViewUtils.computeFitSystemWindows(this, mBaseInnerInsets, mBaseContentInsets);
|
||||
if (!mLastBaseContentInsets.equals(mBaseContentInsets)) {
|
||||
changed = true;
|
||||
mLastBaseContentInsets.set(mBaseContentInsets);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
// We don't do any more at this point. To correctly compute the content/inner
|
||||
// insets in all cases, we need to know the measured size of the various action
|
||||
// bar elements. fitSystemWindows() happens before the measure pass, so we can't
|
||||
// do that here. Instead we will take this up in onMeasure().
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LayoutParams generateDefaultLayoutParams() {
|
||||
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayoutParams generateLayoutParams(AttributeSet attrs) {
|
||||
return new LayoutParams(getContext(), attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
|
||||
return new LayoutParams(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
|
||||
return p instanceof LayoutParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
pullChildren();
|
||||
|
||||
int maxHeight = 0;
|
||||
int maxWidth = 0;
|
||||
int childState = 0;
|
||||
|
||||
int topInset = 0;
|
||||
int bottomInset = 0;
|
||||
|
||||
measureChildWithMargins(mActionBarTop, widthMeasureSpec, 0, heightMeasureSpec, 0);
|
||||
LayoutParams lp = (LayoutParams) mActionBarTop.getLayoutParams();
|
||||
maxWidth = Math.max(maxWidth,
|
||||
mActionBarTop.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
|
||||
maxHeight = Math.max(maxHeight,
|
||||
mActionBarTop.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
|
||||
childState = ViewUtils.combineMeasuredStates(childState,
|
||||
ViewCompat.getMeasuredState(mActionBarTop));
|
||||
|
||||
// xlarge screen layout doesn't have bottom action bar.
|
||||
if (mActionBarBottom != null) {
|
||||
measureChildWithMargins(mActionBarBottom, widthMeasureSpec, 0, heightMeasureSpec, 0);
|
||||
lp = (LayoutParams) mActionBarBottom.getLayoutParams();
|
||||
maxWidth = Math.max(maxWidth,
|
||||
mActionBarBottom.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
|
||||
maxHeight = Math.max(maxHeight,
|
||||
mActionBarBottom.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
|
||||
childState = ViewUtils.combineMeasuredStates(childState,
|
||||
ViewCompat.getMeasuredState(mActionBarBottom));
|
||||
}
|
||||
|
||||
final int vis = ViewCompat.getWindowSystemUiVisibility(this);
|
||||
final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
|
||||
|
||||
if (stable) {
|
||||
// This is the standard space needed for the action bar. For stable measurement,
|
||||
// we can't depend on the size currently reported by it -- this must remain constant.
|
||||
topInset = mActionBarHeight;
|
||||
if (mHasNonEmbeddedTabs) {
|
||||
final View tabs = mActionBarTop.getTabContainer();
|
||||
if (tabs != null) {
|
||||
// If tabs are not embedded, increase space on top to account for them.
|
||||
topInset += mActionBarHeight;
|
||||
}
|
||||
}
|
||||
} else if (mActionBarTop.getVisibility() != GONE) {
|
||||
// This is the space needed on top of the window for all of the action bar
|
||||
// and tabs.
|
||||
topInset = mActionBarTop.getMeasuredHeight();
|
||||
}
|
||||
|
||||
if (mDecorToolbar.isSplit()) {
|
||||
// If action bar is split, adjust bottom insets for it.
|
||||
if (mActionBarBottom != null) {
|
||||
if (stable) {
|
||||
bottomInset = mActionBarHeight;
|
||||
} else {
|
||||
bottomInset = mActionBarBottom.getMeasuredHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the window has not requested system UI layout flags, we need to
|
||||
// make sure its content is not being covered by system UI... though it
|
||||
// will still be covered by the action bar if they have requested it to
|
||||
// overlay.
|
||||
mContentInsets.set(mBaseContentInsets);
|
||||
mInnerInsets.set(mBaseInnerInsets);
|
||||
if (!mOverlayMode && !stable) {
|
||||
mContentInsets.top += topInset;
|
||||
mContentInsets.bottom += bottomInset;
|
||||
} else {
|
||||
mInnerInsets.top += topInset;
|
||||
mInnerInsets.bottom += bottomInset;
|
||||
}
|
||||
applyInsets(mContent, mContentInsets, true, true, true, true);
|
||||
|
||||
if (!mLastInnerInsets.equals(mInnerInsets)) {
|
||||
// If the inner insets have changed, we need to dispatch this down to
|
||||
// the app's fitSystemWindows(). We do this before measuring the content
|
||||
// view to keep the same semantics as the normal fitSystemWindows() call.
|
||||
mLastInnerInsets.set(mInnerInsets);
|
||||
|
||||
mContent.dispatchFitSystemWindows(mInnerInsets);
|
||||
}
|
||||
|
||||
measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, 0);
|
||||
lp = (LayoutParams) mContent.getLayoutParams();
|
||||
maxWidth = Math.max(maxWidth,
|
||||
mContent.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
|
||||
maxHeight = Math.max(maxHeight,
|
||||
mContent.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
|
||||
childState = ViewUtils.combineMeasuredStates(childState,
|
||||
ViewCompat.getMeasuredState(mContent));
|
||||
|
||||
// Account for padding too
|
||||
maxWidth += getPaddingLeft() + getPaddingRight();
|
||||
maxHeight += getPaddingTop() + getPaddingBottom();
|
||||
|
||||
// Check against our minimum height and width
|
||||
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
|
||||
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
|
||||
|
||||
setMeasuredDimension(
|
||||
ViewCompat.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
|
||||
ViewCompat.resolveSizeAndState(maxHeight, heightMeasureSpec,
|
||||
childState << MEASURED_HEIGHT_STATE_SHIFT));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
final int count = getChildCount();
|
||||
|
||||
final int parentLeft = getPaddingLeft();
|
||||
final int parentRight = right - left - getPaddingRight();
|
||||
|
||||
final int parentTop = getPaddingTop();
|
||||
final int parentBottom = bottom - top - getPaddingBottom();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
final View child = getChildAt(i);
|
||||
if (child.getVisibility() != GONE) {
|
||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
|
||||
final int width = child.getMeasuredWidth();
|
||||
final int height = child.getMeasuredHeight();
|
||||
|
||||
int childLeft = parentLeft + lp.leftMargin;
|
||||
int childTop;
|
||||
if (child == mActionBarBottom) {
|
||||
childTop = parentBottom - height - lp.bottomMargin;
|
||||
} else {
|
||||
childTop = parentTop + lp.topMargin;
|
||||
}
|
||||
|
||||
child.layout(childLeft, childTop, childLeft + width, childTop + height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas c) {
|
||||
super.draw(c);
|
||||
if (mWindowContentOverlay != null && !mIgnoreWindowContentOverlay) {
|
||||
final int top = mActionBarTop.getVisibility() == VISIBLE ?
|
||||
(int) (mActionBarTop.getBottom() + ViewCompat.getTranslationY(mActionBarTop) + 0.5f)
|
||||
: 0;
|
||||
mWindowContentOverlay.setBounds(0, top, getWidth(),
|
||||
top + mWindowContentOverlay.getIntrinsicHeight());
|
||||
mWindowContentOverlay.draw(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDelayChildPressedState() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStartNestedScroll(View child, View target, int axes) {
|
||||
if ((axes & SCROLL_AXIS_VERTICAL) == 0 || mActionBarTop.getVisibility() != VISIBLE) {
|
||||
return false;
|
||||
}
|
||||
return mHideOnContentScroll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNestedScrollAccepted(View child, View target, int axes) {
|
||||
super.onNestedScrollAccepted(child, target, axes);
|
||||
mHideOnContentScrollReference = getActionBarHideOffset();
|
||||
haltActionBarHideOffsetAnimations();
|
||||
if (mActionBarVisibilityCallback != null) {
|
||||
mActionBarVisibilityCallback.onContentScrollStarted();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
|
||||
int dxUnconsumed, int dyUnconsumed) {
|
||||
mHideOnContentScrollReference += dyConsumed;
|
||||
setActionBarHideOffset(mHideOnContentScrollReference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopNestedScroll(View target) {
|
||||
super.onStopNestedScroll(target);
|
||||
if (mHideOnContentScroll && !mAnimatingForFling) {
|
||||
if (mHideOnContentScrollReference <= mActionBarTop.getHeight()) {
|
||||
postRemoveActionBarHideOffset();
|
||||
} else {
|
||||
postAddActionBarHideOffset();
|
||||
}
|
||||
}
|
||||
if (mActionBarVisibilityCallback != null) {
|
||||
mActionBarVisibilityCallback.onContentScrollStopped();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
|
||||
if (!mHideOnContentScroll || !consumed) {
|
||||
return false;
|
||||
}
|
||||
if (shouldHideActionBarOnFling(velocityX, velocityY)) {
|
||||
addActionBarHideOffset();
|
||||
} else {
|
||||
removeActionBarHideOffset();
|
||||
}
|
||||
mAnimatingForFling = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void pullChildren() {
|
||||
if (mContent == null) {
|
||||
mContent = (ContentFrameLayout) findViewById(R.id.action_bar_activity_content);
|
||||
mActionBarTop = (ActionBarContainer) findViewById(R.id.action_bar_container);
|
||||
mDecorToolbar = getDecorToolbar(findViewById(R.id.action_bar));
|
||||
mActionBarBottom = (ActionBarContainer) findViewById(R.id.split_action_bar);
|
||||
}
|
||||
}
|
||||
|
||||
private DecorToolbar getDecorToolbar(View view) {
|
||||
if (view instanceof DecorToolbar) {
|
||||
return (DecorToolbar) view;
|
||||
} else if (view instanceof Toolbar) {
|
||||
return ((Toolbar) view).getWrapper();
|
||||
} else {
|
||||
throw new IllegalStateException("Can't make a decor toolbar out of " +
|
||||
view.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) {
|
||||
if (hideOnContentScroll != mHideOnContentScroll) {
|
||||
mHideOnContentScroll = hideOnContentScroll;
|
||||
if (!hideOnContentScroll) {
|
||||
if (VersionUtils.isAtLeastL()) {
|
||||
stopNestedScroll();
|
||||
}
|
||||
haltActionBarHideOffsetAnimations();
|
||||
setActionBarHideOffset(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHideOnContentScrollEnabled() {
|
||||
return mHideOnContentScroll;
|
||||
}
|
||||
|
||||
public int getActionBarHideOffset() {
|
||||
return mActionBarTop != null ? -((int) ViewCompat.getTranslationY(mActionBarTop)) : 0;
|
||||
}
|
||||
|
||||
public void setActionBarHideOffset(int offset) {
|
||||
haltActionBarHideOffsetAnimations();
|
||||
final int topHeight = mActionBarTop.getHeight();
|
||||
offset = Math.max(0, Math.min(offset, topHeight));
|
||||
ViewCompat.setTranslationY(mActionBarTop, -offset);
|
||||
if (mActionBarBottom != null && mActionBarBottom.getVisibility() != GONE) {
|
||||
// Match the hide offset proportionally for a split bar
|
||||
final float fOffset = (float) offset / topHeight;
|
||||
final int bOffset = (int) (mActionBarBottom.getHeight() * fOffset);
|
||||
ViewCompat.setTranslationY(mActionBarBottom, bOffset);
|
||||
}
|
||||
}
|
||||
|
||||
private void haltActionBarHideOffsetAnimations() {
|
||||
removeCallbacks(mRemoveActionBarHideOffset);
|
||||
removeCallbacks(mAddActionBarHideOffset);
|
||||
if (mCurrentActionBarTopAnimator != null) {
|
||||
mCurrentActionBarTopAnimator.cancel();
|
||||
}
|
||||
if (mCurrentActionBarBottomAnimator != null) {
|
||||
mCurrentActionBarBottomAnimator.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void postRemoveActionBarHideOffset() {
|
||||
haltActionBarHideOffsetAnimations();
|
||||
postDelayed(mRemoveActionBarHideOffset, ACTION_BAR_ANIMATE_DELAY);
|
||||
}
|
||||
|
||||
private void postAddActionBarHideOffset() {
|
||||
haltActionBarHideOffsetAnimations();
|
||||
postDelayed(mAddActionBarHideOffset, ACTION_BAR_ANIMATE_DELAY);
|
||||
}
|
||||
|
||||
private void removeActionBarHideOffset() {
|
||||
haltActionBarHideOffsetAnimations();
|
||||
mRemoveActionBarHideOffset.run();
|
||||
}
|
||||
|
||||
private void addActionBarHideOffset() {
|
||||
haltActionBarHideOffsetAnimations();
|
||||
mAddActionBarHideOffset.run();
|
||||
}
|
||||
|
||||
private boolean shouldHideActionBarOnFling(float velocityX, float velocityY) {
|
||||
mFlingEstimator.fling(0, 0, 0, (int) velocityY, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
|
||||
final int finalY = mFlingEstimator.getFinalY();
|
||||
return finalY > mActionBarTop.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindowCallback(Window.Callback cb) {
|
||||
pullChildren();
|
||||
mDecorToolbar.setWindowCallback(cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindowTitle(CharSequence title) {
|
||||
pullChildren();
|
||||
mDecorToolbar.setWindowTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
pullChildren();
|
||||
return mDecorToolbar.getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initFeature(int windowFeature) {
|
||||
pullChildren();
|
||||
switch (windowFeature) {
|
||||
case Window.FEATURE_PROGRESS:
|
||||
mDecorToolbar.initProgress();
|
||||
break;
|
||||
case Window.FEATURE_INDETERMINATE_PROGRESS:
|
||||
mDecorToolbar.initIndeterminateProgress();
|
||||
break;
|
||||
case Window.FEATURE_ACTION_BAR_OVERLAY:
|
||||
setOverlayMode(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUiOptions(int uiOptions) {
|
||||
// Split Action Bar not included.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIcon() {
|
||||
pullChildren();
|
||||
return mDecorToolbar.hasIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLogo() {
|
||||
pullChildren();
|
||||
return mDecorToolbar.hasLogo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIcon(int resId) {
|
||||
pullChildren();
|
||||
mDecorToolbar.setIcon(resId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIcon(Drawable d) {
|
||||
pullChildren();
|
||||
mDecorToolbar.setIcon(d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogo(int resId) {
|
||||
pullChildren();
|
||||
mDecorToolbar.setLogo(resId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canShowOverflowMenu() {
|
||||
pullChildren();
|
||||
return mDecorToolbar.canShowOverflowMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverflowMenuShowing() {
|
||||
pullChildren();
|
||||
return mDecorToolbar.isOverflowMenuShowing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverflowMenuShowPending() {
|
||||
pullChildren();
|
||||
return mDecorToolbar.isOverflowMenuShowPending();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showOverflowMenu() {
|
||||
pullChildren();
|
||||
return mDecorToolbar.showOverflowMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hideOverflowMenu() {
|
||||
pullChildren();
|
||||
return mDecorToolbar.hideOverflowMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMenuPrepared() {
|
||||
pullChildren();
|
||||
mDecorToolbar.setMenuPrepared();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMenu(Menu menu, MenuPresenter.Callback cb) {
|
||||
pullChildren();
|
||||
mDecorToolbar.setMenu(menu, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
|
||||
pullChildren();
|
||||
mDecorToolbar.saveHierarchyState(toolbarStates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
|
||||
pullChildren();
|
||||
mDecorToolbar.restoreHierarchyState(toolbarStates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismissPopups() {
|
||||
pullChildren();
|
||||
mDecorToolbar.dismissPopupMenus();
|
||||
}
|
||||
|
||||
public static class LayoutParams extends MarginLayoutParams {
|
||||
public LayoutParams(Context c, AttributeSet attrs) {
|
||||
super(c, attrs);
|
||||
}
|
||||
|
||||
public LayoutParams(int width, int height) {
|
||||
super(width, height);
|
||||
}
|
||||
|
||||
public LayoutParams(ViewGroup.LayoutParams source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
public LayoutParams(ViewGroup.MarginLayoutParams source) {
|
||||
super(source);
|
||||
}
|
||||
}
|
||||
|
||||
public interface ActionBarVisibilityCallback {
|
||||
void onWindowVisibilityChanged(int visibility);
|
||||
void showForSystem();
|
||||
void hideForSystem();
|
||||
void enableContentAnimations(boolean enable);
|
||||
void onContentScrollStarted();
|
||||
void onContentScrollStopped();
|
||||
}
|
||||
}
|
1100
android/support/v7/internal/widget/ActivityChooserModel.java
Normal file
1100
android/support/v7/internal/widget/ActivityChooserModel.java
Normal file
File diff suppressed because it is too large
Load diff
843
android/support/v7/internal/widget/ActivityChooserView.java
Normal file
843
android/support/v7/internal/widget/ActivityChooserView.java
Normal file
|
@ -0,0 +1,843 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.DataSetObserver;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.view.ActionProvider;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.widget.LinearLayoutCompat;
|
||||
import android.support.v7.widget.ListPopupWindow;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* This class is a view for choosing an activity for handling a given {@link Intent}.
|
||||
* <p>
|
||||
* The view is composed of two adjacent buttons:
|
||||
* <ul>
|
||||
* <li>
|
||||
* The left button is an immediate action and allows one click activity choosing.
|
||||
* Tapping this button immediately executes the intent without requiring any further
|
||||
* user input. Long press on this button shows a popup for changing the default
|
||||
* activity.
|
||||
* </li>
|
||||
* <li>
|
||||
* The right button is an overflow action and provides an optimized menu
|
||||
* of additional activities. Tapping this button shows a popup anchored to this
|
||||
* view, listing the most frequently used activities. This list is initially
|
||||
* limited to a small number of items in frequency used order. The last item,
|
||||
* "Show all..." serves as an affordance to display all available activities.
|
||||
* </li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ActivityChooserView extends ViewGroup implements
|
||||
ActivityChooserModel.ActivityChooserModelClient {
|
||||
|
||||
private static final String LOG_TAG = "ActivityChooserView";
|
||||
|
||||
/**
|
||||
* An adapter for displaying the activities in an {@link android.widget.AdapterView}.
|
||||
*/
|
||||
private final ActivityChooserViewAdapter mAdapter;
|
||||
|
||||
/**
|
||||
* Implementation of various interfaces to avoid publishing them in the APIs.
|
||||
*/
|
||||
private final Callbacks mCallbacks;
|
||||
|
||||
/**
|
||||
* The content of this view.
|
||||
*/
|
||||
private final LinearLayoutCompat mActivityChooserContent;
|
||||
|
||||
/**
|
||||
* Stores the background drawable to allow hiding and latter showing.
|
||||
*/
|
||||
private final Drawable mActivityChooserContentBackground;
|
||||
|
||||
/**
|
||||
* The expand activities action button;
|
||||
*/
|
||||
private final FrameLayout mExpandActivityOverflowButton;
|
||||
|
||||
/**
|
||||
* The image for the expand activities action button;
|
||||
*/
|
||||
private final ImageView mExpandActivityOverflowButtonImage;
|
||||
|
||||
/**
|
||||
* The default activities action button;
|
||||
*/
|
||||
private final FrameLayout mDefaultActivityButton;
|
||||
|
||||
/**
|
||||
* The image for the default activities action button;
|
||||
*/
|
||||
private final ImageView mDefaultActivityButtonImage;
|
||||
|
||||
/**
|
||||
* The maximal width of the list popup.
|
||||
*/
|
||||
private final int mListPopupMaxWidth;
|
||||
|
||||
/**
|
||||
* The ActionProvider hosting this view, if applicable.
|
||||
*/
|
||||
ActionProvider mProvider;
|
||||
|
||||
/**
|
||||
* Observer for the model data.
|
||||
*/
|
||||
private final DataSetObserver mModelDataSetOberver = new DataSetObserver() {
|
||||
|
||||
@Override
|
||||
public void onChanged() {
|
||||
super.onChanged();
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
@Override
|
||||
public void onInvalidated() {
|
||||
super.onInvalidated();
|
||||
mAdapter.notifyDataSetInvalidated();
|
||||
}
|
||||
};
|
||||
|
||||
private final OnGlobalLayoutListener mOnGlobalLayoutListener = new OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
if (isShowingPopup()) {
|
||||
if (!isShown()) {
|
||||
getListPopupWindow().dismiss();
|
||||
} else {
|
||||
getListPopupWindow().show();
|
||||
if (mProvider != null) {
|
||||
mProvider.subUiVisibilityChanged(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Popup window for showing the activity overflow list.
|
||||
*/
|
||||
private ListPopupWindow mListPopupWindow;
|
||||
|
||||
/**
|
||||
* Listener for the dismissal of the popup/alert.
|
||||
*/
|
||||
private PopupWindow.OnDismissListener mOnDismissListener;
|
||||
|
||||
/**
|
||||
* Flag whether a default activity currently being selected.
|
||||
*/
|
||||
private boolean mIsSelectingDefaultActivity;
|
||||
|
||||
/**
|
||||
* The count of activities in the popup.
|
||||
*/
|
||||
private int mInitialActivityCount = ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_DEFAULT;
|
||||
|
||||
/**
|
||||
* Flag whether this view is attached to a window.
|
||||
*/
|
||||
private boolean mIsAttachedToWindow;
|
||||
|
||||
/**
|
||||
* String resource for formatting content description of the default target.
|
||||
*/
|
||||
private int mDefaultActionButtonContentDescription;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param context The application environment.
|
||||
*/
|
||||
public ActivityChooserView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param context The application environment.
|
||||
* @param attrs A collection of attributes.
|
||||
*/
|
||||
public ActivityChooserView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param context The application environment.
|
||||
* @param attrs A collection of attributes.
|
||||
* @param defStyle The default style to apply to this view.
|
||||
*/
|
||||
public ActivityChooserView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
TypedArray attributesArray = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.ActivityChooserView, defStyle, 0);
|
||||
|
||||
mInitialActivityCount = attributesArray.getInt(
|
||||
R.styleable.ActivityChooserView_initialActivityCount,
|
||||
ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_DEFAULT);
|
||||
|
||||
Drawable expandActivityOverflowButtonDrawable = attributesArray.getDrawable(
|
||||
R.styleable.ActivityChooserView_expandActivityOverflowButtonDrawable);
|
||||
|
||||
attributesArray.recycle();
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
inflater.inflate(R.layout.abc_activity_chooser_view, this, true);
|
||||
|
||||
mCallbacks = new Callbacks();
|
||||
|
||||
mActivityChooserContent = (LinearLayoutCompat) findViewById(R.id.activity_chooser_view_content);
|
||||
mActivityChooserContentBackground = mActivityChooserContent.getBackground();
|
||||
|
||||
mDefaultActivityButton = (FrameLayout) findViewById(R.id.default_activity_button);
|
||||
mDefaultActivityButton.setOnClickListener(mCallbacks);
|
||||
mDefaultActivityButton.setOnLongClickListener(mCallbacks);
|
||||
mDefaultActivityButtonImage = (ImageView) mDefaultActivityButton.findViewById(R.id.image);
|
||||
|
||||
final FrameLayout expandButton = (FrameLayout) findViewById(R.id.expand_activities_button);
|
||||
expandButton.setOnClickListener(mCallbacks);
|
||||
expandButton.setOnTouchListener(new ListPopupWindow.ForwardingListener(expandButton) {
|
||||
@Override
|
||||
public ListPopupWindow getPopup() {
|
||||
return getListPopupWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onForwardingStarted() {
|
||||
showPopup();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onForwardingStopped() {
|
||||
dismissPopup();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
mExpandActivityOverflowButton = expandButton;
|
||||
mExpandActivityOverflowButtonImage =
|
||||
(ImageView) expandButton.findViewById(R.id.image);
|
||||
mExpandActivityOverflowButtonImage.setImageDrawable(expandActivityOverflowButtonDrawable);
|
||||
|
||||
mAdapter = new ActivityChooserViewAdapter();
|
||||
mAdapter.registerDataSetObserver(new DataSetObserver() {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
super.onChanged();
|
||||
updateAppearance();
|
||||
}
|
||||
});
|
||||
|
||||
Resources resources = context.getResources();
|
||||
mListPopupMaxWidth = Math.max(resources.getDisplayMetrics().widthPixels / 2,
|
||||
resources.getDimensionPixelSize(R.dimen.abc_config_prefDialogWidth));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void setActivityChooserModel(ActivityChooserModel dataModel) {
|
||||
mAdapter.setDataModel(dataModel);
|
||||
if (isShowingPopup()) {
|
||||
dismissPopup();
|
||||
showPopup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the background for the button that expands the activity
|
||||
* overflow list.
|
||||
*
|
||||
* <strong>Note:</strong> Clients would like to set this drawable
|
||||
* as a clue about the action the chosen activity will perform. For
|
||||
* example, if a share activity is to be chosen the drawable should
|
||||
* give a clue that sharing is to be performed.
|
||||
*
|
||||
* @param drawable The drawable.
|
||||
*/
|
||||
public void setExpandActivityOverflowButtonDrawable(Drawable drawable) {
|
||||
mExpandActivityOverflowButtonImage.setImageDrawable(drawable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content description for the button that expands the activity
|
||||
* overflow list.
|
||||
*
|
||||
* description as a clue about the action performed by the button.
|
||||
* For example, if a share activity is to be chosen the content
|
||||
* description should be something like "Share with".
|
||||
*
|
||||
* @param resourceId The content description resource id.
|
||||
*/
|
||||
public void setExpandActivityOverflowButtonContentDescription(int resourceId) {
|
||||
CharSequence contentDescription = getContext().getString(resourceId);
|
||||
mExpandActivityOverflowButtonImage.setContentDescription(contentDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the provider hosting this view, if applicable.
|
||||
* @hide Internal use only
|
||||
*/
|
||||
public void setProvider(ActionProvider provider) {
|
||||
mProvider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the popup window with activities.
|
||||
*
|
||||
* @return True if the popup was shown, false if already showing.
|
||||
*/
|
||||
public boolean showPopup() {
|
||||
if (isShowingPopup() || !mIsAttachedToWindow) {
|
||||
return false;
|
||||
}
|
||||
mIsSelectingDefaultActivity = false;
|
||||
showPopupUnchecked(mInitialActivityCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the popup no matter if it was already showing.
|
||||
*
|
||||
* @param maxActivityCount The max number of activities to display.
|
||||
*/
|
||||
private void showPopupUnchecked(int maxActivityCount) {
|
||||
if (mAdapter.getDataModel() == null) {
|
||||
throw new IllegalStateException("No data model. Did you call #setDataModel?");
|
||||
}
|
||||
|
||||
getViewTreeObserver().addOnGlobalLayoutListener(mOnGlobalLayoutListener);
|
||||
|
||||
final boolean defaultActivityButtonShown =
|
||||
mDefaultActivityButton.getVisibility() == VISIBLE;
|
||||
|
||||
final int activityCount = mAdapter.getActivityCount();
|
||||
final int maxActivityCountOffset = defaultActivityButtonShown ? 1 : 0;
|
||||
if (maxActivityCount != ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED
|
||||
&& activityCount > maxActivityCount + maxActivityCountOffset) {
|
||||
mAdapter.setShowFooterView(true);
|
||||
mAdapter.setMaxActivityCount(maxActivityCount - 1);
|
||||
} else {
|
||||
mAdapter.setShowFooterView(false);
|
||||
mAdapter.setMaxActivityCount(maxActivityCount);
|
||||
}
|
||||
|
||||
ListPopupWindow popupWindow = getListPopupWindow();
|
||||
if (!popupWindow.isShowing()) {
|
||||
if (mIsSelectingDefaultActivity || !defaultActivityButtonShown) {
|
||||
mAdapter.setShowDefaultActivity(true, defaultActivityButtonShown);
|
||||
} else {
|
||||
mAdapter.setShowDefaultActivity(false, false);
|
||||
}
|
||||
final int contentWidth = Math.min(mAdapter.measureContentWidth(), mListPopupMaxWidth);
|
||||
popupWindow.setContentWidth(contentWidth);
|
||||
popupWindow.show();
|
||||
if (mProvider != null) {
|
||||
mProvider.subUiVisibilityChanged(true);
|
||||
}
|
||||
popupWindow.getListView().setContentDescription(getContext().getString(
|
||||
R.string.abc_activitychooserview_choose_application));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses the popup window with activities.
|
||||
*
|
||||
* @return True if dismissed, false if already dismissed.
|
||||
*/
|
||||
public boolean dismissPopup() {
|
||||
if (isShowingPopup()) {
|
||||
getListPopupWindow().dismiss();
|
||||
ViewTreeObserver viewTreeObserver = getViewTreeObserver();
|
||||
if (viewTreeObserver.isAlive()) {
|
||||
viewTreeObserver.removeGlobalOnLayoutListener(mOnGlobalLayoutListener);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the popup window with activities is shown.
|
||||
*
|
||||
* @return True if the popup is shown.
|
||||
*/
|
||||
public boolean isShowingPopup() {
|
||||
return getListPopupWindow().isShowing();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
ActivityChooserModel dataModel = mAdapter.getDataModel();
|
||||
if (dataModel != null) {
|
||||
dataModel.registerObserver(mModelDataSetOberver);
|
||||
}
|
||||
mIsAttachedToWindow = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
ActivityChooserModel dataModel = mAdapter.getDataModel();
|
||||
if (dataModel != null) {
|
||||
dataModel.unregisterObserver(mModelDataSetOberver);
|
||||
}
|
||||
ViewTreeObserver viewTreeObserver = getViewTreeObserver();
|
||||
if (viewTreeObserver.isAlive()) {
|
||||
viewTreeObserver.removeGlobalOnLayoutListener(mOnGlobalLayoutListener);
|
||||
}
|
||||
if (isShowingPopup()) {
|
||||
dismissPopup();
|
||||
}
|
||||
mIsAttachedToWindow = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
View child = mActivityChooserContent;
|
||||
// If the default action is not visible we want to be as tall as the
|
||||
// ActionBar so if this widget is used in the latter it will look as
|
||||
// a normal action button.
|
||||
if (mDefaultActivityButton.getVisibility() != VISIBLE) {
|
||||
heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
|
||||
MeasureSpec.EXACTLY);
|
||||
}
|
||||
measureChild(child, widthMeasureSpec, heightMeasureSpec);
|
||||
setMeasuredDimension(child.getMeasuredWidth(), child.getMeasuredHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
mActivityChooserContent.layout(0, 0, right - left, bottom - top);
|
||||
if (!isShowingPopup()) {
|
||||
dismissPopup();
|
||||
}
|
||||
}
|
||||
|
||||
public ActivityChooserModel getDataModel() {
|
||||
return mAdapter.getDataModel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a listener to receive a callback when the popup is dismissed.
|
||||
*
|
||||
* @param listener The listener to be notified.
|
||||
*/
|
||||
public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
|
||||
mOnDismissListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial count of items shown in the activities popup
|
||||
* i.e. the items before the popup is expanded. This is an upper
|
||||
* bound since it is not guaranteed that such number of intent
|
||||
* handlers exist.
|
||||
*
|
||||
* @param itemCount The initial popup item count.
|
||||
*/
|
||||
public void setInitialActivityCount(int itemCount) {
|
||||
mInitialActivityCount = itemCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a content description of the default action button. This
|
||||
* resource should be a string taking one formatting argument and
|
||||
* will be used for formatting the content description of the button
|
||||
* dynamically as the default target changes. For example, a resource
|
||||
* pointing to the string "share with %1$s" will result in a content
|
||||
* description "share with Bluetooth" for the Bluetooth activity.
|
||||
*
|
||||
* @param resourceId The resource id.
|
||||
*/
|
||||
public void setDefaultActionButtonContentDescription(int resourceId) {
|
||||
mDefaultActionButtonContentDescription = resourceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list popup window which is lazily initialized.
|
||||
*
|
||||
* @return The popup.
|
||||
*/
|
||||
private ListPopupWindow getListPopupWindow() {
|
||||
if (mListPopupWindow == null) {
|
||||
mListPopupWindow = new ListPopupWindow(getContext());
|
||||
mListPopupWindow.setAdapter(mAdapter);
|
||||
mListPopupWindow.setAnchorView(ActivityChooserView.this);
|
||||
mListPopupWindow.setModal(true);
|
||||
mListPopupWindow.setOnItemClickListener(mCallbacks);
|
||||
mListPopupWindow.setOnDismissListener(mCallbacks);
|
||||
}
|
||||
return mListPopupWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the buttons state.
|
||||
*/
|
||||
private void updateAppearance() {
|
||||
// Expand overflow button.
|
||||
if (mAdapter.getCount() > 0) {
|
||||
mExpandActivityOverflowButton.setEnabled(true);
|
||||
} else {
|
||||
mExpandActivityOverflowButton.setEnabled(false);
|
||||
}
|
||||
// Default activity button.
|
||||
final int activityCount = mAdapter.getActivityCount();
|
||||
final int historySize = mAdapter.getHistorySize();
|
||||
if (activityCount==1 || activityCount > 1 && historySize > 0) {
|
||||
mDefaultActivityButton.setVisibility(VISIBLE);
|
||||
ResolveInfo activity = mAdapter.getDefaultActivity();
|
||||
PackageManager packageManager = getContext().getPackageManager();
|
||||
mDefaultActivityButtonImage.setImageDrawable(activity.loadIcon(packageManager));
|
||||
if (mDefaultActionButtonContentDescription != 0) {
|
||||
CharSequence label = activity.loadLabel(packageManager);
|
||||
String contentDescription = getContext().getString(
|
||||
mDefaultActionButtonContentDescription, label);
|
||||
mDefaultActivityButton.setContentDescription(contentDescription);
|
||||
}
|
||||
} else {
|
||||
mDefaultActivityButton.setVisibility(View.GONE);
|
||||
}
|
||||
// Activity chooser content.
|
||||
if (mDefaultActivityButton.getVisibility() == VISIBLE) {
|
||||
mActivityChooserContent.setBackgroundDrawable(mActivityChooserContentBackground);
|
||||
} else {
|
||||
mActivityChooserContent.setBackgroundDrawable(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface implementation to avoid publishing them in the APIs.
|
||||
*/
|
||||
private class Callbacks implements AdapterView.OnItemClickListener,
|
||||
View.OnClickListener, View.OnLongClickListener, PopupWindow.OnDismissListener {
|
||||
|
||||
// AdapterView#OnItemClickListener
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
ActivityChooserViewAdapter adapter = (ActivityChooserViewAdapter) parent.getAdapter();
|
||||
final int itemViewType = adapter.getItemViewType(position);
|
||||
switch (itemViewType) {
|
||||
case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_FOOTER: {
|
||||
showPopupUnchecked(ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED);
|
||||
} break;
|
||||
case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_ACTIVITY: {
|
||||
dismissPopup();
|
||||
if (mIsSelectingDefaultActivity) {
|
||||
// The item at position zero is the default already.
|
||||
if (position > 0) {
|
||||
mAdapter.getDataModel().setDefaultActivity(position);
|
||||
}
|
||||
} else {
|
||||
// If the default target is not shown in the list, the first
|
||||
// item in the model is default action => adjust index
|
||||
position = mAdapter.getShowDefaultActivity() ? position : position + 1;
|
||||
Intent launchIntent = mAdapter.getDataModel().chooseActivity(position);
|
||||
if (launchIntent != null) {
|
||||
launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||
getContext().startActivity(launchIntent);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
// View.OnClickListener
|
||||
public void onClick(View view) {
|
||||
if (view == mDefaultActivityButton) {
|
||||
dismissPopup();
|
||||
ResolveInfo defaultActivity = mAdapter.getDefaultActivity();
|
||||
final int index = mAdapter.getDataModel().getActivityIndex(defaultActivity);
|
||||
Intent launchIntent = mAdapter.getDataModel().chooseActivity(index);
|
||||
if (launchIntent != null) {
|
||||
launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||
getContext().startActivity(launchIntent);
|
||||
}
|
||||
} else if (view == mExpandActivityOverflowButton) {
|
||||
mIsSelectingDefaultActivity = false;
|
||||
showPopupUnchecked(mInitialActivityCount);
|
||||
} else {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
// OnLongClickListener#onLongClick
|
||||
@Override
|
||||
public boolean onLongClick(View view) {
|
||||
if (view == mDefaultActivityButton) {
|
||||
if (mAdapter.getCount() > 0) {
|
||||
mIsSelectingDefaultActivity = true;
|
||||
showPopupUnchecked(mInitialActivityCount);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// PopUpWindow.OnDismissListener#onDismiss
|
||||
public void onDismiss() {
|
||||
notifyOnDismissListener();
|
||||
if (mProvider != null) {
|
||||
mProvider.subUiVisibilityChanged(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyOnDismissListener() {
|
||||
if (mOnDismissListener != null) {
|
||||
mOnDismissListener.onDismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter for backing the list of activities shown in the popup.
|
||||
*/
|
||||
private class ActivityChooserViewAdapter extends BaseAdapter {
|
||||
|
||||
public static final int MAX_ACTIVITY_COUNT_UNLIMITED = Integer.MAX_VALUE;
|
||||
|
||||
public static final int MAX_ACTIVITY_COUNT_DEFAULT = 4;
|
||||
|
||||
private static final int ITEM_VIEW_TYPE_ACTIVITY = 0;
|
||||
|
||||
private static final int ITEM_VIEW_TYPE_FOOTER = 1;
|
||||
|
||||
private static final int ITEM_VIEW_TYPE_COUNT = 3;
|
||||
|
||||
private ActivityChooserModel mDataModel;
|
||||
|
||||
private int mMaxActivityCount = MAX_ACTIVITY_COUNT_DEFAULT;
|
||||
|
||||
private boolean mShowDefaultActivity;
|
||||
|
||||
private boolean mHighlightDefaultActivity;
|
||||
|
||||
private boolean mShowFooterView;
|
||||
|
||||
public void setDataModel(ActivityChooserModel dataModel) {
|
||||
ActivityChooserModel oldDataModel = mAdapter.getDataModel();
|
||||
if (oldDataModel != null && isShown()) {
|
||||
oldDataModel.unregisterObserver(mModelDataSetOberver);
|
||||
}
|
||||
mDataModel = dataModel;
|
||||
if (dataModel != null && isShown()) {
|
||||
dataModel.registerObserver(mModelDataSetOberver);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (mShowFooterView && position == getCount() - 1) {
|
||||
return ITEM_VIEW_TYPE_FOOTER;
|
||||
} else {
|
||||
return ITEM_VIEW_TYPE_ACTIVITY;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return ITEM_VIEW_TYPE_COUNT;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
int count = 0;
|
||||
int activityCount = mDataModel.getActivityCount();
|
||||
if (!mShowDefaultActivity && mDataModel.getDefaultActivity() != null) {
|
||||
activityCount--;
|
||||
}
|
||||
count = Math.min(activityCount, mMaxActivityCount);
|
||||
if (mShowFooterView) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
final int itemViewType = getItemViewType(position);
|
||||
switch (itemViewType) {
|
||||
case ITEM_VIEW_TYPE_FOOTER:
|
||||
return null;
|
||||
case ITEM_VIEW_TYPE_ACTIVITY:
|
||||
if (!mShowDefaultActivity && mDataModel.getDefaultActivity() != null) {
|
||||
position++;
|
||||
}
|
||||
return mDataModel.getActivity(position);
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final int itemViewType = getItemViewType(position);
|
||||
switch (itemViewType) {
|
||||
case ITEM_VIEW_TYPE_FOOTER:
|
||||
if (convertView == null || convertView.getId() != ITEM_VIEW_TYPE_FOOTER) {
|
||||
convertView = LayoutInflater.from(getContext()).inflate(
|
||||
R.layout.abc_activity_chooser_view_list_item, parent, false);
|
||||
convertView.setId(ITEM_VIEW_TYPE_FOOTER);
|
||||
TextView titleView = (TextView) convertView.findViewById(R.id.title);
|
||||
titleView.setText(getContext().getString(
|
||||
R.string.abc_activity_chooser_view_see_all));
|
||||
}
|
||||
return convertView;
|
||||
case ITEM_VIEW_TYPE_ACTIVITY:
|
||||
if (convertView == null || convertView.getId() != R.id.list_item) {
|
||||
convertView = LayoutInflater.from(getContext()).inflate(
|
||||
R.layout.abc_activity_chooser_view_list_item, parent, false);
|
||||
}
|
||||
PackageManager packageManager = getContext().getPackageManager();
|
||||
// Set the icon
|
||||
ImageView iconView = (ImageView) convertView.findViewById(R.id.icon);
|
||||
ResolveInfo activity = (ResolveInfo) getItem(position);
|
||||
iconView.setImageDrawable(activity.loadIcon(packageManager));
|
||||
// Set the title.
|
||||
TextView titleView = (TextView) convertView.findViewById(R.id.title);
|
||||
titleView.setText(activity.loadLabel(packageManager));
|
||||
// Highlight the default.
|
||||
if (mShowDefaultActivity && position == 0 && mHighlightDefaultActivity) {
|
||||
ViewCompat.setActivated(convertView, true);
|
||||
} else {
|
||||
ViewCompat.setActivated(convertView, false);
|
||||
}
|
||||
return convertView;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
public int measureContentWidth() {
|
||||
// The user may have specified some of the target not to be shown but we
|
||||
// want to measure all of them since after expansion they should fit.
|
||||
final int oldMaxActivityCount = mMaxActivityCount;
|
||||
mMaxActivityCount = MAX_ACTIVITY_COUNT_UNLIMITED;
|
||||
|
||||
int contentWidth = 0;
|
||||
View itemView = null;
|
||||
|
||||
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
final int count = getCount();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
itemView = getView(i, itemView, null);
|
||||
itemView.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
contentWidth = Math.max(contentWidth, itemView.getMeasuredWidth());
|
||||
}
|
||||
|
||||
mMaxActivityCount = oldMaxActivityCount;
|
||||
|
||||
return contentWidth;
|
||||
}
|
||||
|
||||
public void setMaxActivityCount(int maxActivityCount) {
|
||||
if (mMaxActivityCount != maxActivityCount) {
|
||||
mMaxActivityCount = maxActivityCount;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ResolveInfo getDefaultActivity() {
|
||||
return mDataModel.getDefaultActivity();
|
||||
}
|
||||
|
||||
public void setShowFooterView(boolean showFooterView) {
|
||||
if (mShowFooterView != showFooterView) {
|
||||
mShowFooterView = showFooterView;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public int getActivityCount() {
|
||||
return mDataModel.getActivityCount();
|
||||
}
|
||||
|
||||
public int getHistorySize() {
|
||||
return mDataModel.getHistorySize();
|
||||
}
|
||||
|
||||
public ActivityChooserModel getDataModel() {
|
||||
return mDataModel;
|
||||
}
|
||||
|
||||
public void setShowDefaultActivity(boolean showDefaultActivity,
|
||||
boolean highlightDefaultActivity) {
|
||||
if (mShowDefaultActivity != showDefaultActivity
|
||||
|| mHighlightDefaultActivity != highlightDefaultActivity) {
|
||||
mShowDefaultActivity = showDefaultActivity;
|
||||
mHighlightDefaultActivity = highlightDefaultActivity;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getShowDefaultActivity() {
|
||||
return mShowDefaultActivity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows us to set the background using TintTypedArray
|
||||
* @hide
|
||||
*/
|
||||
public static class InnerLayout extends LinearLayoutCompat {
|
||||
|
||||
private static final int[] TINT_ATTRS = {
|
||||
android.R.attr.background
|
||||
};
|
||||
|
||||
public InnerLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS);
|
||||
setBackgroundDrawable(a.getDrawable(0));
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
}
|
1150
android/support/v7/internal/widget/AdapterViewCompat.java
Normal file
1150
android/support/v7/internal/widget/AdapterViewCompat.java
Normal file
File diff suppressed because it is too large
Load diff
120
android/support/v7/internal/widget/AppCompatPopupWindow.java
Normal file
120
android/support/v7/internal/widget/AppCompatPopupWindow.java
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver.OnScrollChangedListener;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class AppCompatPopupWindow extends PopupWindow {
|
||||
|
||||
private static final String TAG = "AppCompatPopupWindow";
|
||||
|
||||
private final boolean mOverlapAnchor;
|
||||
|
||||
public AppCompatPopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
|
||||
R.styleable.PopupWindow, defStyleAttr, 0);
|
||||
mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
|
||||
// We re-set this for tinting purposes
|
||||
setBackgroundDrawable(a.getDrawable(R.styleable.PopupWindow_android_popupBackground));
|
||||
a.recycle();
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
// For devices pre-ICS, we need to wrap the internal OnScrollChangedListener
|
||||
// due to NPEs.
|
||||
wrapOnScrollChangedListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showAsDropDown(View anchor, int xoff, int yoff) {
|
||||
if (Build.VERSION.SDK_INT < 21 && mOverlapAnchor) {
|
||||
// If we're pre-L, emulate overlapAnchor by modifying the yOff
|
||||
yoff -= anchor.getHeight();
|
||||
}
|
||||
super.showAsDropDown(anchor, xoff, yoff);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
@Override
|
||||
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
|
||||
if (Build.VERSION.SDK_INT < 21 && mOverlapAnchor) {
|
||||
// If we're pre-L, emulate overlapAnchor by modifying the yOff
|
||||
yoff -= anchor.getHeight();
|
||||
}
|
||||
super.showAsDropDown(anchor, xoff, yoff, gravity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(View anchor, int xoff, int yoff, int width, int height) {
|
||||
if (Build.VERSION.SDK_INT < 21 && mOverlapAnchor) {
|
||||
// If we're pre-L, emulate overlapAnchor by modifying the yOff
|
||||
yoff -= anchor.getHeight();
|
||||
}
|
||||
super.update(anchor, xoff, yoff, width, height);
|
||||
}
|
||||
|
||||
private static void wrapOnScrollChangedListener(final PopupWindow popup) {
|
||||
try {
|
||||
final Field fieldAnchor = PopupWindow.class.getDeclaredField("mAnchor");
|
||||
fieldAnchor.setAccessible(true);
|
||||
|
||||
final Field fieldListener = PopupWindow.class
|
||||
.getDeclaredField("mOnScrollChangedListener");
|
||||
fieldListener.setAccessible(true);
|
||||
|
||||
final OnScrollChangedListener originalListener =
|
||||
(OnScrollChangedListener) fieldListener.get(popup);
|
||||
|
||||
// Now set a new listener, wrapping the original and only proxying the call when
|
||||
// we have an anchor view.
|
||||
fieldListener.set(popup, new OnScrollChangedListener() {
|
||||
@Override
|
||||
public void onScrollChanged() {
|
||||
try {
|
||||
WeakReference<View> mAnchor = (WeakReference<View>) fieldAnchor.get(popup);
|
||||
if (mAnchor == null || mAnchor.get() == null) {
|
||||
return;
|
||||
} else {
|
||||
originalListener.onScrollChanged();
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
// Oh well...
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Exception while installing workaround OnScrollChangedListener", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
84
android/support/v7/internal/widget/CompatTextView.java
Normal file
84
android/support/v7/internal/widget/CompatTextView.java
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Rect;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.internal.text.AllCapsTransformationMethod;
|
||||
import android.text.method.TransformationMethod;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class CompatTextView extends TextView {
|
||||
|
||||
public CompatTextView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public CompatTextView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public CompatTextView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
// First read the TextAppearance style id
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CompatTextView,
|
||||
defStyle, 0);
|
||||
final int ap = a.getResourceId(R.styleable.CompatTextView_android_textAppearance, -1);
|
||||
a.recycle();
|
||||
|
||||
// Now check TextAppearance's textAllCaps value
|
||||
if (ap != -1) {
|
||||
TypedArray appearance = context.obtainStyledAttributes(ap, R.styleable.TextAppearance);
|
||||
if (appearance.hasValue(R.styleable.TextAppearance_textAllCaps)) {
|
||||
setAllCaps(appearance.getBoolean(R.styleable.TextAppearance_textAllCaps, false));
|
||||
}
|
||||
appearance.recycle();
|
||||
}
|
||||
|
||||
// Now read the style's value
|
||||
a = context.obtainStyledAttributes(attrs, R.styleable.CompatTextView, defStyle, 0);
|
||||
if (a.hasValue(R.styleable.CompatTextView_textAllCaps)) {
|
||||
setAllCaps(a.getBoolean(R.styleable.CompatTextView_textAllCaps, false));
|
||||
}
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
public void setAllCaps(boolean allCaps) {
|
||||
setTransformationMethod(allCaps ? new AllCapsTransformationMethod(getContext()) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTextAppearance(Context context, int resid) {
|
||||
super.setTextAppearance(context, resid);
|
||||
|
||||
TypedArray appearance = context.obtainStyledAttributes(resid, R.styleable.TextAppearance);
|
||||
if (appearance.hasValue(R.styleable.TextAppearance_textAllCaps)) {
|
||||
setAllCaps(appearance.getBoolean(R.styleable.TextAppearance_textAllCaps, false));
|
||||
}
|
||||
appearance.recycle();
|
||||
}
|
||||
}
|
48
android/support/v7/internal/widget/ContentFrameLayout.java
Normal file
48
android/support/v7/internal/widget/ContentFrameLayout.java
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class ContentFrameLayout extends FrameLayout {
|
||||
|
||||
public ContentFrameLayout(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ContentFrameLayout(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ContentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void dispatchFitSystemWindows(Rect insets) {
|
||||
fitSystemWindows(insets);
|
||||
}
|
||||
|
||||
}
|
55
android/support/v7/internal/widget/DecorContentParent.java
Normal file
55
android/support/v7/internal/widget/DecorContentParent.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v7.internal.view.menu.MenuPresenter;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Menu;
|
||||
import android.view.Window;
|
||||
|
||||
/**
|
||||
* Implemented by the top-level decor layout for a window. DecorContentParent offers
|
||||
* entry points for a number of title/window decor features.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public interface DecorContentParent {
|
||||
void setWindowCallback(Window.Callback cb);
|
||||
void setWindowTitle(CharSequence title);
|
||||
CharSequence getTitle();
|
||||
void initFeature(int windowFeature);
|
||||
void setUiOptions(int uiOptions);
|
||||
boolean hasIcon();
|
||||
boolean hasLogo();
|
||||
void setIcon(int resId);
|
||||
void setIcon(Drawable d);
|
||||
void setLogo(int resId);
|
||||
boolean canShowOverflowMenu();
|
||||
boolean isOverflowMenuShowing();
|
||||
boolean isOverflowMenuShowPending();
|
||||
boolean showOverflowMenu();
|
||||
boolean hideOverflowMenu();
|
||||
void setMenuPrepared();
|
||||
void setMenu(Menu menu, MenuPresenter.Callback cb);
|
||||
void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates);
|
||||
void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates);
|
||||
void dismissPopups();
|
||||
|
||||
}
|
104
android/support/v7/internal/widget/DecorToolbar.java
Normal file
104
android/support/v7/internal/widget/DecorToolbar.java
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v7.internal.view.menu.MenuBuilder;
|
||||
import android.support.v7.internal.view.menu.MenuPresenter;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Menu;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.SpinnerAdapter;
|
||||
|
||||
/**
|
||||
* Common interface for a toolbar that sits as part of the window decor.
|
||||
* Layouts that control window decor use this as a point of interaction with different
|
||||
* bar implementations.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public interface DecorToolbar {
|
||||
ViewGroup getViewGroup();
|
||||
Context getContext();
|
||||
boolean isSplit();
|
||||
boolean hasExpandedActionView();
|
||||
void collapseActionView();
|
||||
void setWindowCallback(Window.Callback cb);
|
||||
void setWindowTitle(CharSequence title);
|
||||
CharSequence getTitle();
|
||||
void setTitle(CharSequence title);
|
||||
CharSequence getSubtitle();
|
||||
void setSubtitle(CharSequence subtitle);
|
||||
void initProgress();
|
||||
void initIndeterminateProgress();
|
||||
boolean canSplit();
|
||||
void setSplitView(ViewGroup splitView);
|
||||
void setSplitToolbar(boolean split);
|
||||
void setSplitWhenNarrow(boolean splitWhenNarrow);
|
||||
boolean hasIcon();
|
||||
boolean hasLogo();
|
||||
void setIcon(int resId);
|
||||
void setIcon(Drawable d);
|
||||
void setLogo(int resId);
|
||||
void setLogo(Drawable d);
|
||||
boolean canShowOverflowMenu();
|
||||
boolean isOverflowMenuShowing();
|
||||
boolean isOverflowMenuShowPending();
|
||||
boolean showOverflowMenu();
|
||||
boolean hideOverflowMenu();
|
||||
void setMenuPrepared();
|
||||
void setMenu(Menu menu, MenuPresenter.Callback cb);
|
||||
void dismissPopupMenus();
|
||||
|
||||
int getDisplayOptions();
|
||||
void setDisplayOptions(int opts);
|
||||
void setEmbeddedTabView(ScrollingTabContainerView tabView);
|
||||
boolean hasEmbeddedTabs();
|
||||
boolean isTitleTruncated();
|
||||
void setCollapsible(boolean collapsible);
|
||||
void setHomeButtonEnabled(boolean enable);
|
||||
int getNavigationMode();
|
||||
void setNavigationMode(int mode);
|
||||
void setDropdownParams(SpinnerAdapter adapter, AdapterViewCompat.OnItemSelectedListener listener);
|
||||
void setDropdownSelectedPosition(int position);
|
||||
int getDropdownSelectedPosition();
|
||||
int getDropdownItemCount();
|
||||
void setCustomView(View view);
|
||||
View getCustomView();
|
||||
void animateToVisibility(int visibility);
|
||||
void setNavigationIcon(Drawable icon);
|
||||
void setNavigationIcon(int resId);
|
||||
void setNavigationContentDescription(CharSequence description);
|
||||
void setNavigationContentDescription(int resId);
|
||||
void setDefaultNavigationContentDescription(int defaultNavigationContentDescription);
|
||||
void setDefaultNavigationIcon(Drawable icon);
|
||||
void saveHierarchyState(SparseArray<Parcelable> toolbarStates);
|
||||
void restoreHierarchyState(SparseArray<Parcelable> toolbarStates);
|
||||
void setBackgroundDrawable(Drawable d);
|
||||
int getHeight();
|
||||
void setVisibility(int visible);
|
||||
int getVisibility();
|
||||
void setMenuCallbacks(MenuPresenter.Callback presenterCallback,
|
||||
MenuBuilder.Callback menuBuilderCallback);
|
||||
Menu getMenu();
|
||||
int getPopupTheme();
|
||||
}
|
99
android/support/v7/internal/widget/DrawableUtils.java
Normal file
99
android/support/v7/internal/widget/DrawableUtils.java
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class DrawableUtils {
|
||||
|
||||
private static final String TAG = "DrawableUtils";
|
||||
|
||||
public static final Rect INSETS_NONE = new Rect();
|
||||
|
||||
private static Class<?> sInsetsClazz;
|
||||
|
||||
static {
|
||||
if (Build.VERSION.SDK_INT >= 18) {
|
||||
try {
|
||||
sInsetsClazz = Class.forName("android.graphics.Insets");
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Oh well...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DrawableUtils() {}
|
||||
|
||||
/**
|
||||
* Allows us to get the optical insets for a {@link Drawable}. Since this is hidden we need to
|
||||
* use reflection. Since the {@code Insets} class is hidden also, we return a Rect instead.
|
||||
*/
|
||||
public static Rect getOpticalBounds(Drawable drawable) {
|
||||
if (sInsetsClazz != null) {
|
||||
try {
|
||||
// If the Drawable is wrapped, we need to manually unwrap it and process
|
||||
// the wrapped drawable.
|
||||
drawable = DrawableCompat.unwrap(drawable);
|
||||
|
||||
final Method getOpticalInsetsMethod = drawable.getClass()
|
||||
.getMethod("getOpticalInsets");
|
||||
final Object insets = getOpticalInsetsMethod.invoke(drawable);
|
||||
|
||||
if (insets != null) {
|
||||
// If the drawable has some optical insets, let's copy them into a Rect
|
||||
final Rect result = new Rect();
|
||||
|
||||
for (Field field : sInsetsClazz.getFields()) {
|
||||
switch (field.getName()) {
|
||||
case "left":
|
||||
result.left = field.getInt(insets);
|
||||
break;
|
||||
case "top":
|
||||
result.top = field.getInt(insets);
|
||||
break;
|
||||
case "right":
|
||||
result.right = field.getInt(insets);
|
||||
break;
|
||||
case "bottom":
|
||||
result.bottom = field.getInt(insets);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Eugh, we hit some kind of reflection issue...
|
||||
Log.e(TAG, "Couldn't obtain the optical insets. Ignoring.");
|
||||
}
|
||||
}
|
||||
|
||||
// If we reach here, either we're running on a device pre-v18, the Drawable didn't have
|
||||
// any optical insets, or a reflection issue, so we'll just return an empty rect
|
||||
return INSETS_NONE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class FitWindowsFrameLayout extends FrameLayout implements FitWindowsViewGroup {
|
||||
|
||||
private OnFitSystemWindowsListener mListener;
|
||||
|
||||
public FitWindowsFrameLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public FitWindowsFrameLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnFitSystemWindowsListener(OnFitSystemWindowsListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean fitSystemWindows(Rect insets) {
|
||||
if (mListener != null) {
|
||||
mListener.onFitSystemWindows(insets);
|
||||
}
|
||||
return super.fitSystemWindows(insets);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class FitWindowsLinearLayout extends LinearLayout implements FitWindowsViewGroup {
|
||||
|
||||
private OnFitSystemWindowsListener mListener;
|
||||
|
||||
public FitWindowsLinearLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public FitWindowsLinearLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void setOnFitSystemWindowsListener(OnFitSystemWindowsListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean fitSystemWindows(Rect insets) {
|
||||
if (mListener != null) {
|
||||
mListener.onFitSystemWindows(insets);
|
||||
}
|
||||
return super.fitSystemWindows(insets);
|
||||
}
|
||||
}
|
32
android/support/v7/internal/widget/FitWindowsViewGroup.java
Normal file
32
android/support/v7/internal/widget/FitWindowsViewGroup.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.graphics.Rect;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public interface FitWindowsViewGroup {
|
||||
|
||||
public interface OnFitSystemWindowsListener {
|
||||
void onFitSystemWindows(Rect insets);
|
||||
}
|
||||
|
||||
public void setOnFitSystemWindowsListener(OnFitSystemWindowsListener listener);
|
||||
|
||||
}
|
388
android/support/v7/internal/widget/ListViewCompat.java
Normal file
388
android/support/v7/internal/widget/ListViewCompat.java
Normal file
|
@ -0,0 +1,388 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.support.v7.graphics.drawable.DrawableWrapper;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* This class contains a number of useful things for ListView. Mainly used by
|
||||
* {@link android.support.v7.widget.ListPopupWindow}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ListViewCompat extends ListView {
|
||||
|
||||
public static final int INVALID_POSITION = -1;
|
||||
public static final int NO_POSITION = -1;
|
||||
|
||||
private static final int[] STATE_SET_NOTHING = new int[] { 0 };
|
||||
|
||||
final Rect mSelectorRect = new Rect();
|
||||
int mSelectionLeftPadding = 0;
|
||||
int mSelectionTopPadding = 0;
|
||||
int mSelectionRightPadding = 0;
|
||||
int mSelectionBottomPadding = 0;
|
||||
|
||||
private Field mIsChildViewEnabled;
|
||||
|
||||
private GateKeeperDrawable mSelector;
|
||||
|
||||
public ListViewCompat(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ListViewCompat(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ListViewCompat(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
try {
|
||||
mIsChildViewEnabled = AbsListView.class.getDeclaredField("mIsChildViewEnabled");
|
||||
mIsChildViewEnabled.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelector(Drawable sel) {
|
||||
mSelector = sel != null ? new GateKeeperDrawable(sel) : null;
|
||||
super.setSelector(mSelector);
|
||||
|
||||
final Rect padding = new Rect();
|
||||
if (sel != null) {
|
||||
sel.getPadding(padding);
|
||||
}
|
||||
|
||||
mSelectionLeftPadding = padding.left;
|
||||
mSelectionTopPadding = padding.top;
|
||||
mSelectionRightPadding = padding.right;
|
||||
mSelectionBottomPadding = padding.bottom;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawableStateChanged() {
|
||||
super.drawableStateChanged();
|
||||
|
||||
setSelectorEnabled(true);
|
||||
updateSelectorStateCompat();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
final boolean drawSelectorOnTop = false;
|
||||
if (!drawSelectorOnTop) {
|
||||
drawSelectorCompat(canvas);
|
||||
}
|
||||
|
||||
super.dispatchDraw(canvas);
|
||||
}
|
||||
|
||||
protected void updateSelectorStateCompat() {
|
||||
Drawable selector = getSelector();
|
||||
if (selector != null && shouldShowSelectorCompat()) {
|
||||
selector.setState(getDrawableState());
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean shouldShowSelectorCompat() {
|
||||
return touchModeDrawsInPressedStateCompat() && isPressed();
|
||||
}
|
||||
|
||||
protected boolean touchModeDrawsInPressedStateCompat() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void drawSelectorCompat(Canvas canvas) {
|
||||
if (!mSelectorRect.isEmpty()) {
|
||||
final Drawable selector = getSelector();
|
||||
if (selector != null) {
|
||||
selector.setBounds(mSelectorRect);
|
||||
selector.draw(canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a position that can be selected (i.e., is not a separator).
|
||||
*
|
||||
* @param position The starting position to look at.
|
||||
* @param lookDown Whether to look down for other positions.
|
||||
* @return The next selectable position starting at position and then searching either up or
|
||||
* down. Returns {@link #INVALID_POSITION} if nothing can be found.
|
||||
*/
|
||||
public int lookForSelectablePosition(int position, boolean lookDown) {
|
||||
final ListAdapter adapter = getAdapter();
|
||||
if (adapter == null || isInTouchMode()) {
|
||||
return INVALID_POSITION;
|
||||
}
|
||||
|
||||
final int count = adapter.getCount();
|
||||
if (!getAdapter().areAllItemsEnabled()) {
|
||||
if (lookDown) {
|
||||
position = Math.max(0, position);
|
||||
while (position < count && !adapter.isEnabled(position)) {
|
||||
position++;
|
||||
}
|
||||
} else {
|
||||
position = Math.min(position, count - 1);
|
||||
while (position >= 0 && !adapter.isEnabled(position)) {
|
||||
position--;
|
||||
}
|
||||
}
|
||||
|
||||
if (position < 0 || position >= count) {
|
||||
return INVALID_POSITION;
|
||||
}
|
||||
return position;
|
||||
} else {
|
||||
if (position < 0 || position >= count) {
|
||||
return INVALID_POSITION;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
protected void positionSelectorLikeTouchCompat(int position, View sel, float x, float y) {
|
||||
positionSelectorLikeFocusCompat(position, sel);
|
||||
|
||||
Drawable selector = getSelector();
|
||||
if (selector != null && position != INVALID_POSITION) {
|
||||
DrawableCompat.setHotspot(selector, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
protected void positionSelectorLikeFocusCompat(int position, View sel) {
|
||||
// If we're changing position, update the visibility since the selector
|
||||
// is technically being detached from the previous selection.
|
||||
final Drawable selector = getSelector();
|
||||
final boolean manageState = selector != null && position != INVALID_POSITION;
|
||||
if (manageState) {
|
||||
selector.setVisible(false, false);
|
||||
}
|
||||
|
||||
positionSelectorCompat(position, sel);
|
||||
|
||||
if (manageState) {
|
||||
final Rect bounds = mSelectorRect;
|
||||
final float x = bounds.exactCenterX();
|
||||
final float y = bounds.exactCenterY();
|
||||
selector.setVisible(getVisibility() == VISIBLE, false);
|
||||
DrawableCompat.setHotspot(selector, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
protected void positionSelectorCompat(int position, View sel) {
|
||||
final Rect selectorRect = mSelectorRect;
|
||||
selectorRect.set(sel.getLeft(), sel.getTop(), sel.getRight(), sel.getBottom());
|
||||
|
||||
// Adjust for selection padding.
|
||||
selectorRect.left -= mSelectionLeftPadding;
|
||||
selectorRect.top -= mSelectionTopPadding;
|
||||
selectorRect.right += mSelectionRightPadding;
|
||||
selectorRect.bottom += mSelectionBottomPadding;
|
||||
|
||||
try {
|
||||
// AbsListView.mIsChildViewEnabled controls the selector's state so we need to
|
||||
// modify it's value
|
||||
final boolean isChildViewEnabled = mIsChildViewEnabled.getBoolean(this);
|
||||
if (sel.isEnabled() != isChildViewEnabled) {
|
||||
mIsChildViewEnabled.set(this, !isChildViewEnabled);
|
||||
if (position != INVALID_POSITION) {
|
||||
refreshDrawableState();
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures the height of the given range of children (inclusive) and returns the height
|
||||
* with this ListView's padding and divider heights included. If maxHeight is provided, the
|
||||
* measuring will stop when the current height reaches maxHeight.
|
||||
*
|
||||
* @param widthMeasureSpec The width measure spec to be given to a child's
|
||||
* {@link View#measure(int, int)}.
|
||||
* @param startPosition The position of the first child to be shown.
|
||||
* @param endPosition The (inclusive) position of the last child to be
|
||||
* shown. Specify {@link #NO_POSITION} if the last child
|
||||
* should be the last available child from the adapter.
|
||||
* @param maxHeight The maximum height that will be returned (if all the
|
||||
* children don't fit in this value, this value will be
|
||||
* returned).
|
||||
* @param disallowPartialChildPosition In general, whether the returned height should only
|
||||
* contain entire children. This is more powerful--it is
|
||||
* the first inclusive position at which partial
|
||||
* children will not be allowed. Example: it looks nice
|
||||
* to have at least 3 completely visible children, and
|
||||
* in portrait this will most likely fit; but in
|
||||
* landscape there could be times when even 2 children
|
||||
* can not be completely shown, so a value of 2
|
||||
* (remember, inclusive) would be good (assuming
|
||||
* startPosition is 0).
|
||||
* @return The height of this ListView with the given children.
|
||||
*/
|
||||
public int measureHeightOfChildrenCompat(int widthMeasureSpec, int startPosition,
|
||||
int endPosition, final int maxHeight,
|
||||
int disallowPartialChildPosition) {
|
||||
|
||||
final int paddingTop = getListPaddingTop();
|
||||
final int paddingBottom = getListPaddingBottom();
|
||||
final int paddingLeft = getListPaddingLeft();
|
||||
final int paddingRight = getListPaddingRight();
|
||||
final int reportedDividerHeight = getDividerHeight();
|
||||
final Drawable divider = getDivider();
|
||||
|
||||
final ListAdapter adapter = getAdapter();
|
||||
|
||||
if (adapter == null) {
|
||||
return paddingTop + paddingBottom;
|
||||
}
|
||||
|
||||
// Include the padding of the list
|
||||
int returnedHeight = paddingTop + paddingBottom;
|
||||
final int dividerHeight = ((reportedDividerHeight > 0) && divider != null)
|
||||
? reportedDividerHeight : 0;
|
||||
|
||||
// The previous height value that was less than maxHeight and contained
|
||||
// no partial children
|
||||
int prevHeightWithoutPartialChild = 0;
|
||||
|
||||
View child = null;
|
||||
int viewType = 0;
|
||||
int count = adapter.getCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
int newType = adapter.getItemViewType(i);
|
||||
if (newType != viewType) {
|
||||
child = null;
|
||||
viewType = newType;
|
||||
}
|
||||
child = adapter.getView(i, child, this);
|
||||
|
||||
// Compute child height spec
|
||||
int heightMeasureSpec;
|
||||
final ViewGroup.LayoutParams childLp = child.getLayoutParams();
|
||||
if (childLp != null && childLp.height > 0) {
|
||||
heightMeasureSpec = MeasureSpec.makeMeasureSpec(childLp.height,
|
||||
MeasureSpec.EXACTLY);
|
||||
} else {
|
||||
heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
}
|
||||
child.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
if (i > 0) {
|
||||
// Count the divider for all but one child
|
||||
returnedHeight += dividerHeight;
|
||||
}
|
||||
|
||||
returnedHeight += child.getMeasuredHeight();
|
||||
|
||||
if (returnedHeight >= maxHeight) {
|
||||
// We went over, figure out which height to return. If returnedHeight >
|
||||
// maxHeight, then the i'th position did not fit completely.
|
||||
return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)
|
||||
&& (i > disallowPartialChildPosition) // We've past the min pos
|
||||
&& (prevHeightWithoutPartialChild > 0) // We have a prev height
|
||||
&& (returnedHeight != maxHeight) // i'th child did not fit completely
|
||||
? prevHeightWithoutPartialChild
|
||||
: maxHeight;
|
||||
}
|
||||
|
||||
if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) {
|
||||
prevHeightWithoutPartialChild = returnedHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, we went through the range of children, and they each
|
||||
// completely fit, so return the returnedHeight
|
||||
return returnedHeight;
|
||||
}
|
||||
|
||||
protected void setSelectorEnabled(boolean enabled) {
|
||||
if (mSelector != null) {
|
||||
mSelector.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
private static class GateKeeperDrawable extends DrawableWrapper {
|
||||
private boolean mEnabled;
|
||||
|
||||
public GateKeeperDrawable(Drawable drawable) {
|
||||
super(drawable);
|
||||
mEnabled = true;
|
||||
}
|
||||
|
||||
void setEnabled(boolean enabled) {
|
||||
mEnabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setState(int[] stateSet) {
|
||||
if (mEnabled) {
|
||||
return super.setState(stateSet);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (mEnabled) {
|
||||
super.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHotspot(float x, float y) {
|
||||
if (mEnabled) {
|
||||
super.setHotspot(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHotspotBounds(int left, int top, int right, int bottom) {
|
||||
if (mEnabled) {
|
||||
super.setHotspotBounds(left, top, right, bottom);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setVisible(boolean visible, boolean restart) {
|
||||
if (mEnabled) {
|
||||
return super.setVisible(visible, restart);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.support.v7.internal.widget;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ActionMode;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public class NativeActionModeAwareLayout extends ContentFrameLayout {
|
||||
|
||||
private OnActionModeForChildListener mActionModeForChildListener;
|
||||
|
||||
public NativeActionModeAwareLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void setActionModeForChildListener(OnActionModeForChildListener listener) {
|
||||
mActionModeForChildListener = listener;
|
||||
}
|
||||
|
||||
public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
|
||||
if (mActionModeForChildListener != null) {
|
||||
return mActionModeForChildListener.startActionModeForChild(originalView, callback);
|
||||
}
|
||||
return super.startActionModeForChild(originalView, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public interface OnActionModeForChildListener {
|
||||
ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback);
|
||||
}
|
||||
}
|
282
android/support/v7/internal/widget/ResourcesWrapper.java
Normal file
282
android/support/v7/internal/widget/ResourcesWrapper.java
Normal file
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.support.v7.internal.widget;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.graphics.Movie;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Drawable.ConstantState;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.LongSparseArray;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* This extends Resources but delegates the calls to another Resources object. This enables
|
||||
* any customization done by some subclass of Resources to be also picked up.
|
||||
*/
|
||||
class ResourcesWrapper extends Resources {
|
||||
|
||||
private final Resources mResources;
|
||||
|
||||
public ResourcesWrapper(Resources resources) {
|
||||
super(resources.getAssets(), resources.getDisplayMetrics(), resources.getConfiguration());
|
||||
mResources = resources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getText(int id) throws NotFoundException {
|
||||
return mResources.getText(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getQuantityText(int id, int quantity) throws NotFoundException {
|
||||
return mResources.getQuantityText(id, quantity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int id) throws NotFoundException {
|
||||
return mResources.getString(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int id, Object... formatArgs) throws NotFoundException {
|
||||
return mResources.getString(id, formatArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQuantityString(int id, int quantity, Object... formatArgs)
|
||||
throws NotFoundException {
|
||||
return mResources.getQuantityString(id, quantity, formatArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQuantityString(int id, int quantity) throws NotFoundException {
|
||||
return mResources.getQuantityString(id, quantity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getText(int id, CharSequence def) {
|
||||
return mResources.getText(id, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence[] getTextArray(int id) throws NotFoundException {
|
||||
return mResources.getTextArray(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getStringArray(int id) throws NotFoundException {
|
||||
return mResources.getStringArray(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getIntArray(int id) throws NotFoundException {
|
||||
return mResources.getIntArray(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedArray obtainTypedArray(int id) throws NotFoundException {
|
||||
return mResources.obtainTypedArray(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDimension(int id) throws NotFoundException {
|
||||
return mResources.getDimension(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDimensionPixelOffset(int id) throws NotFoundException {
|
||||
return mResources.getDimensionPixelOffset(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDimensionPixelSize(int id) throws NotFoundException {
|
||||
return mResources.getDimensionPixelSize(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFraction(int id, int base, int pbase) {
|
||||
return mResources.getFraction(id, base, pbase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getDrawable(int id) throws NotFoundException {
|
||||
return mResources.getDrawable(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getDrawable(int id, Theme theme) throws NotFoundException {
|
||||
return mResources.getDrawable(id, theme);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getDrawableForDensity(int id, int density) throws NotFoundException {
|
||||
return mResources.getDrawableForDensity(id, density);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getDrawableForDensity(int id, int density, Theme theme) {
|
||||
return mResources.getDrawableForDensity(id, density, theme);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Movie getMovie(int id) throws NotFoundException {
|
||||
return mResources.getMovie(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColor(int id) throws NotFoundException {
|
||||
return mResources.getColor(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorStateList getColorStateList(int id) throws NotFoundException {
|
||||
return mResources.getColorStateList(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(int id) throws NotFoundException {
|
||||
return mResources.getBoolean(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInteger(int id) throws NotFoundException {
|
||||
return mResources.getInteger(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlResourceParser getLayout(int id) throws NotFoundException {
|
||||
return mResources.getLayout(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlResourceParser getAnimation(int id) throws NotFoundException {
|
||||
return mResources.getAnimation(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlResourceParser getXml(int id) throws NotFoundException {
|
||||
return mResources.getXml(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openRawResource(int id) throws NotFoundException {
|
||||
return mResources.openRawResource(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
|
||||
return mResources.openRawResource(id, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
|
||||
return mResources.openRawResourceFd(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getValue(int id, TypedValue outValue, boolean resolveRefs)
|
||||
throws NotFoundException {
|
||||
mResources.getValue(id, outValue, resolveRefs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getValueForDensity(int id, int density, TypedValue outValue, boolean resolveRefs)
|
||||
throws NotFoundException {
|
||||
mResources.getValueForDensity(id, density, outValue, resolveRefs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getValue(String name, TypedValue outValue, boolean resolveRefs)
|
||||
throws NotFoundException {
|
||||
mResources.getValue(name, outValue, resolveRefs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
|
||||
return mResources.obtainAttributes(set, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateConfiguration(Configuration config, DisplayMetrics metrics) {
|
||||
super.updateConfiguration(config, metrics);
|
||||
if (mResources != null) { // called from super's constructor. So, need to check.
|
||||
mResources.updateConfiguration(config, metrics);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DisplayMetrics getDisplayMetrics() {
|
||||
return mResources.getDisplayMetrics();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration getConfiguration() {
|
||||
return mResources.getConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIdentifier(String name, String defType, String defPackage) {
|
||||
return mResources.getIdentifier(name, defType, defPackage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceName(int resid) throws NotFoundException {
|
||||
return mResources.getResourceName(resid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourcePackageName(int resid) throws NotFoundException {
|
||||
return mResources.getResourcePackageName(resid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceTypeName(int resid) throws NotFoundException {
|
||||
return mResources.getResourceTypeName(resid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceEntryName(int resid) throws NotFoundException {
|
||||
return mResources.getResourceEntryName(resid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle)
|
||||
throws XmlPullParserException, IOException {
|
||||
mResources.parseBundleExtras(parser, outBundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseBundleExtra(String tagName, AttributeSet attrs, Bundle outBundle)
|
||||
throws XmlPullParserException {
|
||||
mResources.parseBundleExtra(tagName, attrs, outBundle);
|
||||
}
|
||||
}
|
||||
|
93
android/support/v7/internal/widget/RtlSpacingHelper.java
Normal file
93
android/support/v7/internal/widget/RtlSpacingHelper.java
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
/**
|
||||
* RtlSpacingHelper manages the relationship between left/right and start/end for views
|
||||
* that need to maintain both absolute and relative settings for a form of spacing similar
|
||||
* to view padding.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class RtlSpacingHelper {
|
||||
public static final int UNDEFINED = Integer.MIN_VALUE;
|
||||
|
||||
private int mLeft = 0;
|
||||
private int mRight = 0;
|
||||
private int mStart = UNDEFINED;
|
||||
private int mEnd = UNDEFINED;
|
||||
private int mExplicitLeft = 0;
|
||||
private int mExplicitRight = 0;
|
||||
|
||||
private boolean mIsRtl = false;
|
||||
private boolean mIsRelative = false;
|
||||
|
||||
public int getLeft() {
|
||||
return mLeft;
|
||||
}
|
||||
|
||||
public int getRight() {
|
||||
return mRight;
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return mIsRtl ? mRight : mLeft;
|
||||
}
|
||||
|
||||
public int getEnd() {
|
||||
return mIsRtl ? mLeft : mRight;
|
||||
}
|
||||
|
||||
public void setRelative(int start, int end) {
|
||||
mStart = start;
|
||||
mEnd = end;
|
||||
mIsRelative = true;
|
||||
if (mIsRtl) {
|
||||
if (end != UNDEFINED) mLeft = end;
|
||||
if (start != UNDEFINED) mRight = start;
|
||||
} else {
|
||||
if (start != UNDEFINED) mLeft = start;
|
||||
if (end != UNDEFINED) mRight = end;
|
||||
}
|
||||
}
|
||||
|
||||
public void setAbsolute(int left, int right) {
|
||||
mIsRelative = false;
|
||||
if (left != UNDEFINED) mLeft = mExplicitLeft = left;
|
||||
if (right != UNDEFINED) mRight = mExplicitRight = right;
|
||||
}
|
||||
|
||||
public void setDirection(boolean isRtl) {
|
||||
if (isRtl == mIsRtl) {
|
||||
return;
|
||||
}
|
||||
mIsRtl = isRtl;
|
||||
if (mIsRelative) {
|
||||
if (isRtl) {
|
||||
mLeft = mEnd != UNDEFINED ? mEnd : mExplicitLeft;
|
||||
mRight = mStart != UNDEFINED ? mStart : mExplicitRight;
|
||||
} else {
|
||||
mLeft = mStart != UNDEFINED ? mStart : mExplicitLeft;
|
||||
mRight = mEnd != UNDEFINED ? mEnd : mExplicitRight;
|
||||
}
|
||||
} else {
|
||||
mLeft = mExplicitLeft;
|
||||
mRight = mExplicitRight;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,609 @@
|
|||
/*
|
||||
* Copyright (C) 2011 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.view.ViewPropertyAnimatorCompat;
|
||||
import android.support.v4.view.ViewPropertyAnimatorListener;
|
||||
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.internal.view.ActionBarPolicy;
|
||||
import android.support.v7.widget.LinearLayoutCompat;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextUtils.TruncateAt;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* This widget implements the dynamic action bar tab behavior that can change across different
|
||||
* configurations or circumstances.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ScrollingTabContainerView extends HorizontalScrollView
|
||||
implements AdapterViewCompat.OnItemClickListener {
|
||||
|
||||
private static final String TAG = "ScrollingTabContainerView";
|
||||
Runnable mTabSelector;
|
||||
private TabClickListener mTabClickListener;
|
||||
|
||||
private LinearLayoutCompat mTabLayout;
|
||||
private SpinnerCompat mTabSpinner;
|
||||
private boolean mAllowCollapse;
|
||||
|
||||
int mMaxTabWidth;
|
||||
int mStackedTabMaxWidth;
|
||||
private int mContentHeight;
|
||||
private int mSelectedTabIndex;
|
||||
|
||||
protected ViewPropertyAnimatorCompat mVisibilityAnim;
|
||||
protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
|
||||
|
||||
private static final Interpolator sAlphaInterpolator = new DecelerateInterpolator();
|
||||
|
||||
private static final int FADE_DURATION = 200;
|
||||
|
||||
public ScrollingTabContainerView(Context context) {
|
||||
super(context);
|
||||
|
||||
setHorizontalScrollBarEnabled(false);
|
||||
|
||||
ActionBarPolicy abp = ActionBarPolicy.get(context);
|
||||
setContentHeight(abp.getTabContainerHeight());
|
||||
mStackedTabMaxWidth = abp.getStackedTabMaxWidth();
|
||||
|
||||
mTabLayout = createTabLayout();
|
||||
addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;
|
||||
setFillViewport(lockedExpanded);
|
||||
|
||||
final int childCount = mTabLayout.getChildCount();
|
||||
if (childCount > 1 &&
|
||||
(widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
|
||||
if (childCount > 2) {
|
||||
mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.4f);
|
||||
} else {
|
||||
mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2;
|
||||
}
|
||||
mMaxTabWidth = Math.min(mMaxTabWidth, mStackedTabMaxWidth);
|
||||
} else {
|
||||
mMaxTabWidth = -1;
|
||||
}
|
||||
|
||||
heightMeasureSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY);
|
||||
|
||||
final boolean canCollapse = !lockedExpanded && mAllowCollapse;
|
||||
|
||||
if (canCollapse) {
|
||||
// See if we should expand
|
||||
mTabLayout.measure(MeasureSpec.UNSPECIFIED, heightMeasureSpec);
|
||||
if (mTabLayout.getMeasuredWidth() > MeasureSpec.getSize(widthMeasureSpec)) {
|
||||
performCollapse();
|
||||
} else {
|
||||
performExpand();
|
||||
}
|
||||
} else {
|
||||
performExpand();
|
||||
}
|
||||
|
||||
final int oldWidth = getMeasuredWidth();
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
final int newWidth = getMeasuredWidth();
|
||||
|
||||
if (lockedExpanded && oldWidth != newWidth) {
|
||||
// Recenter the tab display if we're at a new (scrollable) size.
|
||||
setTabSelected(mSelectedTabIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this view is collapsed into a dropdown menu instead
|
||||
* of traditional tabs.
|
||||
* @return true if showing as a spinner
|
||||
*/
|
||||
private boolean isCollapsed() {
|
||||
return mTabSpinner != null && mTabSpinner.getParent() == this;
|
||||
}
|
||||
|
||||
public void setAllowCollapse(boolean allowCollapse) {
|
||||
mAllowCollapse = allowCollapse;
|
||||
}
|
||||
|
||||
private void performCollapse() {
|
||||
if (isCollapsed()) return;
|
||||
|
||||
if (mTabSpinner == null) {
|
||||
mTabSpinner = createSpinner();
|
||||
}
|
||||
removeView(mTabLayout);
|
||||
addView(mTabSpinner, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
if (mTabSpinner.getAdapter() == null) {
|
||||
mTabSpinner.setAdapter(new TabAdapter());
|
||||
}
|
||||
if (mTabSelector != null) {
|
||||
removeCallbacks(mTabSelector);
|
||||
mTabSelector = null;
|
||||
}
|
||||
mTabSpinner.setSelection(mSelectedTabIndex);
|
||||
}
|
||||
|
||||
private boolean performExpand() {
|
||||
if (!isCollapsed()) return false;
|
||||
|
||||
removeView(mTabSpinner);
|
||||
addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
setTabSelected(mTabSpinner.getSelectedItemPosition());
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setTabSelected(int position) {
|
||||
mSelectedTabIndex = position;
|
||||
final int tabCount = mTabLayout.getChildCount();
|
||||
for (int i = 0; i < tabCount; i++) {
|
||||
final View child = mTabLayout.getChildAt(i);
|
||||
final boolean isSelected = i == position;
|
||||
child.setSelected(isSelected);
|
||||
if (isSelected) {
|
||||
animateToTab(position);
|
||||
}
|
||||
}
|
||||
if (mTabSpinner != null && position >= 0) {
|
||||
mTabSpinner.setSelection(position);
|
||||
}
|
||||
}
|
||||
|
||||
public void setContentHeight(int contentHeight) {
|
||||
mContentHeight = contentHeight;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
private LinearLayoutCompat createTabLayout() {
|
||||
final LinearLayoutCompat tabLayout = new LinearLayoutCompat(getContext(), null,
|
||||
R.attr.actionBarTabBarStyle);
|
||||
tabLayout.setMeasureWithLargestChildEnabled(true);
|
||||
tabLayout.setGravity(Gravity.CENTER);
|
||||
tabLayout.setLayoutParams(new LinearLayoutCompat.LayoutParams(
|
||||
LinearLayoutCompat.LayoutParams.WRAP_CONTENT, LinearLayoutCompat.LayoutParams.MATCH_PARENT));
|
||||
return tabLayout;
|
||||
}
|
||||
|
||||
private SpinnerCompat createSpinner() {
|
||||
final SpinnerCompat spinner = new SpinnerCompat(getContext(), null,
|
||||
R.attr.actionDropDownStyle);
|
||||
spinner.setLayoutParams(new LinearLayoutCompat.LayoutParams(
|
||||
LinearLayoutCompat.LayoutParams.WRAP_CONTENT, LinearLayoutCompat.LayoutParams.MATCH_PARENT));
|
||||
spinner.setOnItemClickListenerInt(this);
|
||||
return spinner;
|
||||
}
|
||||
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
if (Build.VERSION.SDK_INT >= 8) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
ActionBarPolicy abp = ActionBarPolicy.get(getContext());
|
||||
// Action bar can change size on configuration changes.
|
||||
// Reread the desired height from the theme-specified style.
|
||||
setContentHeight(abp.getTabContainerHeight());
|
||||
mStackedTabMaxWidth = abp.getStackedTabMaxWidth();
|
||||
}
|
||||
|
||||
public void animateToVisibility(int visibility) {
|
||||
if (mVisibilityAnim != null) {
|
||||
mVisibilityAnim.cancel();
|
||||
}
|
||||
if (visibility == VISIBLE) {
|
||||
if (getVisibility() != VISIBLE) {
|
||||
ViewCompat.setAlpha(this, 0f);
|
||||
}
|
||||
|
||||
ViewPropertyAnimatorCompat anim = ViewCompat.animate(this).alpha(1f);
|
||||
anim.setDuration(FADE_DURATION);
|
||||
|
||||
anim.setInterpolator(sAlphaInterpolator);
|
||||
anim.setListener(mVisAnimListener.withFinalVisibility(anim, visibility));
|
||||
anim.start();
|
||||
} else {
|
||||
ViewPropertyAnimatorCompat anim = ViewCompat.animate(this).alpha(0f);
|
||||
anim.setDuration(FADE_DURATION);
|
||||
|
||||
anim.setInterpolator(sAlphaInterpolator);
|
||||
anim.setListener(mVisAnimListener.withFinalVisibility(anim, visibility));
|
||||
anim.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void animateToTab(final int position) {
|
||||
final View tabView = mTabLayout.getChildAt(position);
|
||||
if (mTabSelector != null) {
|
||||
removeCallbacks(mTabSelector);
|
||||
}
|
||||
mTabSelector = new Runnable() {
|
||||
public void run() {
|
||||
final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;
|
||||
smoothScrollTo(scrollPos, 0);
|
||||
mTabSelector = null;
|
||||
}
|
||||
};
|
||||
post(mTabSelector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
if (mTabSelector != null) {
|
||||
// Re-post the selector we saved
|
||||
post(mTabSelector);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
if (mTabSelector != null) {
|
||||
removeCallbacks(mTabSelector);
|
||||
}
|
||||
}
|
||||
|
||||
private TabView createTabView(ActionBar.Tab tab, boolean forAdapter) {
|
||||
final TabView tabView = new TabView(getContext(), tab, forAdapter);
|
||||
if (forAdapter) {
|
||||
tabView.setBackgroundDrawable(null);
|
||||
tabView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT,
|
||||
mContentHeight));
|
||||
} else {
|
||||
tabView.setFocusable(true);
|
||||
|
||||
if (mTabClickListener == null) {
|
||||
mTabClickListener = new TabClickListener();
|
||||
}
|
||||
tabView.setOnClickListener(mTabClickListener);
|
||||
}
|
||||
return tabView;
|
||||
}
|
||||
|
||||
public void addTab(ActionBar.Tab tab, boolean setSelected) {
|
||||
TabView tabView = createTabView(tab, false);
|
||||
mTabLayout.addView(tabView, new LinearLayoutCompat.LayoutParams(0,
|
||||
LayoutParams.MATCH_PARENT, 1));
|
||||
if (mTabSpinner != null) {
|
||||
((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
if (setSelected) {
|
||||
tabView.setSelected(true);
|
||||
}
|
||||
if (mAllowCollapse) {
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
|
||||
final TabView tabView = createTabView(tab, false);
|
||||
mTabLayout.addView(tabView, position, new LinearLayoutCompat.LayoutParams(
|
||||
0, LayoutParams.MATCH_PARENT, 1));
|
||||
if (mTabSpinner != null) {
|
||||
((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
if (setSelected) {
|
||||
tabView.setSelected(true);
|
||||
}
|
||||
if (mAllowCollapse) {
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTab(int position) {
|
||||
((TabView) mTabLayout.getChildAt(position)).update();
|
||||
if (mTabSpinner != null) {
|
||||
((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
if (mAllowCollapse) {
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeTabAt(int position) {
|
||||
mTabLayout.removeViewAt(position);
|
||||
if (mTabSpinner != null) {
|
||||
((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
if (mAllowCollapse) {
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllTabs() {
|
||||
mTabLayout.removeAllViews();
|
||||
if (mTabSpinner != null) {
|
||||
((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
if (mAllowCollapse) {
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterViewCompat<?> parent, View view, int position, long id) {
|
||||
TabView tabView = (TabView) view;
|
||||
tabView.getTab().select();
|
||||
}
|
||||
|
||||
private class TabView extends LinearLayoutCompat implements OnLongClickListener {
|
||||
private final int[] BG_ATTRS = {
|
||||
android.R.attr.background
|
||||
};
|
||||
|
||||
private ActionBar.Tab mTab;
|
||||
private TextView mTextView;
|
||||
private ImageView mIconView;
|
||||
private View mCustomView;
|
||||
|
||||
public TabView(Context context, ActionBar.Tab tab, boolean forList) {
|
||||
super(context, null, R.attr.actionBarTabStyle);
|
||||
mTab = tab;
|
||||
|
||||
TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, null, BG_ATTRS,
|
||||
R.attr.actionBarTabStyle, 0);
|
||||
if (a.hasValue(0)) {
|
||||
setBackgroundDrawable(a.getDrawable(0));
|
||||
}
|
||||
a.recycle();
|
||||
|
||||
if (forList) {
|
||||
setGravity(GravityCompat.START | Gravity.CENTER_VERTICAL);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
public void bindTab(ActionBar.Tab tab) {
|
||||
mTab = tab;
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelected(boolean selected) {
|
||||
final boolean changed = (isSelected() != selected);
|
||||
super.setSelected(selected);
|
||||
if (changed && selected) {
|
||||
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
||||
super.onInitializeAccessibilityEvent(event);
|
||||
// This view masquerades as an action bar tab.
|
||||
event.setClassName(ActionBar.Tab.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
// This view masquerades as an action bar tab.
|
||||
info.setClassName(ActionBar.Tab.class.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
// Re-measure if we went beyond our maximum size.
|
||||
if (mMaxTabWidth > 0 && getMeasuredWidth() > mMaxTabWidth) {
|
||||
super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY),
|
||||
heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
public void update() {
|
||||
final ActionBar.Tab tab = mTab;
|
||||
final View custom = tab.getCustomView();
|
||||
if (custom != null) {
|
||||
final ViewParent customParent = custom.getParent();
|
||||
if (customParent != this) {
|
||||
if (customParent != null) ((ViewGroup) customParent).removeView(custom);
|
||||
addView(custom);
|
||||
}
|
||||
mCustomView = custom;
|
||||
if (mTextView != null) mTextView.setVisibility(GONE);
|
||||
if (mIconView != null) {
|
||||
mIconView.setVisibility(GONE);
|
||||
mIconView.setImageDrawable(null);
|
||||
}
|
||||
} else {
|
||||
if (mCustomView != null) {
|
||||
removeView(mCustomView);
|
||||
mCustomView = null;
|
||||
}
|
||||
|
||||
final Drawable icon = tab.getIcon();
|
||||
final CharSequence text = tab.getText();
|
||||
|
||||
if (icon != null) {
|
||||
if (mIconView == null) {
|
||||
ImageView iconView = new ImageView(getContext());
|
||||
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT);
|
||||
lp.gravity = Gravity.CENTER_VERTICAL;
|
||||
iconView.setLayoutParams(lp);
|
||||
addView(iconView, 0);
|
||||
mIconView = iconView;
|
||||
}
|
||||
mIconView.setImageDrawable(icon);
|
||||
mIconView.setVisibility(VISIBLE);
|
||||
} else if (mIconView != null) {
|
||||
mIconView.setVisibility(GONE);
|
||||
mIconView.setImageDrawable(null);
|
||||
}
|
||||
|
||||
final boolean hasText = !TextUtils.isEmpty(text);
|
||||
if (hasText) {
|
||||
if (mTextView == null) {
|
||||
TextView textView = new CompatTextView(getContext(), null,
|
||||
R.attr.actionBarTabTextStyle);
|
||||
textView.setEllipsize(TruncateAt.END);
|
||||
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT);
|
||||
lp.gravity = Gravity.CENTER_VERTICAL;
|
||||
textView.setLayoutParams(lp);
|
||||
addView(textView);
|
||||
mTextView = textView;
|
||||
}
|
||||
mTextView.setText(text);
|
||||
mTextView.setVisibility(VISIBLE);
|
||||
} else if (mTextView != null) {
|
||||
mTextView.setVisibility(GONE);
|
||||
mTextView.setText(null);
|
||||
}
|
||||
|
||||
if (mIconView != null) {
|
||||
mIconView.setContentDescription(tab.getContentDescription());
|
||||
}
|
||||
|
||||
if (!hasText && !TextUtils.isEmpty(tab.getContentDescription())) {
|
||||
setOnLongClickListener(this);
|
||||
} else {
|
||||
setOnLongClickListener(null);
|
||||
setLongClickable(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onLongClick(View v) {
|
||||
final int[] screenPos = new int[2];
|
||||
getLocationOnScreen(screenPos);
|
||||
|
||||
final Context context = getContext();
|
||||
final int width = getWidth();
|
||||
final int height = getHeight();
|
||||
final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
|
||||
|
||||
Toast cheatSheet = Toast.makeText(context, mTab.getContentDescription(),
|
||||
Toast.LENGTH_SHORT);
|
||||
// Show under the tab
|
||||
cheatSheet.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL,
|
||||
(screenPos[0] + width / 2) - screenWidth / 2, height);
|
||||
|
||||
cheatSheet.show();
|
||||
return true;
|
||||
}
|
||||
|
||||
public ActionBar.Tab getTab() {
|
||||
return mTab;
|
||||
}
|
||||
}
|
||||
|
||||
private class TabAdapter extends BaseAdapter {
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mTabLayout.getChildCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return ((TabView) mTabLayout.getChildAt(position)).getTab();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = createTabView((ActionBar.Tab) getItem(position), true);
|
||||
} else {
|
||||
((TabView) convertView).bindTab((ActionBar.Tab) getItem(position));
|
||||
}
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
||||
private class TabClickListener implements OnClickListener {
|
||||
public void onClick(View view) {
|
||||
TabView tabView = (TabView) view;
|
||||
tabView.getTab().select();
|
||||
final int tabCount = mTabLayout.getChildCount();
|
||||
for (int i = 0; i < tabCount; i++) {
|
||||
final View child = mTabLayout.getChildAt(i);
|
||||
child.setSelected(child == view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class VisibilityAnimListener implements ViewPropertyAnimatorListener {
|
||||
private boolean mCanceled = false;
|
||||
private int mFinalVisibility;
|
||||
|
||||
public VisibilityAnimListener withFinalVisibility(ViewPropertyAnimatorCompat animation,
|
||||
int visibility) {
|
||||
mFinalVisibility = visibility;
|
||||
mVisibilityAnim = animation;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(View view) {
|
||||
setVisibility(VISIBLE);
|
||||
mCanceled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(View view) {
|
||||
if (mCanceled) return;
|
||||
|
||||
mVisibilityAnim = null;
|
||||
setVisibility(mFinalVisibility);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(View view) {
|
||||
mCanceled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1102
android/support/v7/internal/widget/SpinnerCompat.java
Normal file
1102
android/support/v7/internal/widget/SpinnerCompat.java
Normal file
File diff suppressed because it is too large
Load diff
107
android/support/v7/internal/widget/ThemeUtils.java
Normal file
107
android/support/v7/internal/widget/ThemeUtils.java
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.support.v4.graphics.ColorUtils;
|
||||
import android.util.TypedValue;
|
||||
|
||||
class ThemeUtils {
|
||||
|
||||
private static final ThreadLocal<TypedValue> TL_TYPED_VALUE = new ThreadLocal<>();
|
||||
|
||||
private static final int[] DISABLED_STATE_SET = new int[]{-android.R.attr.state_enabled};
|
||||
private static final int[] EMPTY_STATE_SET = new int[0];
|
||||
|
||||
private static final int[] TEMP_ARRAY = new int[1];
|
||||
|
||||
static ColorStateList createDisabledStateList(int textColor, int disabledTextColor) {
|
||||
// Now create a new ColorStateList with the default color, and the new disabled
|
||||
// color
|
||||
final int[][] states = new int[2][];
|
||||
final int[] colors = new int[2];
|
||||
int i = 0;
|
||||
|
||||
// Disabled state
|
||||
states[i] = DISABLED_STATE_SET;
|
||||
colors[i] = disabledTextColor;
|
||||
i++;
|
||||
|
||||
// Default state
|
||||
states[i] = EMPTY_STATE_SET;
|
||||
colors[i] = textColor;
|
||||
i++;
|
||||
|
||||
return new ColorStateList(states, colors);
|
||||
}
|
||||
|
||||
static int getThemeAttrColor(Context context, int attr) {
|
||||
TEMP_ARRAY[0] = attr;
|
||||
TypedArray a = context.obtainStyledAttributes(null, TEMP_ARRAY);
|
||||
try {
|
||||
return a.getColor(0, 0);
|
||||
} finally {
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
static ColorStateList getThemeAttrColorStateList(Context context, int attr) {
|
||||
TEMP_ARRAY[0] = attr;
|
||||
TypedArray a = context.obtainStyledAttributes(null, TEMP_ARRAY);
|
||||
try {
|
||||
return a.getColorStateList(0);
|
||||
} finally {
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
static int getDisabledThemeAttrColor(Context context, int attr) {
|
||||
final ColorStateList csl = getThemeAttrColorStateList(context, attr);
|
||||
if (csl != null && csl.isStateful()) {
|
||||
// If the CSL is stateful, we'll assume it has a disabled state and use it
|
||||
return csl.getColorForState(DISABLED_STATE_SET, csl.getDefaultColor());
|
||||
} else {
|
||||
// Else, we'll generate the color using disabledAlpha from the theme
|
||||
|
||||
final TypedValue tv = getTypedValue();
|
||||
// Now retrieve the disabledAlpha value from the theme
|
||||
context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, tv, true);
|
||||
final float disabledAlpha = tv.getFloat();
|
||||
|
||||
return getThemeAttrColor(context, attr, disabledAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
private static TypedValue getTypedValue() {
|
||||
TypedValue typedValue = TL_TYPED_VALUE.get();
|
||||
if (typedValue == null) {
|
||||
typedValue = new TypedValue();
|
||||
TL_TYPED_VALUE.set(typedValue);
|
||||
}
|
||||
return typedValue;
|
||||
}
|
||||
|
||||
static int getThemeAttrColor(Context context, int attr, float alpha) {
|
||||
final int color = getThemeAttrColor(context, attr);
|
||||
final int originalAlpha = Color.alpha(color);
|
||||
return ColorUtils.setAlphaComponent(color, Math.round(originalAlpha * alpha));
|
||||
}
|
||||
|
||||
}
|
142
android/support/v7/internal/widget/TintAutoCompleteTextView.java
Normal file
142
android/support/v7/internal/widget/TintAutoCompleteTextView.java
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.TintableBackgroundView;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
|
||||
/**
|
||||
* An tint aware {@link android.widget.AutoCompleteTextView}.
|
||||
* <p>
|
||||
* This will automatically be used when you use {@link AutoCompleteTextView} in your layouts. You
|
||||
* should only need to manually use this class writing custom views.
|
||||
*/
|
||||
public class TintAutoCompleteTextView extends AutoCompleteTextView implements
|
||||
TintableBackgroundView {
|
||||
|
||||
private static final int[] TINT_ATTRS = {
|
||||
android.R.attr.background,
|
||||
android.R.attr.popupBackground
|
||||
};
|
||||
|
||||
private TintManager mTintManager;
|
||||
private TintInfo mBackgroundTint;
|
||||
|
||||
public TintAutoCompleteTextView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TintAutoCompleteTextView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, android.R.attr.autoCompleteTextViewStyle);
|
||||
}
|
||||
|
||||
public TintAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
|
||||
|
||||
if (TintManager.SHOULD_BE_USED) {
|
||||
TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
|
||||
TINT_ATTRS, defStyleAttr, 0);
|
||||
mTintManager = a.getTintManager();
|
||||
|
||||
if (a.hasValue(0)) {
|
||||
setSupportBackgroundTintList(
|
||||
mTintManager.getColorStateList(a.getResourceId(0, -1)));
|
||||
}
|
||||
if (a.hasValue(1)) {
|
||||
setDropDownBackgroundDrawable(a.getDrawable(1));
|
||||
}
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDropDownBackgroundResource(int id) {
|
||||
setDropDownBackgroundDrawable(mTintManager.getDrawable(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View,
|
||||
* android.content.res.ColorStateList)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
|
||||
if (mBackgroundTint == null) {
|
||||
mBackgroundTint = new TintInfo();
|
||||
}
|
||||
mBackgroundTint.mTintList = tint;
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public ColorStateList getSupportBackgroundTintList() {
|
||||
return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
|
||||
if (mBackgroundTint == null) {
|
||||
mBackgroundTint = new TintInfo();
|
||||
}
|
||||
mBackgroundTint.mTintMode = tintMode;
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public PorterDuff.Mode getSupportBackgroundTintMode() {
|
||||
return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawableStateChanged() {
|
||||
super.drawableStateChanged();
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
private void applySupportBackgroundTint() {
|
||||
if (getBackground() != null && mBackgroundTint != null) {
|
||||
TintManager.tintViewBackground(this, mBackgroundTint);
|
||||
}
|
||||
}
|
||||
}
|
154
android/support/v7/internal/widget/TintButton.java
Normal file
154
android/support/v7/internal/widget/TintButton.java
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.TintableBackgroundView;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.Button;
|
||||
|
||||
/**
|
||||
* An tint aware {@link android.widget.Button}.
|
||||
* <p>
|
||||
* This will automatically be used when you use {@link android.widget.Button} in your layouts. You
|
||||
* should only need to manually use this class when writing custom views.
|
||||
*/
|
||||
public class TintButton extends Button implements TintableBackgroundView {
|
||||
|
||||
private static final int[] TINT_ATTRS = {
|
||||
android.R.attr.background
|
||||
};
|
||||
|
||||
private TintInfo mBackgroundTint;
|
||||
|
||||
public TintButton(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TintButton(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, android.R.attr.buttonStyle);
|
||||
}
|
||||
|
||||
public TintButton(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
if (TintManager.SHOULD_BE_USED) {
|
||||
TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
|
||||
TINT_ATTRS, defStyleAttr, 0);
|
||||
if (a.hasValue(0)) {
|
||||
setSupportBackgroundTintList(
|
||||
a.getTintManager().getColorStateList(a.getResourceId(0, -1)));
|
||||
}
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
final ColorStateList textColors = getTextColors();
|
||||
if (textColors != null && !textColors.isStateful()) {
|
||||
// If we have a ColorStateList which isn't stateful, create one which includes
|
||||
// a disabled state
|
||||
|
||||
final int disabledTextColor;
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
// Pre-Lollipop, we will use textColorSecondary with android:disabledAlpha
|
||||
// applied
|
||||
disabledTextColor = ThemeUtils.getDisabledThemeAttrColor(context,
|
||||
android.R.attr.textColorSecondary);
|
||||
} else {
|
||||
// With certain styles on Lollipop, there is a StateListAnimator which sets
|
||||
// an alpha on the whole view, so we don't need to apply disabledAlpha to
|
||||
// textColorSecondary
|
||||
disabledTextColor = ThemeUtils.getThemeAttrColor(context,
|
||||
android.R.attr.textColorSecondary);
|
||||
}
|
||||
|
||||
setTextColor(ThemeUtils.createDisabledStateList(
|
||||
textColors.getDefaultColor(), disabledTextColor));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View,
|
||||
* android.content.res.ColorStateList)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
|
||||
if (mBackgroundTint == null) {
|
||||
mBackgroundTint = new TintInfo();
|
||||
}
|
||||
mBackgroundTint.mTintList = tint;
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public ColorStateList getSupportBackgroundTintList() {
|
||||
return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
|
||||
if (mBackgroundTint == null) {
|
||||
mBackgroundTint = new TintInfo();
|
||||
}
|
||||
mBackgroundTint.mTintMode = tintMode;
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public PorterDuff.Mode getSupportBackgroundTintMode() {
|
||||
return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawableStateChanged() {
|
||||
super.drawableStateChanged();
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
private void applySupportBackgroundTint() {
|
||||
if (getBackground() != null && mBackgroundTint != null) {
|
||||
TintManager.tintViewBackground(this, mBackgroundTint);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
66
android/support/v7/internal/widget/TintCheckBox.java
Normal file
66
android/support/v7/internal/widget/TintCheckBox.java
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.CheckBox;
|
||||
|
||||
/**
|
||||
* An tint aware {@link android.widget.CheckBox}.
|
||||
* <p>
|
||||
* This will automatically be used when you use {@link android.widget.CheckBox} in your layouts.
|
||||
* You should only need to manually use this class when writing custom views.
|
||||
*/
|
||||
public class TintCheckBox extends CheckBox {
|
||||
|
||||
private static final int[] TINT_ATTRS = {
|
||||
android.R.attr.button
|
||||
};
|
||||
|
||||
private TintManager mTintManager;
|
||||
|
||||
public TintCheckBox(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TintCheckBox(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, android.R.attr.checkboxStyle);
|
||||
}
|
||||
|
||||
public TintCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
if (TintManager.SHOULD_BE_USED) {
|
||||
TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
|
||||
TINT_ATTRS, defStyleAttr, 0);
|
||||
setButtonDrawable(a.getDrawable(0));
|
||||
a.recycle();
|
||||
|
||||
mTintManager = a.getTintManager();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setButtonDrawable(int resid) {
|
||||
if (mTintManager != null) {
|
||||
setButtonDrawable(mTintManager.getDrawable(resid));
|
||||
} else {
|
||||
super.setButtonDrawable(resid);
|
||||
}
|
||||
}
|
||||
}
|
67
android/support/v7/internal/widget/TintCheckedTextView.java
Normal file
67
android/support/v7/internal/widget/TintCheckedTextView.java
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.CheckedTextView;
|
||||
|
||||
/**
|
||||
* An tint aware {@link android.widget.CheckedTextView}.
|
||||
* <p>
|
||||
* This will automatically be used when you use {@link android.widget.CheckedTextView} in your
|
||||
* layouts. You should only need to manually use this class when writing custom views.
|
||||
*/
|
||||
public class TintCheckedTextView extends CheckedTextView {
|
||||
|
||||
private static final int[] TINT_ATTRS = {
|
||||
android.R.attr.checkMark
|
||||
};
|
||||
|
||||
private TintManager mTintManager;
|
||||
|
||||
public TintCheckedTextView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TintCheckedTextView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, android.R.attr.checkedTextViewStyle);
|
||||
}
|
||||
|
||||
public TintCheckedTextView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
if (TintManager.SHOULD_BE_USED) {
|
||||
TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
|
||||
TINT_ATTRS, defStyleAttr, 0);
|
||||
setCheckMarkDrawable(a.getDrawable(0));
|
||||
a.recycle();
|
||||
|
||||
mTintManager = a.getTintManager();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCheckMarkDrawable(int resid) {
|
||||
if (mTintManager != null) {
|
||||
setCheckMarkDrawable(mTintManager.getDrawable(resid));
|
||||
} else {
|
||||
super.setCheckMarkDrawable(resid);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
53
android/support/v7/internal/widget/TintContextWrapper.java
Normal file
53
android/support/v7/internal/widget/TintContextWrapper.java
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.res.Resources;
|
||||
|
||||
/**
|
||||
* A {@link android.content.ContextWrapper} which returns a tint-aware
|
||||
* {@link android.content.res.Resources} instance from {@link #getResources()}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
class TintContextWrapper extends ContextWrapper {
|
||||
|
||||
private final TintManager mTintManager;
|
||||
|
||||
public static Context wrap(Context context) {
|
||||
if (!(context instanceof TintContextWrapper)) {
|
||||
context = new TintContextWrapper(context);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
TintContextWrapper(Context base) {
|
||||
super(base);
|
||||
mTintManager = new TintManager(base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
return mTintManager.getResources();
|
||||
}
|
||||
|
||||
final TintManager getTintManager() {
|
||||
return mTintManager;
|
||||
}
|
||||
}
|
129
android/support/v7/internal/widget/TintEditText.java
Normal file
129
android/support/v7/internal/widget/TintEditText.java
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.TintableBackgroundView;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.EditText;
|
||||
|
||||
/**
|
||||
* An tint aware {@link android.widget.EditText}.
|
||||
* <p>
|
||||
* This will automatically be used when you use {@link android.widget.EditText} in your
|
||||
* layouts. You should only need to manually use this class when writing custom views.
|
||||
*/
|
||||
public class TintEditText extends EditText implements TintableBackgroundView {
|
||||
|
||||
private static final int[] TINT_ATTRS = {
|
||||
android.R.attr.background
|
||||
};
|
||||
|
||||
private TintInfo mBackgroundTint;
|
||||
|
||||
public TintEditText(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TintEditText(Context context, AttributeSet attrs) {
|
||||
this(TintContextWrapper.wrap(context), attrs, android.R.attr.editTextStyle);
|
||||
}
|
||||
|
||||
public TintEditText(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
if (TintManager.SHOULD_BE_USED) {
|
||||
TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
|
||||
TINT_ATTRS, defStyleAttr, 0);
|
||||
if (a.hasValue(0)) {
|
||||
setSupportBackgroundTintList(
|
||||
a.getTintManager().getColorStateList(a.getResourceId(0, -1)));
|
||||
}
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View,
|
||||
* android.content.res.ColorStateList)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
|
||||
if (mBackgroundTint == null) {
|
||||
mBackgroundTint = new TintInfo();
|
||||
}
|
||||
mBackgroundTint.mTintList = tint;
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public ColorStateList getSupportBackgroundTintList() {
|
||||
return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
|
||||
if (mBackgroundTint == null) {
|
||||
mBackgroundTint = new TintInfo();
|
||||
}
|
||||
mBackgroundTint.mTintMode = tintMode;
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public PorterDuff.Mode getSupportBackgroundTintMode() {
|
||||
return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawableStateChanged() {
|
||||
super.drawableStateChanged();
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
private void applySupportBackgroundTint() {
|
||||
if (getBackground() != null && mBackgroundTint != null) {
|
||||
TintManager.tintViewBackground(this, mBackgroundTint);
|
||||
}
|
||||
}
|
||||
}
|
70
android/support/v7/internal/widget/TintImageView.java
Normal file
70
android/support/v7/internal/widget/TintImageView.java
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
|
||||
/**
|
||||
* An tint aware {@link android.widget.ImageView}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class TintImageView extends ImageView {
|
||||
|
||||
private static final int[] TINT_ATTRS = {
|
||||
android.R.attr.background,
|
||||
android.R.attr.src
|
||||
};
|
||||
|
||||
private final TintManager mTintManager;
|
||||
|
||||
public TintImageView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TintImageView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public TintImageView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs, TINT_ATTRS,
|
||||
defStyleAttr, 0);
|
||||
if (a.length() > 0) {
|
||||
if (a.hasValue(0)) {
|
||||
setBackgroundDrawable(a.getDrawable(0));
|
||||
}
|
||||
if (a.hasValue(1)) {
|
||||
setImageDrawable(a.getDrawable(1));
|
||||
}
|
||||
}
|
||||
a.recycle();
|
||||
|
||||
// Keep the TintManager in case we need it later
|
||||
mTintManager = a.getTintManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageResource(@DrawableRes int resId) {
|
||||
// Intercept this call and instead retrieve the Drawable via the tint manager
|
||||
setImageDrawable(mTintManager.getDrawable(resId));
|
||||
}
|
||||
}
|
25
android/support/v7/internal/widget/TintInfo.java
Normal file
25
android/support/v7/internal/widget/TintInfo.java
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.PorterDuff;
|
||||
|
||||
class TintInfo {
|
||||
ColorStateList mTintList;
|
||||
PorterDuff.Mode mTintMode;
|
||||
}
|
511
android/support/v7/internal/widget/TintManager.java
Normal file
511
android/support/v7/internal/widget/TintManager.java
Normal file
|
@ -0,0 +1,511 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.support.v4.util.LruCache;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
|
||||
import static android.support.v7.internal.widget.ThemeUtils.getDisabledThemeAttrColor;
|
||||
import static android.support.v7.internal.widget.ThemeUtils.getThemeAttrColor;
|
||||
import static android.support.v7.internal.widget.ThemeUtils.getThemeAttrColorStateList;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final class TintManager {
|
||||
|
||||
static final boolean SHOULD_BE_USED = Build.VERSION.SDK_INT < 21;
|
||||
|
||||
private static final String TAG = TintManager.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
static final PorterDuff.Mode DEFAULT_MODE = PorterDuff.Mode.SRC_IN;
|
||||
|
||||
private static final ColorFilterLruCache COLOR_FILTER_CACHE = new ColorFilterLruCache(6);
|
||||
|
||||
/**
|
||||
* Drawables which should be tinted with the value of {@code R.attr.colorControlNormal},
|
||||
* using the default mode.
|
||||
*/
|
||||
private static final int[] TINT_COLOR_CONTROL_NORMAL = {
|
||||
R.drawable.abc_ic_ab_back_mtrl_am_alpha,
|
||||
R.drawable.abc_ic_go_search_api_mtrl_alpha,
|
||||
R.drawable.abc_ic_search_api_mtrl_alpha,
|
||||
R.drawable.abc_ic_commit_search_api_mtrl_alpha,
|
||||
R.drawable.abc_ic_clear_mtrl_alpha,
|
||||
R.drawable.abc_ic_menu_share_mtrl_alpha,
|
||||
R.drawable.abc_ic_menu_copy_mtrl_am_alpha,
|
||||
R.drawable.abc_ic_menu_cut_mtrl_alpha,
|
||||
R.drawable.abc_ic_menu_selectall_mtrl_alpha,
|
||||
R.drawable.abc_ic_menu_paste_mtrl_am_alpha,
|
||||
R.drawable.abc_ic_menu_moreoverflow_mtrl_alpha,
|
||||
R.drawable.abc_ic_voice_search_api_mtrl_alpha,
|
||||
R.drawable.abc_textfield_search_default_mtrl_alpha,
|
||||
R.drawable.abc_textfield_default_mtrl_alpha,
|
||||
R.drawable.abc_ab_share_pack_mtrl_alpha
|
||||
};
|
||||
|
||||
/**
|
||||
* Drawables which should be tinted with the value of {@code R.attr.colorControlActivated},
|
||||
* using the default mode.
|
||||
*/
|
||||
private static final int[] TINT_COLOR_CONTROL_ACTIVATED = {
|
||||
R.drawable.abc_textfield_activated_mtrl_alpha,
|
||||
R.drawable.abc_textfield_search_activated_mtrl_alpha,
|
||||
R.drawable.abc_cab_background_top_mtrl_alpha,
|
||||
R.drawable.abc_text_cursor_mtrl_alpha
|
||||
};
|
||||
|
||||
/**
|
||||
* Drawables which should be tinted with the value of {@code android.R.attr.colorBackground},
|
||||
* using the {@link android.graphics.PorterDuff.Mode#MULTIPLY} mode.
|
||||
*/
|
||||
private static final int[] TINT_COLOR_BACKGROUND_MULTIPLY = {
|
||||
R.drawable.abc_popup_background_mtrl_mult,
|
||||
R.drawable.abc_cab_background_internal_bg,
|
||||
R.drawable.abc_menu_hardkey_panel_mtrl_mult
|
||||
};
|
||||
|
||||
/**
|
||||
* Drawables which should be tinted using a state list containing values of
|
||||
* {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated}
|
||||
*/
|
||||
private static final int[] TINT_COLOR_CONTROL_STATE_LIST = {
|
||||
R.drawable.abc_edit_text_material,
|
||||
R.drawable.abc_tab_indicator_material,
|
||||
R.drawable.abc_textfield_search_material,
|
||||
R.drawable.abc_spinner_mtrl_am_alpha,
|
||||
R.drawable.abc_btn_check_material,
|
||||
R.drawable.abc_btn_radio_material,
|
||||
R.drawable.abc_spinner_textfield_background_material,
|
||||
R.drawable.abc_ratingbar_full_material,
|
||||
R.drawable.abc_switch_track_mtrl_alpha,
|
||||
R.drawable.abc_switch_thumb_material,
|
||||
R.drawable.abc_btn_default_mtrl_shape,
|
||||
R.drawable.abc_btn_borderless_material
|
||||
};
|
||||
|
||||
/**
|
||||
* Drawables which contain other drawables which should be tinted. The child drawable IDs
|
||||
* should be defined in one of the arrays above.
|
||||
*/
|
||||
private static final int[] CONTAINERS_WITH_TINT_CHILDREN = {
|
||||
R.drawable.abc_cab_background_top_material
|
||||
};
|
||||
|
||||
private final Context mContext;
|
||||
private final Resources mResources;
|
||||
private final TypedValue mTypedValue;
|
||||
|
||||
private final SparseArray<ColorStateList> mColorStateLists;
|
||||
private ColorStateList mDefaultColorStateList;
|
||||
|
||||
/**
|
||||
* A helper method to instantiate a {@link TintManager} and then call {@link #getDrawable(int)}.
|
||||
* This method should not be used routinely.
|
||||
*/
|
||||
public static Drawable getDrawable(Context context, int resId) {
|
||||
if (isInTintList(resId)) {
|
||||
final TintManager tm = (context instanceof TintContextWrapper)
|
||||
? ((TintContextWrapper) context).getTintManager()
|
||||
: new TintManager(context);
|
||||
return tm.getDrawable(resId);
|
||||
} else {
|
||||
return ContextCompat.getDrawable(context, resId);
|
||||
}
|
||||
}
|
||||
|
||||
public TintManager(Context context) {
|
||||
mColorStateLists = new SparseArray<>();
|
||||
mContext = context;
|
||||
mTypedValue = new TypedValue();
|
||||
mResources = new TintResources(context.getResources(), this);
|
||||
}
|
||||
|
||||
Resources getResources() {
|
||||
return mResources;
|
||||
}
|
||||
|
||||
public Drawable getDrawable(int resId) {
|
||||
Drawable drawable = ContextCompat.getDrawable(mContext, resId);
|
||||
|
||||
if (drawable != null) {
|
||||
drawable = drawable.mutate();
|
||||
|
||||
if (arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)) {
|
||||
ColorStateList colorStateList = getColorStateListForKnownDrawableId(resId);
|
||||
PorterDuff.Mode tintMode = DEFAULT_MODE;
|
||||
if (resId == R.drawable.abc_switch_thumb_material) {
|
||||
tintMode = PorterDuff.Mode.MULTIPLY;
|
||||
}
|
||||
|
||||
if (colorStateList != null) {
|
||||
drawable = DrawableCompat.wrap(drawable);
|
||||
DrawableCompat.setTintList(drawable, colorStateList);
|
||||
DrawableCompat.setTintMode(drawable, tintMode);
|
||||
}
|
||||
} else if (arrayContains(CONTAINERS_WITH_TINT_CHILDREN, resId)) {
|
||||
drawable = mResources.getDrawable(resId);
|
||||
} else {
|
||||
tintDrawable(resId, drawable);
|
||||
}
|
||||
}
|
||||
return drawable;
|
||||
}
|
||||
|
||||
void tintDrawable(final int resId, final Drawable drawable) {
|
||||
PorterDuff.Mode tintMode = null;
|
||||
boolean colorAttrSet = false;
|
||||
int colorAttr = 0;
|
||||
int alpha = -1;
|
||||
|
||||
if (arrayContains(TINT_COLOR_CONTROL_NORMAL, resId)) {
|
||||
colorAttr = R.attr.colorControlNormal;
|
||||
colorAttrSet = true;
|
||||
} else if (arrayContains(TINT_COLOR_CONTROL_ACTIVATED, resId)) {
|
||||
colorAttr = R.attr.colorControlActivated;
|
||||
colorAttrSet = true;
|
||||
} else if (arrayContains(TINT_COLOR_BACKGROUND_MULTIPLY, resId)) {
|
||||
colorAttr = android.R.attr.colorBackground;
|
||||
colorAttrSet = true;
|
||||
tintMode = PorterDuff.Mode.MULTIPLY;
|
||||
} else if (resId == R.drawable.abc_list_divider_mtrl_alpha) {
|
||||
colorAttr = android.R.attr.colorForeground;
|
||||
colorAttrSet = true;
|
||||
alpha = Math.round(0.16f * 255);
|
||||
}
|
||||
|
||||
if (colorAttrSet) {
|
||||
if (tintMode == null) {
|
||||
tintMode = DEFAULT_MODE;
|
||||
}
|
||||
final int color = getThemeAttrColor(mContext, colorAttr);
|
||||
|
||||
tintDrawableUsingColorFilter(drawable, color, tintMode);
|
||||
|
||||
if (alpha != -1) {
|
||||
drawable.setAlpha(alpha);
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Tinted Drawable ID: " + mResources.getResourceName(resId) +
|
||||
" with color: #" + Integer.toHexString(color));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean arrayContains(int[] array, int value) {
|
||||
for (int id : array) {
|
||||
if (id == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isInTintList(int drawableId) {
|
||||
return arrayContains(TINT_COLOR_BACKGROUND_MULTIPLY, drawableId) ||
|
||||
arrayContains(TINT_COLOR_CONTROL_NORMAL, drawableId) ||
|
||||
arrayContains(TINT_COLOR_CONTROL_ACTIVATED, drawableId) ||
|
||||
arrayContains(TINT_COLOR_CONTROL_STATE_LIST, drawableId) ||
|
||||
arrayContains(CONTAINERS_WITH_TINT_CHILDREN, drawableId);
|
||||
}
|
||||
|
||||
ColorStateList getColorStateList(int resId) {
|
||||
return arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)
|
||||
? getColorStateListForKnownDrawableId(resId)
|
||||
: null;
|
||||
}
|
||||
|
||||
private ColorStateList getColorStateListForKnownDrawableId(int resId) {
|
||||
// Try the cache first
|
||||
ColorStateList colorStateList = mColorStateLists.get(resId);
|
||||
|
||||
if (colorStateList == null) {
|
||||
// ...if the cache did not contain a color state list, try and create
|
||||
if (resId == R.drawable.abc_edit_text_material) {
|
||||
colorStateList = createEditTextColorStateList();
|
||||
} else if (resId == R.drawable.abc_switch_track_mtrl_alpha) {
|
||||
colorStateList = createSwitchTrackColorStateList();
|
||||
} else if (resId == R.drawable.abc_switch_thumb_material) {
|
||||
colorStateList = createSwitchThumbColorStateList();
|
||||
} else if (resId == R.drawable.abc_btn_default_mtrl_shape
|
||||
|| resId == R.drawable.abc_btn_borderless_material) {
|
||||
colorStateList = createButtonColorStateList();
|
||||
} else if (resId == R.drawable.abc_spinner_mtrl_am_alpha
|
||||
|| resId == R.drawable.abc_spinner_textfield_background_material) {
|
||||
colorStateList = createSpinnerColorStateList();
|
||||
} else {
|
||||
// If we don't have an explicit color state list for this Drawable, use the default
|
||||
colorStateList = getDefaultColorStateList();
|
||||
}
|
||||
|
||||
// ..and add it to the cache
|
||||
mColorStateLists.append(resId, colorStateList);
|
||||
}
|
||||
return colorStateList;
|
||||
}
|
||||
|
||||
private ColorStateList getDefaultColorStateList() {
|
||||
if (mDefaultColorStateList == null) {
|
||||
/**
|
||||
* Generate the default color state list which uses the colorControl attributes.
|
||||
* Order is important here. The default enabled state needs to go at the bottom.
|
||||
*/
|
||||
|
||||
final int colorControlNormal = getThemeAttrColor(mContext, R.attr.colorControlNormal);
|
||||
final int colorControlActivated = getThemeAttrColor(mContext,
|
||||
R.attr.colorControlActivated);
|
||||
|
||||
final int[][] states = new int[7][];
|
||||
final int[] colors = new int[7];
|
||||
int i = 0;
|
||||
|
||||
// Disabled state
|
||||
states[i] = new int[] { -android.R.attr.state_enabled };
|
||||
colors[i] = getDisabledThemeAttrColor(mContext, R.attr.colorControlNormal);
|
||||
i++;
|
||||
|
||||
states[i] = new int[] { android.R.attr.state_focused };
|
||||
colors[i] = colorControlActivated;
|
||||
i++;
|
||||
|
||||
states[i] = new int[] { android.R.attr.state_activated };
|
||||
colors[i] = colorControlActivated;
|
||||
i++;
|
||||
|
||||
states[i] = new int[] { android.R.attr.state_pressed };
|
||||
colors[i] = colorControlActivated;
|
||||
i++;
|
||||
|
||||
states[i] = new int[] { android.R.attr.state_checked };
|
||||
colors[i] = colorControlActivated;
|
||||
i++;
|
||||
|
||||
states[i] = new int[] { android.R.attr.state_selected };
|
||||
colors[i] = colorControlActivated;
|
||||
i++;
|
||||
|
||||
// Default enabled state
|
||||
states[i] = new int[0];
|
||||
colors[i] = colorControlNormal;
|
||||
i++;
|
||||
|
||||
mDefaultColorStateList = new ColorStateList(states, colors);
|
||||
}
|
||||
return mDefaultColorStateList;
|
||||
}
|
||||
|
||||
private ColorStateList createSwitchTrackColorStateList() {
|
||||
final int[][] states = new int[3][];
|
||||
final int[] colors = new int[3];
|
||||
int i = 0;
|
||||
|
||||
// Disabled state
|
||||
states[i] = new int[]{-android.R.attr.state_enabled};
|
||||
colors[i] = getThemeAttrColor(mContext, android.R.attr.colorForeground, 0.1f);
|
||||
i++;
|
||||
|
||||
states[i] = new int[]{android.R.attr.state_checked};
|
||||
colors[i] = getThemeAttrColor(mContext, R.attr.colorControlActivated, 0.3f);
|
||||
i++;
|
||||
|
||||
// Default enabled state
|
||||
states[i] = new int[0];
|
||||
colors[i] = getThemeAttrColor(mContext, android.R.attr.colorForeground, 0.3f);
|
||||
i++;
|
||||
|
||||
return new ColorStateList(states, colors);
|
||||
}
|
||||
|
||||
private ColorStateList createSwitchThumbColorStateList() {
|
||||
final int[][] states = new int[3][];
|
||||
final int[] colors = new int[3];
|
||||
int i = 0;
|
||||
|
||||
final ColorStateList thumbColor = getThemeAttrColorStateList(mContext,
|
||||
R.attr.colorSwitchThumbNormal);
|
||||
|
||||
if (thumbColor != null && thumbColor.isStateful()) {
|
||||
// If colorSwitchThumbNormal is a valid ColorStateList, extract the default and
|
||||
// disabled colors from it
|
||||
|
||||
// Disabled state
|
||||
states[i] = new int[]{-android.R.attr.state_enabled};
|
||||
colors[i] = thumbColor.getColorForState(states[i], 0);
|
||||
i++;
|
||||
|
||||
states[i] = new int[]{android.R.attr.state_checked};
|
||||
colors[i] = getThemeAttrColor(mContext, R.attr.colorControlActivated);
|
||||
i++;
|
||||
|
||||
// Default enabled state
|
||||
states[i] = new int[0];
|
||||
colors[i] = thumbColor.getDefaultColor();
|
||||
i++;
|
||||
} else {
|
||||
// Else we'll use an approximation using the default disabled alpha
|
||||
|
||||
// Disabled state
|
||||
states[i] = new int[]{-android.R.attr.state_enabled};
|
||||
colors[i] = getDisabledThemeAttrColor(mContext, R.attr.colorSwitchThumbNormal);
|
||||
i++;
|
||||
|
||||
states[i] = new int[]{android.R.attr.state_checked};
|
||||
colors[i] = getThemeAttrColor(mContext, R.attr.colorControlActivated);
|
||||
i++;
|
||||
|
||||
// Default enabled state
|
||||
states[i] = new int[0];
|
||||
colors[i] = getThemeAttrColor(mContext, R.attr.colorSwitchThumbNormal);
|
||||
i++;
|
||||
}
|
||||
|
||||
return new ColorStateList(states, colors);
|
||||
}
|
||||
|
||||
private ColorStateList createEditTextColorStateList() {
|
||||
final int[][] states = new int[3][];
|
||||
final int[] colors = new int[3];
|
||||
int i = 0;
|
||||
|
||||
// Disabled state
|
||||
states[i] = new int[]{-android.R.attr.state_enabled};
|
||||
colors[i] = getDisabledThemeAttrColor(mContext, R.attr.colorControlNormal);
|
||||
i++;
|
||||
|
||||
states[i] = new int[]{-android.R.attr.state_pressed, -android.R.attr.state_focused};
|
||||
colors[i] = getThemeAttrColor(mContext, R.attr.colorControlNormal);
|
||||
i++;
|
||||
|
||||
// Default enabled state
|
||||
states[i] = new int[0];
|
||||
colors[i] = getThemeAttrColor(mContext, R.attr.colorControlActivated);
|
||||
i++;
|
||||
|
||||
return new ColorStateList(states, colors);
|
||||
}
|
||||
|
||||
private ColorStateList createButtonColorStateList() {
|
||||
final int[][] states = new int[4][];
|
||||
final int[] colors = new int[4];
|
||||
int i = 0;
|
||||
|
||||
// Disabled state
|
||||
states[i] = new int[]{-android.R.attr.state_enabled};
|
||||
colors[i] = getDisabledThemeAttrColor(mContext, R.attr.colorButtonNormal);
|
||||
i++;
|
||||
|
||||
states[i] = new int[]{android.R.attr.state_pressed};
|
||||
colors[i] = getThemeAttrColor(mContext, R.attr.colorControlHighlight);
|
||||
i++;
|
||||
|
||||
states[i] = new int[]{android.R.attr.state_focused};
|
||||
colors[i] = getThemeAttrColor(mContext, R.attr.colorControlHighlight);
|
||||
i++;
|
||||
|
||||
// Default enabled state
|
||||
states[i] = new int[0];
|
||||
colors[i] = getThemeAttrColor(mContext, R.attr.colorButtonNormal);
|
||||
i++;
|
||||
|
||||
return new ColorStateList(states, colors);
|
||||
}
|
||||
|
||||
private ColorStateList createSpinnerColorStateList() {
|
||||
final int[][] states = new int[3][];
|
||||
final int[] colors = new int[3];
|
||||
int i = 0;
|
||||
|
||||
// Disabled state
|
||||
states[i] = new int[]{-android.R.attr.state_enabled};
|
||||
colors[i] = getDisabledThemeAttrColor(mContext, R.attr.colorControlNormal);
|
||||
i++;
|
||||
|
||||
states[i] = new int[]{-android.R.attr.state_pressed, -android.R.attr.state_focused};
|
||||
colors[i] = getThemeAttrColor(mContext, R.attr.colorControlNormal);
|
||||
i++;
|
||||
|
||||
states[i] = new int[0];
|
||||
colors[i] = getThemeAttrColor(mContext, R.attr.colorControlActivated);
|
||||
i++;
|
||||
|
||||
return new ColorStateList(states, colors);
|
||||
}
|
||||
|
||||
private static class ColorFilterLruCache extends LruCache<Integer, PorterDuffColorFilter> {
|
||||
|
||||
public ColorFilterLruCache(int maxSize) {
|
||||
super(maxSize);
|
||||
}
|
||||
|
||||
PorterDuffColorFilter get(int color, PorterDuff.Mode mode) {
|
||||
return get(generateCacheKey(color, mode));
|
||||
}
|
||||
|
||||
PorterDuffColorFilter put(int color, PorterDuff.Mode mode, PorterDuffColorFilter filter) {
|
||||
return put(generateCacheKey(color, mode), filter);
|
||||
}
|
||||
|
||||
private static int generateCacheKey(int color, PorterDuff.Mode mode) {
|
||||
int hashCode = 1;
|
||||
hashCode = 31 * hashCode + color;
|
||||
hashCode = 31 * hashCode + mode.hashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static void tintViewBackground(View view, TintInfo tint) {
|
||||
final Drawable background = view.getBackground();
|
||||
if (tint.mTintList != null) {
|
||||
tintDrawableUsingColorFilter(
|
||||
background,
|
||||
tint.mTintList.getColorForState(view.getDrawableState(),
|
||||
tint.mTintList.getDefaultColor()),
|
||||
tint.mTintMode != null ? tint.mTintMode : DEFAULT_MODE);
|
||||
} else {
|
||||
background.clearColorFilter();
|
||||
}
|
||||
}
|
||||
|
||||
private static void tintDrawableUsingColorFilter(Drawable drawable, int color,
|
||||
PorterDuff.Mode mode) {
|
||||
// First, lets see if the cache already contains the color filter
|
||||
PorterDuffColorFilter filter = COLOR_FILTER_CACHE.get(color, mode);
|
||||
|
||||
if (filter == null) {
|
||||
// Cache miss, so create a color filter and add it to the cache
|
||||
filter = new PorterDuffColorFilter(color, mode);
|
||||
COLOR_FILTER_CACHE.put(color, mode, filter);
|
||||
}
|
||||
|
||||
drawable.setColorFilter(filter);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.TintableBackgroundView;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.MultiAutoCompleteTextView;
|
||||
|
||||
/**
|
||||
* An tint aware {@link android.widget.MultiAutoCompleteTextView}.
|
||||
* <p>
|
||||
* This will automatically be used when you use {@link android.widget.MultiAutoCompleteTextView}
|
||||
* in your layouts. You should only need to manually use this class when writing custom views.
|
||||
*/
|
||||
public class TintMultiAutoCompleteTextView extends MultiAutoCompleteTextView
|
||||
implements TintableBackgroundView {
|
||||
|
||||
private static final int[] TINT_ATTRS = {
|
||||
android.R.attr.background,
|
||||
android.R.attr.popupBackground
|
||||
};
|
||||
|
||||
private TintManager mTintManager;
|
||||
private TintInfo mBackgroundTint;
|
||||
|
||||
public TintMultiAutoCompleteTextView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TintMultiAutoCompleteTextView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, android.R.attr.autoCompleteTextViewStyle);
|
||||
}
|
||||
|
||||
public TintMultiAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
|
||||
|
||||
if (TintManager.SHOULD_BE_USED) {
|
||||
TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
|
||||
TINT_ATTRS, defStyleAttr, 0);
|
||||
mTintManager = a.getTintManager();
|
||||
|
||||
if (a.hasValue(0)) {
|
||||
setSupportBackgroundTintList(
|
||||
mTintManager.getColorStateList(a.getResourceId(0, -1)));
|
||||
}
|
||||
if (a.hasValue(1)) {
|
||||
setDropDownBackgroundDrawable(a.getDrawable(1));
|
||||
}
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDropDownBackgroundResource(int id) {
|
||||
if (mTintManager != null) {
|
||||
setDropDownBackgroundDrawable(mTintManager.getDrawable(id));
|
||||
} else {
|
||||
super.setDropDownBackgroundResource(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View,
|
||||
* android.content.res.ColorStateList)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
|
||||
if (mBackgroundTint == null) {
|
||||
mBackgroundTint = new TintInfo();
|
||||
}
|
||||
mBackgroundTint.mTintList = tint;
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public ColorStateList getSupportBackgroundTintList() {
|
||||
return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
|
||||
if (mBackgroundTint == null) {
|
||||
mBackgroundTint = new TintInfo();
|
||||
}
|
||||
mBackgroundTint.mTintMode = tintMode;
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public PorterDuff.Mode getSupportBackgroundTintMode() {
|
||||
return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawableStateChanged() {
|
||||
super.drawableStateChanged();
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
private void applySupportBackgroundTint() {
|
||||
if (getBackground() != null && mBackgroundTint != null) {
|
||||
TintManager.tintViewBackground(this, mBackgroundTint);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
66
android/support/v7/internal/widget/TintRadioButton.java
Normal file
66
android/support/v7/internal/widget/TintRadioButton.java
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.RadioButton;
|
||||
|
||||
/**
|
||||
* An tint aware {@link android.widget.RadioButton}.
|
||||
* <p>
|
||||
* This will automatically be used when you use {@link android.widget.RadioButton} in your
|
||||
* layouts. You should only need to manually use this class when writing custom views.
|
||||
*/
|
||||
public class TintRadioButton extends RadioButton {
|
||||
|
||||
private static final int[] TINT_ATTRS = {
|
||||
android.R.attr.button
|
||||
};
|
||||
|
||||
private TintManager mTintManager;
|
||||
|
||||
public TintRadioButton(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TintRadioButton(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, android.R.attr.radioButtonStyle);
|
||||
}
|
||||
|
||||
public TintRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
if (TintManager.SHOULD_BE_USED) {
|
||||
TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
|
||||
TINT_ATTRS, defStyleAttr, 0);
|
||||
setButtonDrawable(a.getDrawable(0));
|
||||
a.recycle();
|
||||
|
||||
mTintManager = a.getTintManager();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setButtonDrawable(int resid) {
|
||||
if (mTintManager != null) {
|
||||
setButtonDrawable(mTintManager.getDrawable(resid));
|
||||
} else {
|
||||
super.setButtonDrawable(resid);
|
||||
}
|
||||
}
|
||||
}
|
166
android/support/v7/internal/widget/TintRatingBar.java
Normal file
166
android/support/v7/internal/widget/TintRatingBar.java
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.AnimationDrawable;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ClipDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.graphics.drawable.shapes.RoundRectShape;
|
||||
import android.graphics.drawable.shapes.Shape;
|
||||
import android.support.v4.graphics.drawable.DrawableWrapper;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.widget.RatingBar;
|
||||
|
||||
/**
|
||||
* An tint aware {@link android.widget.RatingBar}.
|
||||
* <p>
|
||||
* This will automatically be used when you use {@link android.widget.RatingBar} in your
|
||||
* layouts. You should only need to manually use this class when writing custom views.
|
||||
*/
|
||||
public class TintRatingBar extends RatingBar {
|
||||
|
||||
private static final int[] TINT_ATTRS = {
|
||||
android.R.attr.indeterminateDrawable,
|
||||
android.R.attr.progressDrawable
|
||||
};
|
||||
|
||||
private Bitmap mSampleTile;
|
||||
|
||||
public TintRatingBar(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TintRatingBar(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, android.R.attr.ratingBarStyle);
|
||||
}
|
||||
|
||||
public TintRatingBar(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
if (TintManager.SHOULD_BE_USED) {
|
||||
TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
|
||||
TINT_ATTRS, defStyleAttr, 0);
|
||||
|
||||
Drawable drawable = a.getDrawable(0);
|
||||
if (drawable != null) {
|
||||
setIndeterminateDrawable(tileifyIndeterminate(drawable));
|
||||
}
|
||||
|
||||
drawable = a.getDrawable(1);
|
||||
if (drawable != null) {
|
||||
setProgressDrawable(tileify(drawable, false));
|
||||
}
|
||||
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a drawable to a tiled version of itself. It will recursively
|
||||
* traverse layer and state list drawables.
|
||||
*/
|
||||
private Drawable tileify(Drawable drawable, boolean clip) {
|
||||
if (drawable instanceof DrawableWrapper) {
|
||||
Drawable inner = ((DrawableWrapper) drawable).getWrappedDrawable();
|
||||
if (inner != null) {
|
||||
inner = tileify(inner, clip);
|
||||
((DrawableWrapper) drawable).setWrappedDrawable(inner);
|
||||
}
|
||||
} else if (drawable instanceof LayerDrawable) {
|
||||
LayerDrawable background = (LayerDrawable) drawable;
|
||||
final int N = background.getNumberOfLayers();
|
||||
Drawable[] outDrawables = new Drawable[N];
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
int id = background.getId(i);
|
||||
outDrawables[i] = tileify(background.getDrawable(i),
|
||||
(id == android.R.id.progress || id == android.R.id.secondaryProgress));
|
||||
}
|
||||
LayerDrawable newBg = new LayerDrawable(outDrawables);
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
newBg.setId(i, background.getId(i));
|
||||
}
|
||||
|
||||
return newBg;
|
||||
|
||||
} else if (drawable instanceof BitmapDrawable) {
|
||||
final Bitmap tileBitmap = ((BitmapDrawable) drawable).getBitmap();
|
||||
if (mSampleTile == null) {
|
||||
mSampleTile = tileBitmap;
|
||||
}
|
||||
|
||||
final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());
|
||||
final BitmapShader bitmapShader = new BitmapShader(tileBitmap,
|
||||
Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
|
||||
shapeDrawable.getPaint().setShader(bitmapShader);
|
||||
return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT,
|
||||
ClipDrawable.HORIZONTAL) : shapeDrawable;
|
||||
}
|
||||
|
||||
return drawable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a AnimationDrawable for use as a barberpole animation.
|
||||
* Each frame of the animation is wrapped in a ClipDrawable and
|
||||
* given a tiling BitmapShader.
|
||||
*/
|
||||
private Drawable tileifyIndeterminate(Drawable drawable) {
|
||||
if (drawable instanceof AnimationDrawable) {
|
||||
AnimationDrawable background = (AnimationDrawable) drawable;
|
||||
final int N = background.getNumberOfFrames();
|
||||
AnimationDrawable newBg = new AnimationDrawable();
|
||||
newBg.setOneShot(background.isOneShot());
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
Drawable frame = tileify(background.getFrame(i), true);
|
||||
frame.setLevel(10000);
|
||||
newBg.addFrame(frame, background.getDuration(i));
|
||||
}
|
||||
newBg.setLevel(10000);
|
||||
drawable = newBg;
|
||||
}
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private Shape getDrawableShape() {
|
||||
final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };
|
||||
return new RoundRectShape(roundedCorners, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
if (mSampleTile != null) {
|
||||
final int width = mSampleTile.getWidth() * getNumStars();
|
||||
setMeasuredDimension(ViewCompat.resolveSizeAndState(width, widthMeasureSpec, 0),
|
||||
getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
49
android/support/v7/internal/widget/TintResources.java
Normal file
49
android/support/v7/internal/widget/TintResources.java
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v7.appcompat.R;
|
||||
|
||||
/**
|
||||
* This class allows us to intercept calls so that we can tint resources (if applicable).
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
class TintResources extends ResourcesWrapper {
|
||||
|
||||
private final TintManager mTintManager;
|
||||
|
||||
public TintResources(Resources resources, TintManager tintManager) {
|
||||
super(resources);
|
||||
mTintManager = tintManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* We intercept this call so that we tint the result (if applicable). This is needed for things
|
||||
* like {@link DrawableContainer}s which retrieve their children via this method.
|
||||
*/
|
||||
@Override
|
||||
public Drawable getDrawable(int id) throws NotFoundException {
|
||||
Drawable d = super.getDrawable(id);
|
||||
if (d != null) {
|
||||
mTintManager.tintDrawable(id, d);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
}
|
163
android/support/v7/internal/widget/TintSpinner.java
Normal file
163
android/support/v7/internal/widget/TintSpinner.java
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.TintableBackgroundView;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ListPopupWindow;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* An tint aware {@link android.widget.Spinner}.
|
||||
* <p>
|
||||
* This will automatically be used when you use {@link android.widget.Spinner} in your
|
||||
* layouts. You should only need to manually use this class when writing custom views.
|
||||
*/
|
||||
public class TintSpinner extends Spinner implements TintableBackgroundView {
|
||||
|
||||
private static final int[] TINT_ATTRS = {
|
||||
android.R.attr.background,
|
||||
android.R.attr.popupBackground
|
||||
};
|
||||
|
||||
private TintInfo mBackgroundTint;
|
||||
|
||||
public TintSpinner(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TintSpinner(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, android.R.attr.spinnerStyle);
|
||||
}
|
||||
|
||||
public TintSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
if (TintManager.SHOULD_BE_USED) {
|
||||
TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
|
||||
TINT_ATTRS, defStyleAttr, 0);
|
||||
if (a.hasValue(0)) {
|
||||
setSupportBackgroundTintList(
|
||||
a.getTintManager().getColorStateList(a.getResourceId(0, -1)));
|
||||
}
|
||||
if (a.hasValue(1)) {
|
||||
final Drawable popupBackground = a.getDrawable(1);
|
||||
if (Build.VERSION.SDK_INT >= 16) {
|
||||
setPopupBackgroundDrawable(popupBackground);
|
||||
} else if (Build.VERSION.SDK_INT >= 11) {
|
||||
setPopupBackgroundDrawableV11(this, popupBackground);
|
||||
}
|
||||
}
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
private static void setPopupBackgroundDrawableV11(Spinner view, Drawable background) {
|
||||
try {
|
||||
Field popupField = Spinner.class.getDeclaredField("mPopup");
|
||||
popupField.setAccessible(true);
|
||||
|
||||
Object popup = popupField.get(view);
|
||||
|
||||
if (popup instanceof ListPopupWindow) {
|
||||
((ListPopupWindow) popup).setBackgroundDrawable(background);
|
||||
}
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View,
|
||||
* android.content.res.ColorStateList)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
|
||||
if (mBackgroundTint == null && tint != null) {
|
||||
mBackgroundTint = new TintInfo();
|
||||
}
|
||||
mBackgroundTint.mTintList = tint;
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public ColorStateList getSupportBackgroundTintList() {
|
||||
return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
|
||||
if (mBackgroundTint == null) {
|
||||
mBackgroundTint = new TintInfo();
|
||||
}
|
||||
mBackgroundTint.mTintMode = tintMode;
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be accessed via
|
||||
* {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public PorterDuff.Mode getSupportBackgroundTintMode() {
|
||||
return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawableStateChanged() {
|
||||
super.drawableStateChanged();
|
||||
applySupportBackgroundTint();
|
||||
}
|
||||
|
||||
private void applySupportBackgroundTint() {
|
||||
if (getBackground() != null && mBackgroundTint != null) {
|
||||
TintManager.tintViewBackground(this, mBackgroundTint);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
189
android/support/v7/internal/widget/TintTypedArray.java
Normal file
189
android/support/v7/internal/widget/TintTypedArray.java
Normal file
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
|
||||
/**
|
||||
* A class that wraps a {@link android.content.res.TypedArray} and provides the same public API
|
||||
* surface. The purpose of this class is so that we can intercept the {@link #getDrawable(int)}
|
||||
* call and tint the result.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class TintTypedArray {
|
||||
|
||||
private final Context mContext;
|
||||
private final TypedArray mWrapped;
|
||||
|
||||
private TintManager mTintManager;
|
||||
|
||||
public static TintTypedArray obtainStyledAttributes(Context context, AttributeSet set,
|
||||
int[] attrs) {
|
||||
TypedArray array = context.obtainStyledAttributes(set, attrs);
|
||||
return new TintTypedArray(context, array);
|
||||
}
|
||||
|
||||
public static TintTypedArray obtainStyledAttributes(Context context, AttributeSet set,
|
||||
int[] attrs, int defStyleAttr, int defStyleRes) {
|
||||
TypedArray array = context.obtainStyledAttributes(set, attrs, defStyleAttr, defStyleRes);
|
||||
return new TintTypedArray(context, array);
|
||||
}
|
||||
|
||||
private TintTypedArray(Context context, TypedArray array) {
|
||||
mContext = context;
|
||||
mWrapped = array;
|
||||
}
|
||||
|
||||
public Drawable getDrawable(int index) {
|
||||
if (mWrapped.hasValue(index)) {
|
||||
final int resourceId = mWrapped.getResourceId(index, 0);
|
||||
if (resourceId != 0) {
|
||||
return getTintManager().getDrawable(resourceId);
|
||||
}
|
||||
}
|
||||
return mWrapped.getDrawable(index);
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return mWrapped.length();
|
||||
}
|
||||
|
||||
public int getIndexCount() {
|
||||
return mWrapped.getIndexCount();
|
||||
}
|
||||
|
||||
public int getIndex(int at) {
|
||||
return mWrapped.getIndex(at);
|
||||
}
|
||||
|
||||
public Resources getResources() {
|
||||
return mWrapped.getResources();
|
||||
}
|
||||
|
||||
public CharSequence getText(int index) {
|
||||
return mWrapped.getText(index);
|
||||
}
|
||||
|
||||
public String getString(int index) {
|
||||
return mWrapped.getString(index);
|
||||
}
|
||||
|
||||
public String getNonResourceString(int index) {
|
||||
return mWrapped.getNonResourceString(index);
|
||||
}
|
||||
|
||||
public boolean getBoolean(int index, boolean defValue) {
|
||||
return mWrapped.getBoolean(index, defValue);
|
||||
}
|
||||
|
||||
public int getInt(int index, int defValue) {
|
||||
return mWrapped.getInt(index, defValue);
|
||||
}
|
||||
|
||||
public float getFloat(int index, float defValue) {
|
||||
return mWrapped.getFloat(index, defValue);
|
||||
}
|
||||
|
||||
public int getColor(int index, int defValue) {
|
||||
return mWrapped.getColor(index, defValue);
|
||||
}
|
||||
|
||||
public ColorStateList getColorStateList(int index) {
|
||||
return mWrapped.getColorStateList(index);
|
||||
}
|
||||
|
||||
public int getInteger(int index, int defValue) {
|
||||
return mWrapped.getInteger(index, defValue);
|
||||
}
|
||||
|
||||
public float getDimension(int index, float defValue) {
|
||||
return mWrapped.getDimension(index, defValue);
|
||||
}
|
||||
|
||||
public int getDimensionPixelOffset(int index, int defValue) {
|
||||
return mWrapped.getDimensionPixelOffset(index, defValue);
|
||||
}
|
||||
|
||||
public int getDimensionPixelSize(int index, int defValue) {
|
||||
return mWrapped.getDimensionPixelSize(index, defValue);
|
||||
}
|
||||
|
||||
public int getLayoutDimension(int index, String name) {
|
||||
return mWrapped.getLayoutDimension(index, name);
|
||||
}
|
||||
|
||||
public int getLayoutDimension(int index, int defValue) {
|
||||
return mWrapped.getLayoutDimension(index, defValue);
|
||||
}
|
||||
|
||||
public float getFraction(int index, int base, int pbase, float defValue) {
|
||||
return mWrapped.getFraction(index, base, pbase, defValue);
|
||||
}
|
||||
|
||||
public int getResourceId(int index, int defValue) {
|
||||
return mWrapped.getResourceId(index, defValue);
|
||||
}
|
||||
|
||||
public CharSequence[] getTextArray(int index) {
|
||||
return mWrapped.getTextArray(index);
|
||||
}
|
||||
|
||||
public boolean getValue(int index, TypedValue outValue) {
|
||||
return mWrapped.getValue(index, outValue);
|
||||
}
|
||||
|
||||
public int getType(int index) {
|
||||
return mWrapped.getType(index);
|
||||
}
|
||||
|
||||
public boolean hasValue(int index) {
|
||||
return mWrapped.hasValue(index);
|
||||
}
|
||||
|
||||
public TypedValue peekValue(int index) {
|
||||
return mWrapped.peekValue(index);
|
||||
}
|
||||
|
||||
public String getPositionDescription() {
|
||||
return mWrapped.getPositionDescription();
|
||||
}
|
||||
|
||||
public void recycle() {
|
||||
mWrapped.recycle();
|
||||
}
|
||||
|
||||
public int getChangingConfigurations() {
|
||||
return mWrapped.getChangingConfigurations();
|
||||
}
|
||||
|
||||
public TintManager getTintManager() {
|
||||
if (mTintManager == null) {
|
||||
mTintManager = (mContext instanceof TintContextWrapper)
|
||||
? ((TintContextWrapper) mContext).getTintManager()
|
||||
: new TintManager(mContext);
|
||||
}
|
||||
return mTintManager;
|
||||
}
|
||||
|
||||
}
|
716
android/support/v7/internal/widget/ToolbarWidgetWrapper.java
Normal file
716
android/support/v7/internal/widget/ToolbarWidgetWrapper.java
Normal file
|
@ -0,0 +1,716 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.internal.view.menu.ActionMenuItem;
|
||||
import android.support.v7.internal.view.menu.MenuBuilder;
|
||||
import android.support.v7.internal.view.menu.MenuPresenter;
|
||||
import android.support.v7.widget.ActionMenuPresenter;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.SpinnerAdapter;
|
||||
|
||||
/**
|
||||
* Internal class used to interact with the Toolbar widget without
|
||||
* exposing interface methods to the public API.
|
||||
*
|
||||
* <p>ToolbarWidgetWrapper manages the differences between Toolbar and ActionBarView
|
||||
* so that either variant acting as a
|
||||
* {@link android.support.v7.internal.app.WindowDecorActionBar WindowDecorActionBar} can behave
|
||||
* in the same way.</p>
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ToolbarWidgetWrapper implements DecorToolbar {
|
||||
private static final String TAG = "ToolbarWidgetWrapper";
|
||||
|
||||
private static final int AFFECTS_LOGO_MASK =
|
||||
ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_USE_LOGO;
|
||||
|
||||
private Toolbar mToolbar;
|
||||
|
||||
private int mDisplayOpts;
|
||||
private View mTabView;
|
||||
private SpinnerCompat mSpinner;
|
||||
private View mCustomView;
|
||||
|
||||
private Drawable mIcon;
|
||||
private Drawable mLogo;
|
||||
private Drawable mNavIcon;
|
||||
|
||||
private boolean mTitleSet;
|
||||
private CharSequence mTitle;
|
||||
private CharSequence mSubtitle;
|
||||
private CharSequence mHomeDescription;
|
||||
|
||||
private Window.Callback mWindowCallback;
|
||||
private boolean mMenuPrepared;
|
||||
private ActionMenuPresenter mActionMenuPresenter;
|
||||
|
||||
private int mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD;
|
||||
|
||||
private final TintManager mTintManager;
|
||||
private int mDefaultNavigationContentDescription = 0;
|
||||
private Drawable mDefaultNavigationIcon;
|
||||
|
||||
public ToolbarWidgetWrapper(Toolbar toolbar, boolean style) {
|
||||
this(toolbar, style, R.string.abc_action_bar_up_description,
|
||||
R.drawable.abc_ic_ab_back_mtrl_am_alpha);
|
||||
}
|
||||
|
||||
public ToolbarWidgetWrapper(Toolbar toolbar, boolean style,
|
||||
int defaultNavigationContentDescription, int defaultNavigationIcon) {
|
||||
mToolbar = toolbar;
|
||||
mTitle = toolbar.getTitle();
|
||||
mSubtitle = toolbar.getSubtitle();
|
||||
mTitleSet = mTitle != null;
|
||||
mNavIcon = toolbar.getNavigationIcon();
|
||||
|
||||
if (style) {
|
||||
final TintTypedArray a = TintTypedArray.obtainStyledAttributes(toolbar.getContext(),
|
||||
null, R.styleable.ActionBar, R.attr.actionBarStyle, 0);
|
||||
|
||||
final CharSequence title = a.getText(R.styleable.ActionBar_title);
|
||||
if (!TextUtils.isEmpty(title)) {
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
final CharSequence subtitle = a.getText(R.styleable.ActionBar_subtitle);
|
||||
if (!TextUtils.isEmpty(subtitle)) {
|
||||
setSubtitle(subtitle);
|
||||
}
|
||||
|
||||
final Drawable logo = a.getDrawable(R.styleable.ActionBar_logo);
|
||||
if (logo != null) {
|
||||
setLogo(logo);
|
||||
}
|
||||
|
||||
final Drawable icon = a.getDrawable(R.styleable.ActionBar_icon);
|
||||
if (mNavIcon == null && icon != null) {
|
||||
setIcon(icon);
|
||||
}
|
||||
|
||||
final Drawable navIcon = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator);
|
||||
if (navIcon != null) {
|
||||
setNavigationIcon(navIcon);
|
||||
}
|
||||
|
||||
setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, 0));
|
||||
|
||||
final int customNavId = a.getResourceId(
|
||||
R.styleable.ActionBar_customNavigationLayout, 0);
|
||||
if (customNavId != 0) {
|
||||
setCustomView(LayoutInflater.from(mToolbar.getContext()).inflate(customNavId,
|
||||
mToolbar, false));
|
||||
setDisplayOptions(mDisplayOpts | ActionBar.DISPLAY_SHOW_CUSTOM);
|
||||
}
|
||||
|
||||
final int height = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
|
||||
if (height > 0) {
|
||||
final ViewGroup.LayoutParams lp = mToolbar.getLayoutParams();
|
||||
lp.height = height;
|
||||
mToolbar.setLayoutParams(lp);
|
||||
}
|
||||
|
||||
final int contentInsetStart = a.getDimensionPixelOffset(
|
||||
R.styleable.ActionBar_contentInsetStart, -1);
|
||||
final int contentInsetEnd = a.getDimensionPixelOffset(
|
||||
R.styleable.ActionBar_contentInsetEnd, -1);
|
||||
if (contentInsetStart >= 0 || contentInsetEnd >= 0) {
|
||||
mToolbar.setContentInsetsRelative(Math.max(contentInsetStart, 0),
|
||||
Math.max(contentInsetEnd, 0));
|
||||
}
|
||||
|
||||
final int titleTextStyle = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0);
|
||||
if (titleTextStyle != 0) {
|
||||
mToolbar.setTitleTextAppearance(mToolbar.getContext(), titleTextStyle);
|
||||
}
|
||||
|
||||
final int subtitleTextStyle = a.getResourceId(
|
||||
R.styleable.ActionBar_subtitleTextStyle, 0);
|
||||
if (subtitleTextStyle != 0) {
|
||||
mToolbar.setSubtitleTextAppearance(mToolbar.getContext(), subtitleTextStyle);
|
||||
}
|
||||
|
||||
final int popupTheme = a.getResourceId(R.styleable.ActionBar_popupTheme, 0);
|
||||
if (popupTheme != 0) {
|
||||
mToolbar.setPopupTheme(popupTheme);
|
||||
}
|
||||
|
||||
a.recycle();
|
||||
// Keep the TintManager in case we need it later
|
||||
mTintManager = a.getTintManager();
|
||||
} else {
|
||||
mDisplayOpts = detectDisplayOptions();
|
||||
// Create a TintManager in case we need it later
|
||||
mTintManager = new TintManager(toolbar.getContext());
|
||||
}
|
||||
|
||||
setDefaultNavigationContentDescription(defaultNavigationContentDescription);
|
||||
mHomeDescription = mToolbar.getNavigationContentDescription();
|
||||
|
||||
setDefaultNavigationIcon(mTintManager.getDrawable(defaultNavigationIcon));
|
||||
|
||||
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||
final ActionMenuItem mNavItem = new ActionMenuItem(mToolbar.getContext(),
|
||||
0, android.R.id.home, 0, 0, mTitle);
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mWindowCallback != null && mMenuPrepared) {
|
||||
mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mNavItem);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default content description for the navigation button.
|
||||
* <p>
|
||||
* It changes the current content description if and only if the provided resource id is
|
||||
* different than the current default resource id and the current content description is empty.
|
||||
*
|
||||
* @param defaultNavigationContentDescription The resource id for the default content
|
||||
* description
|
||||
*/
|
||||
@Override
|
||||
public void setDefaultNavigationContentDescription(int defaultNavigationContentDescription) {
|
||||
if (defaultNavigationContentDescription == mDefaultNavigationContentDescription) {
|
||||
return;
|
||||
}
|
||||
mDefaultNavigationContentDescription = defaultNavigationContentDescription;
|
||||
if (TextUtils.isEmpty(mToolbar.getNavigationContentDescription())) {
|
||||
setNavigationContentDescription(mDefaultNavigationContentDescription);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultNavigationIcon(Drawable defaultNavigationIcon) {
|
||||
if (mDefaultNavigationIcon != defaultNavigationIcon) {
|
||||
mDefaultNavigationIcon = defaultNavigationIcon;
|
||||
updateNavigationIcon();
|
||||
}
|
||||
}
|
||||
|
||||
private int detectDisplayOptions() {
|
||||
int opts = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME |
|
||||
ActionBar.DISPLAY_USE_LOGO;
|
||||
if (mToolbar.getNavigationIcon() != null) {
|
||||
opts |= ActionBar.DISPLAY_HOME_AS_UP;
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewGroup getViewGroup() {
|
||||
return mToolbar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return mToolbar.getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSplit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasExpandedActionView() {
|
||||
return mToolbar.hasExpandedActionView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collapseActionView() {
|
||||
mToolbar.collapseActionView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindowCallback(Window.Callback cb) {
|
||||
mWindowCallback = cb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindowTitle(CharSequence title) {
|
||||
// "Real" title always trumps window title.
|
||||
if (!mTitleSet) {
|
||||
setTitleInt(title);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
return mToolbar.getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(CharSequence title) {
|
||||
mTitleSet = true;
|
||||
setTitleInt(title);
|
||||
}
|
||||
|
||||
private void setTitleInt(CharSequence title) {
|
||||
mTitle = title;
|
||||
if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
|
||||
mToolbar.setTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSubtitle() {
|
||||
return mToolbar.getSubtitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubtitle(CharSequence subtitle) {
|
||||
mSubtitle = subtitle;
|
||||
if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
|
||||
mToolbar.setSubtitle(subtitle);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initProgress() {
|
||||
Log.i(TAG, "Progress display unsupported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initIndeterminateProgress() {
|
||||
Log.i(TAG, "Progress display unsupported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSplit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSplitView(ViewGroup splitView) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSplitToolbar(boolean split) {
|
||||
if (split) {
|
||||
throw new UnsupportedOperationException("Cannot split an android.widget.Toolbar");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSplitWhenNarrow(boolean splitWhenNarrow) {
|
||||
// Ignore.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIcon() {
|
||||
return mIcon != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLogo() {
|
||||
return mLogo != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIcon(int resId) {
|
||||
setIcon(resId != 0 ? mTintManager.getDrawable(resId) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIcon(Drawable d) {
|
||||
mIcon = d;
|
||||
updateToolbarLogo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogo(int resId) {
|
||||
setLogo(resId != 0 ? mTintManager.getDrawable(resId) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogo(Drawable d) {
|
||||
mLogo = d;
|
||||
updateToolbarLogo();
|
||||
}
|
||||
|
||||
private void updateToolbarLogo() {
|
||||
Drawable logo = null;
|
||||
if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_HOME) != 0) {
|
||||
if ((mDisplayOpts & ActionBar.DISPLAY_USE_LOGO) != 0) {
|
||||
logo = mLogo != null ? mLogo : mIcon;
|
||||
} else {
|
||||
logo = mIcon;
|
||||
}
|
||||
}
|
||||
mToolbar.setLogo(logo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canShowOverflowMenu() {
|
||||
return mToolbar.canShowOverflowMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverflowMenuShowing() {
|
||||
return mToolbar.isOverflowMenuShowing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverflowMenuShowPending() {
|
||||
return mToolbar.isOverflowMenuShowPending();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showOverflowMenu() {
|
||||
return mToolbar.showOverflowMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hideOverflowMenu() {
|
||||
return mToolbar.hideOverflowMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMenuPrepared() {
|
||||
mMenuPrepared = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMenu(Menu menu, MenuPresenter.Callback cb) {
|
||||
if (mActionMenuPresenter == null) {
|
||||
mActionMenuPresenter = new ActionMenuPresenter(mToolbar.getContext());
|
||||
mActionMenuPresenter.setId(R.id.action_menu_presenter);
|
||||
}
|
||||
mActionMenuPresenter.setCallback(cb);
|
||||
mToolbar.setMenu((MenuBuilder) menu, mActionMenuPresenter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismissPopupMenus() {
|
||||
mToolbar.dismissPopupMenus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisplayOptions() {
|
||||
return mDisplayOpts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayOptions(int newOpts) {
|
||||
final int oldOpts = mDisplayOpts;
|
||||
final int changed = oldOpts ^ newOpts;
|
||||
mDisplayOpts = newOpts;
|
||||
if (changed != 0) {
|
||||
if ((changed & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
|
||||
if ((newOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
|
||||
updateNavigationIcon();
|
||||
updateHomeAccessibility();
|
||||
} else {
|
||||
mToolbar.setNavigationIcon(null);
|
||||
}
|
||||
}
|
||||
|
||||
if ((changed & AFFECTS_LOGO_MASK) != 0) {
|
||||
updateToolbarLogo();
|
||||
}
|
||||
|
||||
if ((changed & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
|
||||
if ((newOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
|
||||
mToolbar.setTitle(mTitle);
|
||||
mToolbar.setSubtitle(mSubtitle);
|
||||
} else {
|
||||
mToolbar.setTitle(null);
|
||||
mToolbar.setSubtitle(null);
|
||||
}
|
||||
}
|
||||
|
||||
if ((changed & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomView != null) {
|
||||
if ((newOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
|
||||
mToolbar.addView(mCustomView);
|
||||
} else {
|
||||
mToolbar.removeView(mCustomView);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmbeddedTabView(ScrollingTabContainerView tabView) {
|
||||
if (mTabView != null && mTabView.getParent() == mToolbar) {
|
||||
mToolbar.removeView(mTabView);
|
||||
}
|
||||
mTabView = tabView;
|
||||
if (tabView != null && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) {
|
||||
mToolbar.addView(mTabView, 0);
|
||||
Toolbar.LayoutParams lp = (Toolbar.LayoutParams) mTabView.getLayoutParams();
|
||||
lp.width = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
lp.gravity = Gravity.START | Gravity.BOTTOM;
|
||||
tabView.setAllowCollapse(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEmbeddedTabs() {
|
||||
return mTabView != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTitleTruncated() {
|
||||
return mToolbar.isTitleTruncated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCollapsible(boolean collapsible) {
|
||||
mToolbar.setCollapsible(collapsible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHomeButtonEnabled(boolean enable) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNavigationMode() {
|
||||
return mNavigationMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationMode(int mode) {
|
||||
final int oldMode = mNavigationMode;
|
||||
if (mode != oldMode) {
|
||||
switch (oldMode) {
|
||||
case ActionBar.NAVIGATION_MODE_LIST:
|
||||
if (mSpinner != null && mSpinner.getParent() == mToolbar) {
|
||||
mToolbar.removeView(mSpinner);
|
||||
}
|
||||
break;
|
||||
case ActionBar.NAVIGATION_MODE_TABS:
|
||||
if (mTabView != null && mTabView.getParent() == mToolbar) {
|
||||
mToolbar.removeView(mTabView);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
mNavigationMode = mode;
|
||||
|
||||
switch (mode) {
|
||||
case ActionBar.NAVIGATION_MODE_STANDARD:
|
||||
break;
|
||||
case ActionBar.NAVIGATION_MODE_LIST:
|
||||
ensureSpinner();
|
||||
mToolbar.addView(mSpinner, 0);
|
||||
break;
|
||||
case ActionBar.NAVIGATION_MODE_TABS:
|
||||
if (mTabView != null) {
|
||||
mToolbar.addView(mTabView, 0);
|
||||
Toolbar.LayoutParams lp = (Toolbar.LayoutParams) mTabView.getLayoutParams();
|
||||
lp.width = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
lp.gravity = Gravity.START | Gravity.BOTTOM;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid navigation mode " + mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureSpinner() {
|
||||
if (mSpinner == null) {
|
||||
mSpinner = new SpinnerCompat(getContext(), null, R.attr.actionDropDownStyle);
|
||||
Toolbar.LayoutParams lp = new Toolbar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL);
|
||||
mSpinner.setLayoutParams(lp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDropdownParams(SpinnerAdapter adapter,
|
||||
AdapterViewCompat.OnItemSelectedListener listener) {
|
||||
ensureSpinner();
|
||||
mSpinner.setAdapter(adapter);
|
||||
mSpinner.setOnItemSelectedListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDropdownSelectedPosition(int position) {
|
||||
if (mSpinner == null) {
|
||||
throw new IllegalStateException(
|
||||
"Can't set dropdown selected position without an adapter");
|
||||
}
|
||||
mSpinner.setSelection(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDropdownSelectedPosition() {
|
||||
return mSpinner != null ? mSpinner.getSelectedItemPosition() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDropdownItemCount() {
|
||||
return mSpinner != null ? mSpinner.getCount() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomView(View view) {
|
||||
if (mCustomView != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
|
||||
mToolbar.removeView(mCustomView);
|
||||
}
|
||||
mCustomView = view;
|
||||
if (view != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
|
||||
mToolbar.addView(mCustomView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getCustomView() {
|
||||
return mCustomView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void animateToVisibility(int visibility) {
|
||||
if (visibility == View.GONE) {
|
||||
ViewCompat.animate(mToolbar).alpha(0)
|
||||
.setListener(new ViewPropertyAnimatorListenerAdapter() {
|
||||
private boolean mCanceled = false;
|
||||
@Override
|
||||
public void onAnimationEnd(View view) {
|
||||
if (!mCanceled) {
|
||||
mToolbar.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(View view) {
|
||||
mCanceled = true;
|
||||
}
|
||||
});
|
||||
} else if (visibility == View.VISIBLE) {
|
||||
ViewCompat.animate(mToolbar).alpha(1)
|
||||
.setListener(new ViewPropertyAnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(View view) {
|
||||
mToolbar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationIcon(Drawable icon) {
|
||||
mNavIcon = icon;
|
||||
updateNavigationIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationIcon(int resId) {
|
||||
setNavigationIcon(resId != 0
|
||||
? mTintManager.getDrawable(resId)
|
||||
: null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationContentDescription(CharSequence description) {
|
||||
mHomeDescription = description;
|
||||
updateHomeAccessibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationContentDescription(int resId) {
|
||||
setNavigationContentDescription(resId == 0 ? null : getContext().getString(resId));
|
||||
}
|
||||
|
||||
private void updateHomeAccessibility() {
|
||||
if ((mDisplayOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
|
||||
if (TextUtils.isEmpty(mHomeDescription)) {
|
||||
mToolbar.setNavigationContentDescription(mDefaultNavigationContentDescription);
|
||||
} else {
|
||||
mToolbar.setNavigationContentDescription(mHomeDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateNavigationIcon() {
|
||||
if ((mDisplayOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
|
||||
mToolbar.setNavigationIcon(mNavIcon != null ? mNavIcon : mDefaultNavigationIcon);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveHierarchyState(SparseArray<Parcelable> toolbarStates) {
|
||||
mToolbar.saveHierarchyState(toolbarStates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreHierarchyState(SparseArray<Parcelable> toolbarStates) {
|
||||
mToolbar.restoreHierarchyState(toolbarStates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackgroundDrawable(Drawable d) {
|
||||
//noinspection deprecation
|
||||
mToolbar.setBackgroundDrawable(d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return mToolbar.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibility(int visible) {
|
||||
mToolbar.setVisibility(visible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVisibility() {
|
||||
return mToolbar.getVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMenuCallbacks(MenuPresenter.Callback actionMenuPresenterCallback,
|
||||
MenuBuilder.Callback menuBuilderCallback) {
|
||||
mToolbar.setMenuCallbacks(actionMenuPresenterCallback, menuBuilderCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Menu getMenu() {
|
||||
return mToolbar.getMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPopupTheme() {
|
||||
return mToolbar.getPopupTheme();
|
||||
}
|
||||
|
||||
}
|
262
android/support/v7/internal/widget/ViewStubCompat.java
Normal file
262
android/support/v7/internal/widget/ViewStubCompat.java
Normal file
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* Backport of {@link android.view.ViewStub} so that we can set the
|
||||
* {@link android.view.LayoutInflater} on devices before Jelly Bean.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class ViewStubCompat extends View {
|
||||
private int mLayoutResource = 0;
|
||||
private int mInflatedId;
|
||||
|
||||
private WeakReference<View> mInflatedViewRef;
|
||||
|
||||
private LayoutInflater mInflater;
|
||||
private OnInflateListener mInflateListener;
|
||||
|
||||
public ViewStubCompat(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ViewStubCompat(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewStubCompat,
|
||||
defStyle, 0);
|
||||
|
||||
mInflatedId = a.getResourceId(R.styleable.ViewStubCompat_android_inflatedId, NO_ID);
|
||||
mLayoutResource = a.getResourceId(R.styleable.ViewStubCompat_android_layout, 0);
|
||||
|
||||
setId(a.getResourceId(R.styleable.ViewStubCompat_android_id, NO_ID));
|
||||
a.recycle();
|
||||
|
||||
setVisibility(GONE);
|
||||
setWillNotDraw(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id taken by the inflated view. If the inflated id is
|
||||
* {@link View#NO_ID}, the inflated view keeps its original id.
|
||||
*
|
||||
* @return A positive integer used to identify the inflated view or
|
||||
* {@link #NO_ID} if the inflated view should keep its id.
|
||||
*
|
||||
* @see #setInflatedId(int)
|
||||
* @attr ref android.R.styleable#ViewStub_inflatedId
|
||||
*/
|
||||
public int getInflatedId() {
|
||||
return mInflatedId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the id taken by the inflated view. If the inflated id is
|
||||
* {@link View#NO_ID}, the inflated view keeps its original id.
|
||||
*
|
||||
* @param inflatedId A positive integer used to identify the inflated view or
|
||||
* {@link #NO_ID} if the inflated view should keep its id.
|
||||
*
|
||||
* @see #getInflatedId()
|
||||
* @attr ref android.R.styleable#ViewStub_inflatedId
|
||||
*/
|
||||
public void setInflatedId(int inflatedId) {
|
||||
mInflatedId = inflatedId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layout resource that will be used by {@link #setVisibility(int)} or
|
||||
* {@link #inflate()} to replace this StubbedView
|
||||
* in its parent by another view.
|
||||
*
|
||||
* @return The layout resource identifier used to inflate the new View.
|
||||
*
|
||||
* @see #setLayoutResource(int)
|
||||
* @see #setVisibility(int)
|
||||
* @see #inflate()
|
||||
* @attr ref android.R.styleable#ViewStub_layout
|
||||
*/
|
||||
public int getLayoutResource() {
|
||||
return mLayoutResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the layout resource to inflate when this StubbedView becomes visible or invisible
|
||||
* or when {@link #inflate()} is invoked. The View created by inflating the layout resource is
|
||||
* used to replace this StubbedView in its parent.
|
||||
*
|
||||
* @param layoutResource A valid layout resource identifier (different from 0.)
|
||||
*
|
||||
* @see #getLayoutResource()
|
||||
* @see #setVisibility(int)
|
||||
* @see #inflate()
|
||||
* @attr ref android.R.styleable#ViewStub_layout
|
||||
*/
|
||||
public void setLayoutResource(int layoutResource) {
|
||||
mLayoutResource = layoutResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@link LayoutInflater} to use in {@link #inflate()}, or {@code null}
|
||||
* to use the default.
|
||||
*/
|
||||
public void setLayoutInflater(LayoutInflater inflater) {
|
||||
mInflater = inflater;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current {@link LayoutInflater} used in {@link #inflate()}.
|
||||
*/
|
||||
public LayoutInflater getLayoutInflater() {
|
||||
return mInflater;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
setMeasuredDimension(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
}
|
||||
|
||||
/**
|
||||
* When visibility is set to {@link #VISIBLE} or {@link #INVISIBLE},
|
||||
* {@link #inflate()} is invoked and this StubbedView is replaced in its parent
|
||||
* by the inflated layout resource. After that calls to this function are passed
|
||||
* through to the inflated view.
|
||||
*
|
||||
* @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
|
||||
*
|
||||
* @see #inflate()
|
||||
*/
|
||||
@Override
|
||||
public void setVisibility(int visibility) {
|
||||
if (mInflatedViewRef != null) {
|
||||
View view = mInflatedViewRef.get();
|
||||
if (view != null) {
|
||||
view.setVisibility(visibility);
|
||||
} else {
|
||||
throw new IllegalStateException("setVisibility called on un-referenced view");
|
||||
}
|
||||
} else {
|
||||
super.setVisibility(visibility);
|
||||
if (visibility == VISIBLE || visibility == INVISIBLE) {
|
||||
inflate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates the layout resource identified by {@link #getLayoutResource()}
|
||||
* and replaces this StubbedView in its parent by the inflated layout resource.
|
||||
*
|
||||
* @return The inflated layout resource.
|
||||
*
|
||||
*/
|
||||
public View inflate() {
|
||||
final ViewParent viewParent = getParent();
|
||||
|
||||
if (viewParent != null && viewParent instanceof ViewGroup) {
|
||||
if (mLayoutResource != 0) {
|
||||
final ViewGroup parent = (ViewGroup) viewParent;
|
||||
final LayoutInflater factory;
|
||||
if (mInflater != null) {
|
||||
factory = mInflater;
|
||||
} else {
|
||||
factory = LayoutInflater.from(getContext());
|
||||
}
|
||||
final View view = factory.inflate(mLayoutResource, parent,
|
||||
false);
|
||||
|
||||
if (mInflatedId != NO_ID) {
|
||||
view.setId(mInflatedId);
|
||||
}
|
||||
|
||||
final int index = parent.indexOfChild(this);
|
||||
parent.removeViewInLayout(this);
|
||||
|
||||
final ViewGroup.LayoutParams layoutParams = getLayoutParams();
|
||||
if (layoutParams != null) {
|
||||
parent.addView(view, index, layoutParams);
|
||||
} else {
|
||||
parent.addView(view, index);
|
||||
}
|
||||
|
||||
mInflatedViewRef = new WeakReference<View>(view);
|
||||
|
||||
if (mInflateListener != null) {
|
||||
mInflateListener.onInflate(this, view);
|
||||
}
|
||||
|
||||
return view;
|
||||
} else {
|
||||
throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the inflate listener to be notified after this ViewStub successfully
|
||||
* inflated its layout resource.
|
||||
*
|
||||
* @param inflateListener The OnInflateListener to notify of successful inflation.
|
||||
*
|
||||
* @see android.view.ViewStub.OnInflateListener
|
||||
*/
|
||||
public void setOnInflateListener(OnInflateListener inflateListener) {
|
||||
mInflateListener = inflateListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener used to receive a notification after a ViewStub has successfully
|
||||
* inflated its layout resource.
|
||||
*
|
||||
* @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener)
|
||||
*/
|
||||
public static interface OnInflateListener {
|
||||
/**
|
||||
* Invoked after a ViewStub successfully inflated its layout resource.
|
||||
* This method is invoked after the inflated view was added to the
|
||||
* hierarchy but before the layout pass.
|
||||
*
|
||||
* @param stub The ViewStub that initiated the inflation.
|
||||
* @param inflated The inflated View.
|
||||
*/
|
||||
void onInflate(ViewStubCompat stub, View inflated);
|
||||
}
|
||||
}
|
140
android/support/v7/internal/widget/ViewUtils.java
Normal file
140
android/support/v7/internal/widget/ViewUtils.java
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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.support.v7.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.internal.view.ContextThemeWrapper;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class ViewUtils {
|
||||
private static final String TAG = "ViewUtils";
|
||||
|
||||
private static Method sComputeFitSystemWindowsMethod;
|
||||
|
||||
static {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
try {
|
||||
sComputeFitSystemWindowsMethod = View.class.getDeclaredMethod(
|
||||
"computeFitSystemWindows", Rect.class, Rect.class);
|
||||
if (!sComputeFitSystemWindowsMethod.isAccessible()) {
|
||||
sComputeFitSystemWindowsMethod.setAccessible(true);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.d(TAG, "Could not find method computeFitSystemWindows. Oh well.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ViewUtils() {}
|
||||
|
||||
public static boolean isLayoutRtl(View view) {
|
||||
return ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two states as returned by {@link ViewCompat#getMeasuredState(android.view.View)} ()}.
|
||||
* @param curState The current state as returned from a view or the result
|
||||
* of combining multiple views.
|
||||
* @param newState The new view state to combine.
|
||||
* @return Returns a new integer reflecting the combination of the two
|
||||
* states.
|
||||
*/
|
||||
public static int combineMeasuredStates(int curState, int newState) {
|
||||
return curState | newState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow calling the hidden method {@code computeFitSystemWindows(Rect, Rect)} through
|
||||
* reflection on {@code view}.
|
||||
*/
|
||||
public static void computeFitSystemWindows(View view, Rect inoutInsets, Rect outLocalInsets) {
|
||||
if (sComputeFitSystemWindowsMethod != null) {
|
||||
try {
|
||||
sComputeFitSystemWindowsMethod.invoke(view, inoutInsets, outLocalInsets);
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Could not invoke computeFitSystemWindows", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow calling the hidden method {@code makeOptionalFitsSystem()} through reflection on
|
||||
* {@code view}.
|
||||
*/
|
||||
public static void makeOptionalFitsSystemWindows(View view) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
try {
|
||||
// We need to use getMethod() for makeOptionalFitsSystemWindows since both View
|
||||
// and ViewGroup implement the method
|
||||
Method method = view.getClass().getMethod("makeOptionalFitsSystemWindows");
|
||||
if (!method.isAccessible()) {
|
||||
method.setAccessible(true);
|
||||
}
|
||||
method.invoke(view);
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.d(TAG, "Could not find method makeOptionalFitsSystemWindows. Oh well...");
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.d(TAG, "Could not invoke makeOptionalFitsSystemWindows", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.d(TAG, "Could not invoke makeOptionalFitsSystemWindows", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows us to emulate the {@code android:theme} attribute for devices before L.
|
||||
*/
|
||||
public static Context themifyContext(Context context, AttributeSet attrs,
|
||||
boolean useAndroidTheme, boolean useAppTheme) {
|
||||
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.View, 0, 0);
|
||||
int themeId = 0;
|
||||
if (useAndroidTheme) {
|
||||
// First try reading android:theme if enabled
|
||||
themeId = a.getResourceId(R.styleable.View_android_theme, 0);
|
||||
}
|
||||
if (useAppTheme && themeId == 0) {
|
||||
// ...if that didn't work, try reading app:theme (for legacy reasons) if enabled
|
||||
themeId = a.getResourceId(R.styleable.View_theme, 0);
|
||||
|
||||
if (themeId != 0) {
|
||||
Log.i(TAG, "app:theme is now deprecated. Please move to using android:theme instead.");
|
||||
}
|
||||
}
|
||||
a.recycle();
|
||||
|
||||
if (themeId != 0 && (!(context instanceof ContextThemeWrapper)
|
||||
|| ((ContextThemeWrapper) context).getThemeResId() != themeId)) {
|
||||
// If the context isn't a ContextThemeWrapperCompat, or it is but does not have
|
||||
// the same theme as we need, wrap it in a new wrapper
|
||||
context = new ContextThemeWrapper(context, themeId);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
}
|
279
android/support/v7/view/ActionMode.java
Normal file
279
android/support/v7/view/ActionMode.java
Normal file
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* Copyright (C) 2010 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.support.v7.view;
|
||||
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Represents a contextual mode of the user interface. Action modes can be used to provide
|
||||
* alternative interaction modes and replace parts of the normal UI until finished.
|
||||
* Examples of good action modes include text selection and contextual actions.
|
||||
* <div class="special reference">
|
||||
*
|
||||
* <h3>Developer Guides</h3>
|
||||
* <p>For information about how to provide contextual actions with {@code ActionMode},
|
||||
* read the <a href="{@docRoot}guide/topics/ui/menus.html#context-menu">Menus</a>
|
||||
* developer guide.</p>
|
||||
*
|
||||
* </div>
|
||||
*/
|
||||
public abstract class ActionMode {
|
||||
|
||||
private Object mTag;
|
||||
private boolean mTitleOptionalHint;
|
||||
|
||||
/**
|
||||
* Set a tag object associated with this ActionMode.
|
||||
*
|
||||
* <p>Like the tag available to views, this allows applications to associate arbitrary
|
||||
* data with an ActionMode for later reference.
|
||||
*
|
||||
* @param tag Tag to associate with this ActionMode
|
||||
*
|
||||
* @see #getTag()
|
||||
*/
|
||||
public void setTag(Object tag) {
|
||||
mTag = tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the tag object associated with this ActionMode.
|
||||
*
|
||||
* <p>Like the tag available to views, this allows applications to associate arbitrary
|
||||
* data with an ActionMode for later reference.
|
||||
*
|
||||
* @return Tag associated with this ActionMode
|
||||
*
|
||||
* @see #setTag(Object)
|
||||
*/
|
||||
public Object getTag() {
|
||||
return mTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the title of the action mode. This method will have no visible effect if
|
||||
* a custom view has been set.
|
||||
*
|
||||
* @param title Title string to set
|
||||
*
|
||||
* @see #setTitle(int)
|
||||
* @see #setCustomView(View)
|
||||
*/
|
||||
public abstract void setTitle(CharSequence title);
|
||||
|
||||
/**
|
||||
* Set the title of the action mode. This method will have no visible effect if
|
||||
* a custom view has been set.
|
||||
*
|
||||
* @param resId Resource ID of a string to set as the title
|
||||
*
|
||||
* @see #setTitle(CharSequence)
|
||||
* @see #setCustomView(View)
|
||||
*/
|
||||
public abstract void setTitle(int resId);
|
||||
|
||||
/**
|
||||
* Set the subtitle of the action mode. This method will have no visible effect if
|
||||
* a custom view has been set.
|
||||
*
|
||||
* @param subtitle Subtitle string to set
|
||||
*
|
||||
* @see #setSubtitle(int)
|
||||
* @see #setCustomView(View)
|
||||
*/
|
||||
public abstract void setSubtitle(CharSequence subtitle);
|
||||
|
||||
/**
|
||||
* Set the subtitle of the action mode. This method will have no visible effect if
|
||||
* a custom view has been set.
|
||||
*
|
||||
* @param resId Resource ID of a string to set as the subtitle
|
||||
*
|
||||
* @see #setSubtitle(CharSequence)
|
||||
* @see #setCustomView(View)
|
||||
*/
|
||||
public abstract void setSubtitle(int resId);
|
||||
|
||||
/**
|
||||
* Set whether or not the title/subtitle display for this action mode
|
||||
* is optional.
|
||||
*
|
||||
* <p>In many cases the supplied title for an action mode is merely
|
||||
* meant to add context and is not strictly required for the action
|
||||
* mode to be useful. If the title is optional, the system may choose
|
||||
* to hide the title entirely rather than truncate it due to a lack
|
||||
* of available space.</p>
|
||||
*
|
||||
* <p>Note that this is merely a hint; the underlying implementation
|
||||
* may choose to ignore this setting under some circumstances.</p>
|
||||
*
|
||||
* @param titleOptional true if the title only presents optional information.
|
||||
*/
|
||||
public void setTitleOptionalHint(boolean titleOptional) {
|
||||
mTitleOptionalHint = titleOptional;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this action mode has been given a hint to consider the
|
||||
* title/subtitle display to be optional.
|
||||
*
|
||||
* @see #setTitleOptionalHint(boolean)
|
||||
* @see #isTitleOptional()
|
||||
*/
|
||||
public boolean getTitleOptionalHint() {
|
||||
return mTitleOptionalHint;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this action mode considers the title and subtitle fields
|
||||
* as optional. Optional titles may not be displayed to the user.
|
||||
*/
|
||||
public boolean isTitleOptional() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom view for this action mode. The custom view will take the place of
|
||||
* the title and subtitle. Useful for things like search boxes.
|
||||
*
|
||||
* @param view Custom view to use in place of the title/subtitle.
|
||||
*
|
||||
* @see #setTitle(CharSequence)
|
||||
* @see #setSubtitle(CharSequence)
|
||||
*/
|
||||
public abstract void setCustomView(View view);
|
||||
|
||||
/**
|
||||
* Invalidate the action mode and refresh menu content. The mode's
|
||||
* {@link ActionMode.Callback} will have its
|
||||
* {@link Callback#onPrepareActionMode(ActionMode, Menu)} method called.
|
||||
* If it returns true the menu will be scanned for updated content and any relevant changes
|
||||
* will be reflected to the user.
|
||||
*/
|
||||
public abstract void invalidate();
|
||||
|
||||
/**
|
||||
* Finish and close this action mode. The action mode's {@link ActionMode.Callback} will
|
||||
* have its {@link Callback#onDestroyActionMode(ActionMode)} method called.
|
||||
*/
|
||||
public abstract void finish();
|
||||
|
||||
/**
|
||||
* Returns the menu of actions that this action mode presents.
|
||||
*
|
||||
* @return The action mode's menu.
|
||||
*/
|
||||
public abstract Menu getMenu();
|
||||
|
||||
/**
|
||||
* Returns the current title of this action mode.
|
||||
*
|
||||
* @return Title text
|
||||
*/
|
||||
public abstract CharSequence getTitle();
|
||||
|
||||
/**
|
||||
* Returns the current subtitle of this action mode.
|
||||
*
|
||||
* @return Subtitle text
|
||||
*/
|
||||
public abstract CharSequence getSubtitle();
|
||||
|
||||
/**
|
||||
* Returns the current custom view for this action mode.
|
||||
*
|
||||
* @return The current custom view
|
||||
*/
|
||||
public abstract View getCustomView();
|
||||
|
||||
/**
|
||||
* Returns a {@link MenuInflater} with the ActionMode's context.
|
||||
*/
|
||||
public abstract MenuInflater getMenuInflater();
|
||||
|
||||
/**
|
||||
* Returns whether the UI presenting this action mode can take focus or not.
|
||||
* This is used by internal components within the framework that would otherwise
|
||||
* present an action mode UI that requires focus, such as an EditText as a custom view.
|
||||
*
|
||||
* @return true if the UI used to show this action mode can take focus
|
||||
* @hide Internal use only
|
||||
*/
|
||||
public boolean isUiFocusable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback interface for action modes. Supplied to
|
||||
* {@link android.support.v7.app.AppCompatDelegate#startSupportActionMode(Callback)} (Callback)},
|
||||
* a Callback configures and handles events raised by a user's interaction with an action mode.
|
||||
*
|
||||
* <p>An action mode's lifecycle is as follows:
|
||||
* <ul>
|
||||
* <li>{@link Callback#onCreateActionMode(ActionMode, Menu)} once on initial
|
||||
* creation</li>
|
||||
* <li>{@link Callback#onPrepareActionMode(ActionMode, Menu)} after creation
|
||||
* and any time the {@link ActionMode} is invalidated</li>
|
||||
* <li>{@link Callback#onActionItemClicked(ActionMode, MenuItem)} any time a
|
||||
* contextual action button is clicked</li>
|
||||
* <li>{@link Callback#onDestroyActionMode(ActionMode)} when the action mode
|
||||
* is closed</li>
|
||||
* </ul>
|
||||
*/
|
||||
public interface Callback {
|
||||
|
||||
/**
|
||||
* Called when action mode is first created. The menu supplied will be used to
|
||||
* generate action buttons for the action mode.
|
||||
*
|
||||
* @param mode ActionMode being created
|
||||
* @param menu Menu used to populate action buttons
|
||||
* @return true if the action mode should be created, false if entering this
|
||||
* mode should be aborted.
|
||||
*/
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu);
|
||||
|
||||
/**
|
||||
* Called to refresh an action mode's action menu whenever it is invalidated.
|
||||
*
|
||||
* @param mode ActionMode being prepared
|
||||
* @param menu Menu used to populate action buttons
|
||||
* @return true if the menu or action mode was updated, false otherwise.
|
||||
*/
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu);
|
||||
|
||||
/**
|
||||
* Called to report a user click on an action button.
|
||||
*
|
||||
* @param mode The current ActionMode
|
||||
* @param item The item that was clicked
|
||||
* @return true if this callback handled the event, false if the standard MenuItem
|
||||
* invocation should continue.
|
||||
*/
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item);
|
||||
|
||||
/**
|
||||
* Called when an action mode is about to be exited and destroyed.
|
||||
*
|
||||
* @param mode The current ActionMode being destroyed
|
||||
*/
|
||||
public void onDestroyActionMode(ActionMode mode);
|
||||
}
|
||||
}
|
42
android/support/v7/view/CollapsibleActionView.java
Normal file
42
android/support/v7/view/CollapsibleActionView.java
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2011 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.support.v7.view;
|
||||
|
||||
import android.support.v4.view.MenuItemCompat.OnActionExpandListener;
|
||||
|
||||
/**
|
||||
* When a {@link android.view.View} implements this interface it will receive callbacks when expanded or
|
||||
* collapsed as an action view alongside the optional, app-specified callbacks to {@link
|
||||
* OnActionExpandListener}.
|
||||
*
|
||||
* <p>See {@link android.support.v4.view.MenuItemCompat} for more information about action views.
|
||||
* See {@link android.app.ActionBar} for more information about the action bar.
|
||||
*/
|
||||
public interface CollapsibleActionView {
|
||||
|
||||
/**
|
||||
* Called when this view is expanded as an action view. See {@link
|
||||
* android.support.v4.view.MenuItemCompat#expandActionView(android.view.MenuItem)}.
|
||||
*/
|
||||
public void onActionViewExpanded();
|
||||
|
||||
/**
|
||||
* Called when this view is collapsed as an action view. See {@link
|
||||
* android.support.v4.view.MenuItemCompat#collapseActionView(android.view.MenuItem)}.
|
||||
*/
|
||||
public void onActionViewCollapsed();
|
||||
}
|
769
android/support/v7/widget/ActionMenuPresenter.java
Normal file
769
android/support/v7/widget/ActionMenuPresenter.java
Normal file
|
@ -0,0 +1,769 @@
|
|||
/*
|
||||
* 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.support.v7.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.support.v4.view.ActionProvider;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v7.appcompat.R;
|
||||
import android.support.v7.internal.transition.ActionBarTransition;
|
||||
import android.support.v7.internal.view.ActionBarPolicy;
|
||||
import android.support.v7.internal.view.menu.ActionMenuItemView;
|
||||
import android.support.v7.internal.view.menu.BaseMenuPresenter;
|
||||
import android.support.v7.internal.view.menu.MenuBuilder;
|
||||
import android.support.v7.internal.view.menu.MenuItemImpl;
|
||||
import android.support.v7.internal.view.menu.MenuPopupHelper;
|
||||
import android.support.v7.internal.view.menu.MenuView;
|
||||
import android.support.v7.internal.view.menu.SubMenuBuilder;
|
||||
import android.support.v7.internal.widget.TintImageView;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SoundEffectConstants;
|
||||
import android.view.View;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* MenuPresenter for building action menus as seen in the action bar and action modes.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ActionMenuPresenter extends BaseMenuPresenter
|
||||
implements ActionProvider.SubUiVisibilityListener {
|
||||
|
||||
private static final String TAG = "ActionMenuPresenter";
|
||||
|
||||
private View mOverflowButton;
|
||||
private boolean mReserveOverflow;
|
||||
private boolean mReserveOverflowSet;
|
||||
private int mWidthLimit;
|
||||
private int mActionItemWidthLimit;
|
||||
private int mMaxItems;
|
||||
private boolean mMaxItemsSet;
|
||||
private boolean mStrictWidthLimit;
|
||||
private boolean mWidthLimitSet;
|
||||
private boolean mExpandedActionViewsExclusive;
|
||||
|
||||
private int mMinCellSize;
|
||||
|
||||
// Group IDs that have been added as actions - used temporarily, allocated here for reuse.
|
||||
private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray();
|
||||
|
||||
private View mScrapActionButtonView;
|
||||
|
||||
private OverflowPopup mOverflowPopup;
|
||||
private ActionButtonSubmenu mActionButtonPopup;
|
||||
|
||||
private OpenOverflowRunnable mPostedOpenRunnable;
|
||||
private ActionMenuPopupCallback mPopupCallback;
|
||||
|
||||
final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
|
||||
int mOpenSubMenuId;
|
||||
|
||||
public ActionMenuPresenter(Context context) {
|
||||
super(context, R.layout.abc_action_menu_layout, R.layout.abc_action_menu_item_layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initForMenu(Context context, MenuBuilder menu) {
|
||||
super.initForMenu(context, menu);
|
||||
|
||||
final Resources res = context.getResources();
|
||||
|
||||
final ActionBarPolicy abp = ActionBarPolicy.get(context);
|
||||
if (!mReserveOverflowSet) {
|
||||
mReserveOverflow = abp.showsOverflowMenuButton();
|
||||
}
|
||||
|
||||
if (!mWidthLimitSet) {
|
||||
mWidthLimit = abp.getEmbeddedMenuWidthLimit();
|
||||
}
|
||||
|
||||
// Measure for initial configuration
|
||||
if (!mMaxItemsSet) {
|
||||
mMaxItems = abp.getMaxActionButtons();
|
||||
}
|
||||
|
||||
int width = mWidthLimit;
|
||||
if (mReserveOverflow) {
|
||||
if (mOverflowButton == null) {
|
||||
mOverflowButton = new OverflowMenuButton(mSystemContext);
|
||||
final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
mOverflowButton.measure(spec, spec);
|
||||
}
|
||||
width -= mOverflowButton.getMeasuredWidth();
|
||||
} else {
|
||||
mOverflowButton = null;
|
||||
}
|
||||
|
||||
mActionItemWidthLimit = width;
|
||||
|
||||
mMinCellSize = (int) (ActionMenuView.MIN_CELL_SIZE * res.getDisplayMetrics().density);
|
||||
|
||||
// Drop a scrap view as it may no longer reflect the proper context/config.
|
||||
mScrapActionButtonView = null;
|
||||
}
|
||||
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
if (!mMaxItemsSet) {
|
||||
mMaxItems = mContext.getResources().getInteger(
|
||||
R.integer.abc_max_action_buttons);
|
||||
}
|
||||
if (mMenu != null) {
|
||||
mMenu.onItemsChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void setWidthLimit(int width, boolean strict) {
|
||||
mWidthLimit = width;
|
||||
mStrictWidthLimit = strict;
|
||||
mWidthLimitSet = true;
|
||||
}
|
||||
|
||||
public void setReserveOverflow(boolean reserveOverflow) {
|
||||
mReserveOverflow = reserveOverflow;
|
||||
mReserveOverflowSet = true;
|
||||
}
|
||||
|
||||
public void setItemLimit(int itemCount) {
|
||||
mMaxItems = itemCount;
|
||||
mMaxItemsSet = true;
|
||||
}
|
||||
|
||||
public void setExpandedActionViewsExclusive(boolean isExclusive) {
|
||||
mExpandedActionViewsExclusive = isExclusive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuView getMenuView(ViewGroup root) {
|
||||
MenuView result = super.getMenuView(root);
|
||||
((ActionMenuView) result).setPresenter(this);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getItemView(final MenuItemImpl item, View convertView, ViewGroup parent) {
|
||||
View actionView = item.getActionView();
|
||||
if (actionView == null || item.hasCollapsibleActionView()) {
|
||||
actionView = super.getItemView(item, convertView, parent);
|
||||
}
|
||||
actionView.setVisibility(item.isActionViewExpanded() ? View.GONE : View.VISIBLE);
|
||||
|
||||
final ActionMenuView menuParent = (ActionMenuView) parent;
|
||||
final ViewGroup.LayoutParams lp = actionView.getLayoutParams();
|
||||
if (!menuParent.checkLayoutParams(lp)) {
|
||||
actionView.setLayoutParams(menuParent.generateLayoutParams(lp));
|
||||
}
|
||||
return actionView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindItemView(MenuItemImpl item, MenuView.ItemView itemView) {
|
||||
itemView.initialize(item, 0);
|
||||
|
||||
final ActionMenuView menuView = (ActionMenuView) mMenuView;
|
||||
final ActionMenuItemView actionItemView = (ActionMenuItemView) itemView;
|
||||
actionItemView.setItemInvoker(menuView);
|
||||
|
||||
if (mPopupCallback == null) {
|
||||
mPopupCallback = new ActionMenuPopupCallback();
|
||||
}
|
||||
actionItemView.setPopupCallback(mPopupCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
|
||||
return item.isActionButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMenuView(boolean cleared) {
|
||||
final ViewGroup menuViewParent = (ViewGroup) ((View) mMenuView).getParent();
|
||||
if (menuViewParent != null) {
|
||||
ActionBarTransition.beginDelayedTransition(menuViewParent);
|
||||
}
|
||||
super.updateMenuView(cleared);
|
||||
|
||||
((View) mMenuView).requestLayout();
|
||||
|
||||
if (mMenu != null) {
|
||||
final ArrayList<MenuItemImpl> actionItems = mMenu.getActionItems();
|
||||
final int count = actionItems.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final ActionProvider provider = actionItems.get(i).getSupportActionProvider();
|
||||
if (provider != null) {
|
||||
provider.setSubUiVisibilityListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final ArrayList<MenuItemImpl> nonActionItems = mMenu != null ?
|
||||
mMenu.getNonActionItems() : null;
|
||||
|
||||
boolean hasOverflow = false;
|
||||
if (mReserveOverflow && nonActionItems != null) {
|
||||
final int count = nonActionItems.size();
|
||||
if (count == 1) {
|
||||
hasOverflow = !nonActionItems.get(0).isActionViewExpanded();
|
||||
} else {
|
||||
hasOverflow = count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasOverflow) {
|
||||
if (mOverflowButton == null) {
|
||||
mOverflowButton = new OverflowMenuButton(mSystemContext);
|
||||
}
|
||||
ViewGroup parent = (ViewGroup) mOverflowButton.getParent();
|
||||
if (parent != mMenuView) {
|
||||
if (parent != null) {
|
||||
parent.removeView(mOverflowButton);
|
||||
}
|
||||
ActionMenuView menuView = (ActionMenuView) mMenuView;
|
||||
menuView.addView(mOverflowButton, menuView.generateOverflowButtonLayoutParams());
|
||||
}
|
||||
} else if (mOverflowButton != null && mOverflowButton.getParent() == mMenuView) {
|
||||
((ViewGroup) mMenuView).removeView(mOverflowButton);
|
||||
}
|
||||
|
||||
((ActionMenuView) mMenuView).setOverflowReserved(mReserveOverflow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filterLeftoverView(ViewGroup parent, int childIndex) {
|
||||
if (parent.getChildAt(childIndex) == mOverflowButton) return false;
|
||||
return super.filterLeftoverView(parent, childIndex);
|
||||
}
|
||||
|
||||
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
|
||||
if (!subMenu.hasVisibleItems()) return false;
|
||||
|
||||
SubMenuBuilder topSubMenu = subMenu;
|
||||
while (topSubMenu.getParentMenu() != mMenu) {
|
||||
topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu();
|
||||
}
|
||||
View anchor = findViewForItem(topSubMenu.getItem());
|
||||
if (anchor == null) {
|
||||
if (mOverflowButton == null) return false;
|
||||
anchor = mOverflowButton;
|
||||
}
|
||||
|
||||
mOpenSubMenuId = subMenu.getItem().getItemId();
|
||||
mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu);
|
||||
mActionButtonPopup.setAnchorView(anchor);
|
||||
mActionButtonPopup.show();
|
||||
super.onSubMenuSelected(subMenu);
|
||||
return true;
|
||||
}
|
||||
|
||||
private View findViewForItem(MenuItem item) {
|
||||
final ViewGroup parent = (ViewGroup) mMenuView;
|
||||
if (parent == null) return null;
|
||||
|
||||
final int count = parent.getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final View child = parent.getChildAt(i);
|
||||
if (child instanceof MenuView.ItemView &&
|
||||
((MenuView.ItemView) child).getItemData() == item) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the overflow menu if one is present.
|
||||
* @return true if the overflow menu was shown, false otherwise.
|
||||
*/
|
||||
public boolean showOverflowMenu() {
|
||||
if (mReserveOverflow && !isOverflowMenuShowing() && mMenu != null && mMenuView != null &&
|
||||
mPostedOpenRunnable == null && !mMenu.getNonActionItems().isEmpty()) {
|
||||
OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true);
|
||||
mPostedOpenRunnable = new OpenOverflowRunnable(popup);
|
||||
// Post this for later; we might still need a layout for the anchor to be right.
|
||||
((View) mMenuView).post(mPostedOpenRunnable);
|
||||
|
||||
// ActionMenuPresenter uses null as a callback argument here
|
||||
// to indicate overflow is opening.
|
||||
super.onSubMenuSelected(null);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the overflow menu if it is currently showing.
|
||||
*
|
||||
* @return true if the overflow menu was hidden, false otherwise.
|
||||
*/
|
||||
public boolean hideOverflowMenu() {
|
||||
if (mPostedOpenRunnable != null && mMenuView != null) {
|
||||
((View) mMenuView).removeCallbacks(mPostedOpenRunnable);
|
||||
mPostedOpenRunnable = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
MenuPopupHelper popup = mOverflowPopup;
|
||||
if (popup != null) {
|
||||
popup.dismiss();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss all popup menus - overflow and submenus.
|
||||
* @return true if popups were dismissed, false otherwise. (This can be because none were open.)
|
||||
*/
|
||||
public boolean dismissPopupMenus() {
|
||||
boolean result = hideOverflowMenu();
|
||||
result |= hideSubMenus();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss all submenu popups.
|
||||
*
|
||||
* @return true if popups were dismissed, false otherwise. (This can be because none were open.)
|
||||
*/
|
||||
public boolean hideSubMenus() {
|
||||
if (mActionButtonPopup != null) {
|
||||
mActionButtonPopup.dismiss();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the overflow menu is currently showing
|
||||
*/
|
||||
public boolean isOverflowMenuShowing() {
|
||||
return mOverflowPopup != null && mOverflowPopup.isShowing();
|
||||
}
|
||||
|
||||
public boolean isOverflowMenuShowPending() {
|
||||
return mPostedOpenRunnable != null || isOverflowMenuShowing();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if space has been reserved in the action menu for an overflow item.
|
||||
*/
|
||||
public boolean isOverflowReserved() {
|
||||
return mReserveOverflow;
|
||||
}
|
||||
|
||||
public boolean flagActionItems() {
|
||||
final ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
|
||||
final int itemsSize = visibleItems.size();
|
||||
int maxActions = mMaxItems;
|
||||
int widthLimit = mActionItemWidthLimit;
|
||||
final int querySpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
final ViewGroup parent = (ViewGroup) mMenuView;
|
||||
|
||||
int requiredItems = 0;
|
||||
int requestedItems = 0;
|
||||
int firstActionWidth = 0;
|
||||
boolean hasOverflow = false;
|
||||
for (int i = 0; i < itemsSize; i++) {
|
||||
MenuItemImpl item = visibleItems.get(i);
|
||||
if (item.requiresActionButton()) {
|
||||
requiredItems++;
|
||||
} else if (item.requestsActionButton()) {
|
||||
requestedItems++;
|
||||
} else {
|
||||
hasOverflow = true;
|
||||
}
|
||||
if (mExpandedActionViewsExclusive && item.isActionViewExpanded()) {
|
||||
// Overflow everything if we have an expanded action view and we're
|
||||
// space constrained.
|
||||
maxActions = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Reserve a spot for the overflow item if needed.
|
||||
if (mReserveOverflow &&
|
||||
(hasOverflow || requiredItems + requestedItems > maxActions)) {
|
||||
maxActions--;
|
||||
}
|
||||
maxActions -= requiredItems;
|
||||
|
||||
final SparseBooleanArray seenGroups = mActionButtonGroups;
|
||||
seenGroups.clear();
|
||||
|
||||
int cellSize = 0;
|
||||
int cellsRemaining = 0;
|
||||
if (mStrictWidthLimit) {
|
||||
cellsRemaining = widthLimit / mMinCellSize;
|
||||
final int cellSizeRemaining = widthLimit % mMinCellSize;
|
||||
cellSize = mMinCellSize + cellSizeRemaining / cellsRemaining;
|
||||
}
|
||||
|
||||
// Flag as many more requested items as will fit.
|
||||
for (int i = 0; i < itemsSize; i++) {
|
||||
MenuItemImpl item = visibleItems.get(i);
|
||||
|
||||
if (item.requiresActionButton()) {
|
||||
View v = getItemView(item, mScrapActionButtonView, parent);
|
||||
if (mScrapActionButtonView == null) {
|
||||
mScrapActionButtonView = v;
|
||||
}
|
||||
if (mStrictWidthLimit) {
|
||||
cellsRemaining -= ActionMenuView.measureChildForCells(v,
|
||||
cellSize, cellsRemaining, querySpec, 0);
|
||||
} else {
|
||||
v.measure(querySpec, querySpec);
|
||||
}
|
||||
final int measuredWidth = v.getMeasuredWidth();
|
||||
widthLimit -= measuredWidth;
|
||||
if (firstActionWidth == 0) {
|
||||
firstActionWidth = measuredWidth;
|
||||
}
|
||||
final int groupId = item.getGroupId();
|
||||
if (groupId != 0) {
|
||||
seenGroups.put(groupId, true);
|
||||
}
|
||||
item.setIsActionButton(true);
|
||||
} else if (item.requestsActionButton()) {
|
||||
// Items in a group with other items that already have an action slot
|
||||
// can break the max actions rule, but not the width limit.
|
||||
final int groupId = item.getGroupId();
|
||||
final boolean inGroup = seenGroups.get(groupId);
|
||||
boolean isAction = (maxActions > 0 || inGroup) && widthLimit > 0 &&
|
||||
(!mStrictWidthLimit || cellsRemaining > 0);
|
||||
|
||||
if (isAction) {
|
||||
View v = getItemView(item, mScrapActionButtonView, parent);
|
||||
if (mScrapActionButtonView == null) {
|
||||
mScrapActionButtonView = v;
|
||||
}
|
||||
if (mStrictWidthLimit) {
|
||||
final int cells = ActionMenuView.measureChildForCells(v,
|
||||
cellSize, cellsRemaining, querySpec, 0);
|
||||
cellsRemaining -= cells;
|
||||
if (cells == 0) {
|
||||
isAction = false;
|
||||
}
|
||||
} else {
|
||||
v.measure(querySpec, querySpec);
|
||||
}
|
||||
final int measuredWidth = v.getMeasuredWidth();
|
||||
widthLimit -= measuredWidth;
|
||||
if (firstActionWidth == 0) {
|
||||
firstActionWidth = measuredWidth;
|
||||
}
|
||||
|
||||
if (mStrictWidthLimit) {
|
||||
isAction &= widthLimit >= 0;
|
||||
} else {
|
||||
// Did this push the entire first item past the limit?
|
||||
isAction &= widthLimit + firstActionWidth > 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (isAction && groupId != 0) {
|
||||
seenGroups.put(groupId, true);
|
||||
} else if (inGroup) {
|
||||
// We broke the width limit. Demote the whole group, they all overflow now.
|
||||
seenGroups.put(groupId, false);
|
||||
for (int j = 0; j < i; j++) {
|
||||
MenuItemImpl areYouMyGroupie = visibleItems.get(j);
|
||||
if (areYouMyGroupie.getGroupId() == groupId) {
|
||||
// Give back the action slot
|
||||
if (areYouMyGroupie.isActionButton()) maxActions++;
|
||||
areYouMyGroupie.setIsActionButton(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isAction) maxActions--;
|
||||
|
||||
item.setIsActionButton(isAction);
|
||||
} else {
|
||||
// Neither requires nor requests an action button.
|
||||
item.setIsActionButton(false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
||||
dismissPopupMenus();
|
||||
super.onCloseMenu(menu, allMenusAreClosing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
SavedState state = new SavedState();
|
||||
state.openSubMenuId = mOpenSubMenuId;
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
SavedState saved = (SavedState) state;
|
||||
if (saved.openSubMenuId > 0) {
|
||||
MenuItem item = mMenu.findItem(saved.openSubMenuId);
|
||||
if (item != null) {
|
||||
SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
|
||||
onSubMenuSelected(subMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubUiVisibilityChanged(boolean isVisible) {
|
||||
if (isVisible) {
|
||||
// Not a submenu, but treat it like one.
|
||||
super.onSubMenuSelected(null);
|
||||
} else {
|
||||
mMenu.close(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void setMenuView(ActionMenuView menuView) {
|
||||
mMenuView = menuView;
|
||||
menuView.initialize(mMenu);
|
||||
}
|
||||
|
||||
private static class SavedState implements Parcelable {
|
||||
public int openSubMenuId;
|
||||
|
||||
SavedState() {
|
||||
}
|
||||
|
||||
SavedState(Parcel in) {
|
||||
openSubMenuId = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(openSubMenuId);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SavedState> CREATOR
|
||||
= new Parcelable.Creator<SavedState>() {
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class OverflowMenuButton extends TintImageView implements ActionMenuView.ActionMenuChildView {
|
||||
private final float[] mTempPts = new float[2];
|
||||
|
||||
public OverflowMenuButton(Context context) {
|
||||
super(context, null, R.attr.actionOverflowButtonStyle);
|
||||
|
||||
setClickable(true);
|
||||
setFocusable(true);
|
||||
setVisibility(VISIBLE);
|
||||
setEnabled(true);
|
||||
|
||||
setOnTouchListener(new ListPopupWindow.ForwardingListener(this) {
|
||||
@Override
|
||||
public ListPopupWindow getPopup() {
|
||||
if (mOverflowPopup == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mOverflowPopup.getPopup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onForwardingStarted() {
|
||||
showOverflowMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onForwardingStopped() {
|
||||
// Displaying the popup occurs asynchronously, so wait for
|
||||
// the runnable to finish before deciding whether to stop
|
||||
// forwarding.
|
||||
if (mPostedOpenRunnable != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hideOverflowMenu();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performClick() {
|
||||
if (super.performClick()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
playSoundEffect(SoundEffectConstants.CLICK);
|
||||
showOverflowMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsDividerBefore() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsDividerAfter() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean setFrame(int l, int t, int r, int b) {
|
||||
final boolean changed = super.setFrame(l, t, r, b);
|
||||
|
||||
// Set up the hotspot bounds to be centered on the image.
|
||||
final Drawable d = getDrawable();
|
||||
final Drawable bg = getBackground();
|
||||
if (d != null && bg != null) {
|
||||
final int width = getWidth();
|
||||
final int height = getHeight();
|
||||
final int halfEdge = Math.max(width, height) / 2;
|
||||
final int offsetX = getPaddingLeft() - getPaddingRight();
|
||||
final int offsetY = getPaddingTop() - getPaddingBottom();
|
||||
final int centerX = (width + offsetX) / 2;
|
||||
final int centerY = (height + offsetY) / 2;
|
||||
DrawableCompat.setHotspotBounds(bg, centerX - halfEdge, centerY - halfEdge,
|
||||
centerX + halfEdge, centerY + halfEdge);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
|
||||
private class OverflowPopup extends MenuPopupHelper {
|
||||
|
||||
public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
|
||||
boolean overflowOnly) {
|
||||
super(context, menu, anchorView, overflowOnly, R.attr.actionOverflowMenuStyle);
|
||||
setGravity(GravityCompat.END);
|
||||
setCallback(mPopupPresenterCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss() {
|
||||
super.onDismiss();
|
||||
mMenu.close();
|
||||
mOverflowPopup = null;
|
||||
}
|
||||
}
|
||||
|
||||
private class ActionButtonSubmenu extends MenuPopupHelper {
|
||||
private SubMenuBuilder mSubMenu;
|
||||
|
||||
public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
|
||||
super(context, subMenu, null, false,
|
||||
R.attr.actionOverflowMenuStyle);
|
||||
mSubMenu = subMenu;
|
||||
|
||||
MenuItemImpl item = (MenuItemImpl) subMenu.getItem();
|
||||
if (!item.isActionButton()) {
|
||||
// Give a reasonable anchor to nested submenus.
|
||||
setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton);
|
||||
}
|
||||
|
||||
setCallback(mPopupPresenterCallback);
|
||||
|
||||
boolean preserveIconSpacing = false;
|
||||
final int count = subMenu.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
MenuItem childItem = subMenu.getItem(i);
|
||||
if (childItem.isVisible() && childItem.getIcon() != null) {
|
||||
preserveIconSpacing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
setForceShowIcon(preserveIconSpacing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss() {
|
||||
super.onDismiss();
|
||||
mActionButtonPopup = null;
|
||||
mOpenSubMenuId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private class PopupPresenterCallback implements Callback {
|
||||
|
||||
@Override
|
||||
public boolean onOpenSubMenu(MenuBuilder subMenu) {
|
||||
if (subMenu == null) return false;
|
||||
|
||||
mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId();
|
||||
final Callback cb = getCallback();
|
||||
return cb != null ? cb.onOpenSubMenu(subMenu) : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
||||
if (menu instanceof SubMenuBuilder) {
|
||||
((SubMenuBuilder) menu).getRootMenu().close(false);
|
||||
}
|
||||
final Callback cb = getCallback();
|
||||
if (cb != null) {
|
||||
cb.onCloseMenu(menu, allMenusAreClosing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class OpenOverflowRunnable implements Runnable {
|
||||
private OverflowPopup mPopup;
|
||||
|
||||
public OpenOverflowRunnable(OverflowPopup popup) {
|
||||
mPopup = popup;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
mMenu.changeMenuMode();
|
||||
final View menuView = (View) mMenuView;
|
||||
if (menuView != null && menuView.getWindowToken() != null && mPopup.tryShow()) {
|
||||
mOverflowPopup = mPopup;
|
||||
}
|
||||
mPostedOpenRunnable = null;
|
||||
}
|
||||
}
|
||||
|
||||
private class ActionMenuPopupCallback extends ActionMenuItemView.PopupCallback {
|
||||
@Override
|
||||
public ListPopupWindow getPopup() {
|
||||
return mActionButtonPopup != null ? mActionButtonPopup.getPopup() : null;
|
||||
}
|
||||
}
|
||||
}
|
811
android/support/v7/widget/ActionMenuView.java
Normal file
811
android/support/v7/widget/ActionMenuView.java
Normal file
|
@ -0,0 +1,811 @@
|
|||
/*
|
||||
* 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.support.v7.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.support.v7.internal.view.menu.ActionMenuItemView;
|
||||
import android.support.v7.internal.view.menu.MenuBuilder;
|
||||
import android.support.v7.internal.view.menu.MenuItemImpl;
|
||||
import android.support.v7.internal.view.menu.MenuPresenter;
|
||||
import android.support.v7.internal.view.menu.MenuView;
|
||||
import android.support.v7.internal.widget.ViewUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.Gravity;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewDebug;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
/**
|
||||
* ActionMenuView is a presentation of a series of menu options as a View. It provides
|
||||
* several top level options as action buttons while spilling remaining options over as
|
||||
* items in an overflow menu. This allows applications to present packs of actions inline with
|
||||
* specific or repeating content.
|
||||
*/
|
||||
public class ActionMenuView extends LinearLayoutCompat implements MenuBuilder.ItemInvoker,
|
||||
MenuView {
|
||||
|
||||
private static final String TAG = "ActionMenuView";
|
||||
|
||||
static final int MIN_CELL_SIZE = 56; // dips
|
||||
static final int GENERATED_ITEM_PADDING = 4; // dips
|
||||
|
||||
private MenuBuilder mMenu;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
/** Context against which to inflate popup menus. */
|
||||
private Context mPopupContext;
|
||||
|
||||
/** Theme resource against which to inflate popup menus. */
|
||||
private int mPopupTheme;
|
||||
|
||||
private boolean mReserveOverflow;
|
||||
private ActionMenuPresenter mPresenter;
|
||||
private MenuPresenter.Callback mActionMenuPresenterCallback;
|
||||
private MenuBuilder.Callback mMenuBuilderCallback;
|
||||
private boolean mFormatItems;
|
||||
private int mFormatItemsWidth;
|
||||
private int mMinCellSize;
|
||||
private int mGeneratedItemPadding;
|
||||
|
||||
private OnMenuItemClickListener mOnMenuItemClickListener;
|
||||
|
||||
public ActionMenuView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ActionMenuView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
setBaselineAligned(false);
|
||||
final float density = context.getResources().getDisplayMetrics().density;
|
||||
mMinCellSize = (int) (MIN_CELL_SIZE * density);
|
||||
mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density);
|
||||
mPopupContext = context;
|
||||
mPopupTheme = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the theme to use when inflating popup menus. By default, uses
|
||||
* the same theme as the action menu view itself.
|
||||
*
|
||||
* @param resId theme used to inflate popup menus
|
||||
* @see #getPopupTheme()
|
||||
*/
|
||||
public void setPopupTheme(int resId) {
|
||||
if (mPopupTheme != resId) {
|
||||
mPopupTheme = resId;
|
||||
if (resId == 0) {
|
||||
mPopupContext = mContext;
|
||||
} else {
|
||||
mPopupContext = new ContextThemeWrapper(mContext, resId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return resource identifier of the theme used to inflate popup menus, or
|
||||
* 0 if menus are inflated against the action menu view theme
|
||||
* @see #setPopupTheme(int)
|
||||
*/
|
||||
public int getPopupTheme() {
|
||||
return mPopupTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param presenter Menu presenter used to display popup menu
|
||||
* @hide
|
||||
*/
|
||||
public void setPresenter(ActionMenuPresenter presenter) {
|
||||
mPresenter = presenter;
|
||||
mPresenter.setMenuView(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
if (Build.VERSION.SDK_INT >= 8) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
if (mPresenter != null) {
|
||||
mPresenter.updateMenuView(false);
|
||||
|
||||
if (mPresenter.isOverflowMenuShowing()) {
|
||||
mPresenter.hideOverflowMenu();
|
||||
mPresenter.showOverflowMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
final boolean wasFormatted = mFormatItems;
|
||||
mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
|
||||
|
||||
if (wasFormatted != mFormatItems) {
|
||||
mFormatItemsWidth = 0; // Reset this when switching modes
|
||||
}
|
||||
|
||||
// Special formatting can change whether items can fit as action buttons.
|
||||
// Kick the menu and update presenters when this changes.
|
||||
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
||||
if (mFormatItems && mMenu != null && widthSize != mFormatItemsWidth) {
|
||||
mFormatItemsWidth = widthSize;
|
||||
mMenu.onItemsChanged(true);
|
||||
}
|
||||
|
||||
final int childCount = getChildCount();
|
||||
if (mFormatItems && childCount > 0) {
|
||||
onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec);
|
||||
} else {
|
||||
// Previous measurement at exact format may have set margins - reset them.
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = getChildAt(i);
|
||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
lp.leftMargin = lp.rightMargin = 0;
|
||||
}
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
private void onMeasureExactFormat(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
// We already know the width mode is EXACTLY if we're here.
|
||||
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
final int widthPadding = getPaddingLeft() + getPaddingRight();
|
||||
final int heightPadding = getPaddingTop() + getPaddingBottom();
|
||||
|
||||
final int itemHeightSpec = getChildMeasureSpec(heightMeasureSpec, heightPadding,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
widthSize -= widthPadding;
|
||||
|
||||
// Divide the view into cells.
|
||||
final int cellCount = widthSize / mMinCellSize;
|
||||
final int cellSizeRemaining = widthSize % mMinCellSize;
|
||||
|
||||
if (cellCount == 0) {
|
||||
// Give up, nothing fits.
|
||||
setMeasuredDimension(widthSize, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
final int cellSize = mMinCellSize + cellSizeRemaining / cellCount;
|
||||
|
||||
int cellsRemaining = cellCount;
|
||||
int maxChildHeight = 0;
|
||||
int maxCellsUsed = 0;
|
||||
int expandableItemCount = 0;
|
||||
int visibleItemCount = 0;
|
||||
boolean hasOverflow = false;
|
||||
|
||||
// This is used as a bitfield to locate the smallest items present. Assumes childCount < 64.
|
||||
long smallestItemsAt = 0;
|
||||
|
||||
final int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = getChildAt(i);
|
||||
if (child.getVisibility() == GONE) continue;
|
||||
|
||||
final boolean isGeneratedItem = child instanceof ActionMenuItemView;
|
||||
visibleItemCount++;
|
||||
|
||||
if (isGeneratedItem) {
|
||||
// Reset padding for generated menu item views; it may change below
|
||||
// and views are recycled.
|
||||
child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0);
|
||||
}
|
||||
|
||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
lp.expanded = false;
|
||||
lp.extraPixels = 0;
|
||||
lp.cellsUsed = 0;
|
||||
lp.expandable = false;
|
||||
lp.leftMargin = 0;
|
||||
lp.rightMargin = 0;
|
||||
lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText();
|
||||
|
||||
// Overflow always gets 1 cell. No more, no less.
|
||||
final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining;
|
||||
|
||||
final int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable,
|
||||
itemHeightSpec, heightPadding);
|
||||
|
||||
maxCellsUsed = Math.max(maxCellsUsed, cellsUsed);
|
||||
if (lp.expandable) expandableItemCount++;
|
||||
if (lp.isOverflowButton) hasOverflow = true;
|
||||
|
||||
cellsRemaining -= cellsUsed;
|
||||
maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
|
||||
if (cellsUsed == 1) smallestItemsAt |= (1 << i);
|
||||
}
|
||||
|
||||
// When we have overflow and a single expanded (text) item, we want to try centering it
|
||||
// visually in the available space even though overflow consumes some of it.
|
||||
final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2;
|
||||
|
||||
// Divide space for remaining cells if we have items that can expand.
|
||||
// Try distributing whole leftover cells to smaller items first.
|
||||
|
||||
boolean needsExpansion = false;
|
||||
while (expandableItemCount > 0 && cellsRemaining > 0) {
|
||||
int minCells = Integer.MAX_VALUE;
|
||||
long minCellsAt = 0; // Bit locations are indices of relevant child views
|
||||
int minCellsItemCount = 0;
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = getChildAt(i);
|
||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
|
||||
// Don't try to expand items that shouldn't.
|
||||
if (!lp.expandable) continue;
|
||||
|
||||
// Mark indices of children that can receive an extra cell.
|
||||
if (lp.cellsUsed < minCells) {
|
||||
minCells = lp.cellsUsed;
|
||||
minCellsAt = 1 << i;
|
||||
minCellsItemCount = 1;
|
||||
} else if (lp.cellsUsed == minCells) {
|
||||
minCellsAt |= 1 << i;
|
||||
minCellsItemCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Items that get expanded will always be in the set of smallest items when we're done.
|
||||
smallestItemsAt |= minCellsAt;
|
||||
|
||||
if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
|
||||
|
||||
// We have enough cells, all minimum size items will be incremented.
|
||||
minCells++;
|
||||
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = getChildAt(i);
|
||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
if ((minCellsAt & (1 << i)) == 0) {
|
||||
// If this item is already at our small item count, mark it for later.
|
||||
if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) {
|
||||
// Add padding to this item such that it centers.
|
||||
child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0);
|
||||
}
|
||||
lp.cellsUsed++;
|
||||
lp.expanded = true;
|
||||
cellsRemaining--;
|
||||
}
|
||||
|
||||
needsExpansion = true;
|
||||
}
|
||||
|
||||
// Divide any space left that wouldn't divide along cell boundaries
|
||||
// evenly among the smallest items
|
||||
|
||||
final boolean singleItem = !hasOverflow && visibleItemCount == 1;
|
||||
if (cellsRemaining > 0 && smallestItemsAt != 0 &&
|
||||
(cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) {
|
||||
float expandCount = Long.bitCount(smallestItemsAt);
|
||||
|
||||
if (!singleItem) {
|
||||
// The items at the far edges may only expand by half in order to pin to either side.
|
||||
if ((smallestItemsAt & 1) != 0) {
|
||||
LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams();
|
||||
if (!lp.preventEdgeOffset) expandCount -= 0.5f;
|
||||
}
|
||||
if ((smallestItemsAt & (1 << (childCount - 1))) != 0) {
|
||||
LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams());
|
||||
if (!lp.preventEdgeOffset) expandCount -= 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
final int extraPixels = expandCount > 0 ?
|
||||
(int) (cellsRemaining * cellSize / expandCount) : 0;
|
||||
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
if ((smallestItemsAt & (1 << i)) == 0) continue;
|
||||
|
||||
final View child = getChildAt(i);
|
||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
if (child instanceof ActionMenuItemView) {
|
||||
// If this is one of our views, expand and measure at the larger size.
|
||||
lp.extraPixels = extraPixels;
|
||||
lp.expanded = true;
|
||||
if (i == 0 && !lp.preventEdgeOffset) {
|
||||
// First item gets part of its new padding pushed out of sight.
|
||||
// The last item will get this implicitly from layout.
|
||||
lp.leftMargin = -extraPixels / 2;
|
||||
}
|
||||
needsExpansion = true;
|
||||
} else if (lp.isOverflowButton) {
|
||||
lp.extraPixels = extraPixels;
|
||||
lp.expanded = true;
|
||||
lp.rightMargin = -extraPixels / 2;
|
||||
needsExpansion = true;
|
||||
} else {
|
||||
// If we don't know what it is, give it some margins instead
|
||||
// and let it center within its space. We still want to pin
|
||||
// against the edges.
|
||||
if (i != 0) {
|
||||
lp.leftMargin = extraPixels / 2;
|
||||
}
|
||||
if (i != childCount - 1) {
|
||||
lp.rightMargin = extraPixels / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cellsRemaining = 0;
|
||||
}
|
||||
|
||||
// Remeasure any items that have had extra space allocated to them.
|
||||
if (needsExpansion) {
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = getChildAt(i);
|
||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
|
||||
if (!lp.expanded) continue;
|
||||
|
||||
final int width = lp.cellsUsed * cellSize + lp.extraPixels;
|
||||
child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||
itemHeightSpec);
|
||||
}
|
||||
}
|
||||
|
||||
if (heightMode != MeasureSpec.EXACTLY) {
|
||||
heightSize = maxChildHeight;
|
||||
}
|
||||
|
||||
setMeasuredDimension(widthSize, heightSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Measure a child view to fit within cell-based formatting. The child's width
|
||||
* will be measured to a whole multiple of cellSize.
|
||||
*
|
||||
* <p>Sets the expandable and cellsUsed fields of LayoutParams.
|
||||
*
|
||||
* @param child Child to measure
|
||||
* @param cellSize Size of one cell
|
||||
* @param cellsRemaining Number of cells remaining that this view can expand to fill
|
||||
* @param parentHeightMeasureSpec MeasureSpec used by the parent view
|
||||
* @param parentHeightPadding Padding present in the parent view
|
||||
* @return Number of cells this child was measured to occupy
|
||||
*/
|
||||
static int measureChildForCells(View child, int cellSize, int cellsRemaining,
|
||||
int parentHeightMeasureSpec, int parentHeightPadding) {
|
||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
|
||||
final int childHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec) -
|
||||
parentHeightPadding;
|
||||
final int childHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec);
|
||||
final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode);
|
||||
|
||||
final ActionMenuItemView itemView = child instanceof ActionMenuItemView ?
|
||||
(ActionMenuItemView) child : null;
|
||||
final boolean hasText = itemView != null && itemView.hasText();
|
||||
|
||||
int cellsUsed = 0;
|
||||
if (cellsRemaining > 0 && (!hasText || cellsRemaining >= 2)) {
|
||||
final int childWidthSpec = MeasureSpec.makeMeasureSpec(
|
||||
cellSize * cellsRemaining, MeasureSpec.AT_MOST);
|
||||
child.measure(childWidthSpec, childHeightSpec);
|
||||
|
||||
final int measuredWidth = child.getMeasuredWidth();
|
||||
cellsUsed = measuredWidth / cellSize;
|
||||
if (measuredWidth % cellSize != 0) cellsUsed++;
|
||||
if (hasText && cellsUsed < 2) cellsUsed = 2;
|
||||
}
|
||||
|
||||
final boolean expandable = !lp.isOverflowButton && hasText;
|
||||
lp.expandable = expandable;
|
||||
|
||||
lp.cellsUsed = cellsUsed;
|
||||
final int targetWidth = cellsUsed * cellSize;
|
||||
child.measure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
|
||||
childHeightSpec);
|
||||
return cellsUsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
if (!mFormatItems) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
return;
|
||||
}
|
||||
|
||||
final int childCount = getChildCount();
|
||||
final int midVertical = (bottom - top) / 2;
|
||||
final int dividerWidth = getDividerWidth();
|
||||
int overflowWidth = 0;
|
||||
int nonOverflowWidth = 0;
|
||||
int nonOverflowCount = 0;
|
||||
int widthRemaining = right - left - getPaddingRight() - getPaddingLeft();
|
||||
boolean hasOverflow = false;
|
||||
final boolean isLayoutRtl = ViewUtils.isLayoutRtl(this);
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View v = getChildAt(i);
|
||||
if (v.getVisibility() == GONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LayoutParams p = (LayoutParams) v.getLayoutParams();
|
||||
if (p.isOverflowButton) {
|
||||
overflowWidth = v.getMeasuredWidth();
|
||||
if (hasSupportDividerBeforeChildAt(i)) {
|
||||
overflowWidth += dividerWidth;
|
||||
}
|
||||
int height = v.getMeasuredHeight();
|
||||
int r;
|
||||
int l;
|
||||
if (isLayoutRtl) {
|
||||
l = getPaddingLeft() + p.leftMargin;
|
||||
r = l + overflowWidth;
|
||||
} else {
|
||||
r = getWidth() - getPaddingRight() - p.rightMargin;
|
||||
l = r - overflowWidth;
|
||||
}
|
||||
int t = midVertical - (height / 2);
|
||||
int b = t + height;
|
||||
v.layout(l, t, r, b);
|
||||
|
||||
widthRemaining -= overflowWidth;
|
||||
hasOverflow = true;
|
||||
} else {
|
||||
final int size = v.getMeasuredWidth() + p.leftMargin + p.rightMargin;
|
||||
nonOverflowWidth += size;
|
||||
widthRemaining -= size;
|
||||
if (hasSupportDividerBeforeChildAt(i)) {
|
||||
nonOverflowWidth += dividerWidth;
|
||||
}
|
||||
nonOverflowCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (childCount == 1 && !hasOverflow) {
|
||||
// Center a single child
|
||||
final View v = getChildAt(0);
|
||||
final int width = v.getMeasuredWidth();
|
||||
final int height = v.getMeasuredHeight();
|
||||
final int midHorizontal = (right - left) / 2;
|
||||
final int l = midHorizontal - width / 2;
|
||||
final int t = midVertical - height / 2;
|
||||
v.layout(l, t, l + width, t + height);
|
||||
return;
|
||||
}
|
||||
|
||||
final int spacerCount = nonOverflowCount - (hasOverflow ? 0 : 1);
|
||||
final int spacerSize = Math.max(0, spacerCount > 0 ? widthRemaining / spacerCount : 0);
|
||||
|
||||
if (isLayoutRtl) {
|
||||
int startRight = getWidth() - getPaddingRight();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View v = getChildAt(i);
|
||||
final LayoutParams lp = (LayoutParams) v.getLayoutParams();
|
||||
if (v.getVisibility() == GONE || lp.isOverflowButton) {
|
||||
continue;
|
||||
}
|
||||
|
||||
startRight -= lp.rightMargin;
|
||||
int width = v.getMeasuredWidth();
|
||||
int height = v.getMeasuredHeight();
|
||||
int t = midVertical - height / 2;
|
||||
v.layout(startRight - width, t, startRight, t + height);
|
||||
startRight -= width + lp.leftMargin + spacerSize;
|
||||
}
|
||||
} else {
|
||||
int startLeft = getPaddingLeft();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View v = getChildAt(i);
|
||||
final LayoutParams lp = (LayoutParams) v.getLayoutParams();
|
||||
if (v.getVisibility() == GONE || lp.isOverflowButton) {
|
||||
continue;
|
||||
}
|
||||
|
||||
startLeft += lp.leftMargin;
|
||||
int width = v.getMeasuredWidth();
|
||||
int height = v.getMeasuredHeight();
|
||||
int t = midVertical - height / 2;
|
||||
v.layout(startLeft, t, startLeft + width, t + height);
|
||||
startLeft += width + lp.rightMargin + spacerSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
dismissPopupMenus();
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public boolean isOverflowReserved() {
|
||||
return mReserveOverflow;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void setOverflowReserved(boolean reserveOverflow) {
|
||||
mReserveOverflow = reserveOverflow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LayoutParams generateDefaultLayoutParams() {
|
||||
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT);
|
||||
params.gravity = Gravity.CENTER_VERTICAL;
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LayoutParams generateLayoutParams(AttributeSet attrs) {
|
||||
return new LayoutParams(getContext(), attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
|
||||
if (p != null) {
|
||||
final LayoutParams result = p instanceof LayoutParams
|
||||
? new LayoutParams((LayoutParams) p)
|
||||
: new LayoutParams(p);
|
||||
if (result.gravity <= Gravity.NO_GRAVITY) {
|
||||
result.gravity = Gravity.CENTER_VERTICAL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return generateDefaultLayoutParams();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
|
||||
return p != null && p instanceof LayoutParams;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public LayoutParams generateOverflowButtonLayoutParams() {
|
||||
LayoutParams result = generateDefaultLayoutParams();
|
||||
result.isOverflowButton = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public boolean invokeItem(MenuItemImpl item) {
|
||||
return mMenu.performItemAction(item, 0);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public int getWindowAnimations() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void initialize(MenuBuilder menu) {
|
||||
mMenu = menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Menu object that this ActionMenuView is currently presenting.
|
||||
*
|
||||
* <p>Applications should use this method to obtain the ActionMenuView's Menu object
|
||||
* and inflate or add content to it as necessary.</p>
|
||||
*
|
||||
* @return the Menu presented by this view
|
||||
*/
|
||||
public Menu getMenu() {
|
||||
if (mMenu == null) {
|
||||
final Context context = getContext();
|
||||
mMenu = new MenuBuilder(context);
|
||||
mMenu.setCallback(new MenuBuilderCallback());
|
||||
mPresenter = new ActionMenuPresenter(context);
|
||||
mPresenter.setReserveOverflow(true);
|
||||
mPresenter.setCallback(mActionMenuPresenterCallback != null
|
||||
? mActionMenuPresenterCallback : new ActionMenuPresenterCallback());
|
||||
mMenu.addMenuPresenter(mPresenter, mPopupContext);
|
||||
mPresenter.setMenuView(this);
|
||||
}
|
||||
|
||||
return mMenu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called before the first call to getMenu()
|
||||
* @hide
|
||||
*/
|
||||
public void setMenuCallbacks(MenuPresenter.Callback pcb, MenuBuilder.Callback mcb) {
|
||||
mActionMenuPresenterCallback = pcb;
|
||||
mMenuBuilderCallback = mcb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current menu or null if one has not yet been configured.
|
||||
* @hide Internal use only for action bar integration
|
||||
*/
|
||||
public MenuBuilder peekMenu() {
|
||||
return mMenu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow items from the associated menu.
|
||||
*
|
||||
* @return true if the menu was able to be shown, false otherwise
|
||||
*/
|
||||
public boolean showOverflowMenu() {
|
||||
return mPresenter != null && mPresenter.showOverflowMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the overflow items from the associated menu.
|
||||
*
|
||||
* @return true if the menu was able to be hidden, false otherwise
|
||||
*/
|
||||
public boolean hideOverflowMenu() {
|
||||
return mPresenter != null && mPresenter.hideOverflowMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the overflow menu is currently showing. This may not reflect
|
||||
* a pending show operation in progress.
|
||||
*
|
||||
* @return true if the overflow menu is currently showing
|
||||
*/
|
||||
public boolean isOverflowMenuShowing() {
|
||||
return mPresenter != null && mPresenter.isOverflowMenuShowing();
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public boolean isOverflowMenuShowPending() {
|
||||
return mPresenter != null && mPresenter.isOverflowMenuShowPending();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss any popups associated with this menu view.
|
||||
*/
|
||||
public void dismissPopupMenus() {
|
||||
if (mPresenter != null) {
|
||||
mPresenter.dismissPopupMenus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide Private LinearLayout (superclass) API. Un-hide if LinearLayout API is made public.
|
||||
*/
|
||||
protected boolean hasSupportDividerBeforeChildAt(int childIndex) {
|
||||
if (childIndex == 0) {
|
||||
return false;
|
||||
}
|
||||
final View childBefore = getChildAt(childIndex - 1);
|
||||
final View child = getChildAt(childIndex);
|
||||
boolean result = false;
|
||||
if (childIndex < getChildCount() && childBefore instanceof ActionMenuChildView) {
|
||||
result |= ((ActionMenuChildView) childBefore).needsDividerAfter();
|
||||
}
|
||||
if (childIndex > 0 && child instanceof ActionMenuChildView) {
|
||||
result |= ((ActionMenuChildView) child).needsDividerBefore();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void setExpandedActionViewsExclusive(boolean exclusive) {
|
||||
mPresenter.setExpandedActionViewsExclusive(exclusive);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (mMenuBuilderCallback != null) {
|
||||
mMenuBuilderCallback.onMenuModeChange(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();
|
||||
public boolean needsDividerAfter();
|
||||
}
|
||||
|
||||
public static class LayoutParams extends LinearLayoutCompat.LayoutParams {
|
||||
|
||||
@ViewDebug.ExportedProperty()
|
||||
public boolean isOverflowButton;
|
||||
|
||||
@ViewDebug.ExportedProperty()
|
||||
public int cellsUsed;
|
||||
|
||||
@ViewDebug.ExportedProperty()
|
||||
public int extraPixels;
|
||||
|
||||
@ViewDebug.ExportedProperty()
|
||||
public boolean expandable;
|
||||
|
||||
@ViewDebug.ExportedProperty()
|
||||
public boolean preventEdgeOffset;
|
||||
|
||||
boolean expanded;
|
||||
|
||||
public LayoutParams(Context c, AttributeSet attrs) {
|
||||
super(c, attrs);
|
||||
}
|
||||
|
||||
public LayoutParams(ViewGroup.LayoutParams other) {
|
||||
super(other);
|
||||
}
|
||||
|
||||
public LayoutParams(LayoutParams other) {
|
||||
super((ViewGroup.LayoutParams) other);
|
||||
isOverflowButton = other.isOverflowButton;
|
||||
}
|
||||
|
||||
public LayoutParams(int width, int height) {
|
||||
super(width, height);
|
||||
isOverflowButton = false;
|
||||
}
|
||||
|
||||
LayoutParams(int width, int height, boolean isOverflowButton) {
|
||||
super(width, height);
|
||||
this.isOverflowButton = isOverflowButton;
|
||||
}
|
||||
}
|
||||
}
|
1837
android/support/v7/widget/LinearLayoutCompat.java
Normal file
1837
android/support/v7/widget/LinearLayoutCompat.java
Normal file
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue