summaryrefslogtreecommitdiff
path: root/android/support
diff options
context:
space:
mode:
authorJeff Davidson <jpd@google.com>2018-02-08 15:30:06 -0800
committerJeff Davidson <jpd@google.com>2018-02-08 15:30:06 -0800
commita192cc2a132cb0ee8588e2df755563ec7008c179 (patch)
tree380e4db22df19c819bd37df34bf06e7568916a50 /android/support
parent98fe7819c6d14f4f464a5cac047f9e82dee5da58 (diff)
downloadandroid-28-a192cc2a132cb0ee8588e2df755563ec7008c179.tar.gz
Update fullsdk to 4575844
/google/data/ro/projects/android/fetch_artifact \ --bid 4575844 \ --target sdk_phone_x86_64-sdk \ sdk-repo-linux-sources-4575844.zip Test: TreeHugger Change-Id: I81e0eb157b4ac3b38408d0ef86f9d6286471f87a
Diffstat (limited to 'android/support')
-rw-r--r--android/support/LibraryVersions.java19
-rw-r--r--android/support/Version.java157
-rw-r--r--android/support/VersionFileWriterTask.java109
-rw-r--r--android/support/animation/AnimationHandler.java7
-rw-r--r--android/support/animation/DynamicAnimation.java2
-rw-r--r--android/support/animation/SpringForce.java3
-rw-r--r--android/support/customtabs/CustomTabsClient.java2
-rw-r--r--android/support/design/internal/BaselineLayout.java116
-rw-r--r--android/support/design/internal/BottomNavigationItemView.java258
-rw-r--r--android/support/design/internal/BottomNavigationMenu.java59
-rw-r--r--android/support/design/internal/BottomNavigationMenuView.java343
-rw-r--r--android/support/design/internal/BottomNavigationPresenter.java152
-rw-r--r--android/support/design/internal/ForegroundLinearLayout.java240
-rw-r--r--android/support/design/internal/NavigationMenu.java49
-rw-r--r--android/support/design/internal/NavigationMenuItemView.java272
-rw-r--r--android/support/design/internal/NavigationMenuPresenter.java686
-rw-r--r--android/support/design/internal/NavigationMenuView.java58
-rw-r--r--android/support/design/internal/NavigationSubMenu.java46
-rw-r--r--android/support/design/internal/ParcelableSparseArray.java83
-rw-r--r--android/support/design/internal/ScrimInsetsFrameLayout.java138
-rw-r--r--android/support/design/internal/SnackbarContentLayout.java157
-rw-r--r--android/support/design/internal/TextScale.java85
-rw-r--r--android/support/design/internal/package-info.java9
-rw-r--r--android/support/design/widget/AnimationUtils.java45
-rw-r--r--android/support/design/widget/AppBarLayout.java1474
-rw-r--r--android/support/design/widget/BaseTransientBottomBar.java753
-rw-r--r--android/support/design/widget/BottomNavigationView.java477
-rw-r--r--android/support/design/widget/BottomSheetBehavior.java829
-rw-r--r--android/support/design/widget/BottomSheetDialog.java230
-rw-r--r--android/support/design/widget/BottomSheetDialogFragment.java35
-rw-r--r--android/support/design/widget/CheckableImageButton.java101
-rw-r--r--android/support/design/widget/CircularBorderDrawable.java211
-rw-r--r--android/support/design/widget/CircularBorderDrawableLollipop.java34
-rw-r--r--android/support/design/widget/CollapsingTextHelper.java723
-rw-r--r--android/support/design/widget/CollapsingToolbarLayout.java1308
-rw-r--r--android/support/design/widget/CoordinatorLayout.java6
-rw-r--r--android/support/design/widget/DrawableUtils.java65
-rw-r--r--android/support/design/widget/FloatingActionButton.java870
-rw-r--r--android/support/design/widget/FloatingActionButtonImpl.java531
-rw-r--r--android/support/design/widget/FloatingActionButtonLollipop.java226
-rw-r--r--android/support/design/widget/HeaderBehavior.java307
-rw-r--r--android/support/design/widget/HeaderScrollingViewBehavior.java182
-rw-r--r--android/support/design/widget/NavigationView.java494
-rw-r--r--android/support/design/widget/ShadowDrawableWrapper.java365
-rw-r--r--android/support/design/widget/ShadowViewDelegate.java26
-rw-r--r--android/support/design/widget/Snackbar.java353
-rw-r--r--android/support/design/widget/SnackbarManager.java243
-rw-r--r--android/support/design/widget/StateListAnimator.java116
-rw-r--r--android/support/design/widget/SwipeDismissBehavior.java411
-rw-r--r--android/support/design/widget/TabItem.java57
-rw-r--r--android/support/design/widget/TabLayout.java2217
-rw-r--r--android/support/design/widget/TextInputEditText.java65
-rw-r--r--android/support/design/widget/TextInputLayout.java1530
-rw-r--r--android/support/design/widget/ThemeUtils.java37
-rw-r--r--android/support/design/widget/ViewOffsetBehavior.java91
-rw-r--r--android/support/design/widget/ViewOffsetHelper.java102
-rw-r--r--android/support/design/widget/ViewUtils.java39
-rw-r--r--android/support/design/widget/ViewUtilsLollipop.java79
-rw-r--r--android/support/design/widget/VisibilityAwareImageButton.java55
-rw-r--r--android/support/graphics/drawable/AndroidResources.java4
-rw-r--r--android/support/graphics/drawable/AnimatedVectorDrawableCompat.java3
-rw-r--r--android/support/graphics/drawable/AnimatorInflaterCompat.java1
-rw-r--r--android/support/media/ExifInterface.java5
-rw-r--r--android/support/media/tv/BasePreviewProgram.java2
-rw-r--r--android/support/media/tv/BaseProgram.java2
-rw-r--r--android/support/media/tv/TvContractCompat.java1
-rw-r--r--android/support/multidex/MultiDex.java136
-rw-r--r--android/support/multidex/MultiDexExtractor.java149
-rw-r--r--android/support/transition/TransitionSet.java11
-rw-r--r--android/support/transition/TransitionSetTest.java8
-rw-r--r--android/support/v13/view/DragAndDropPermissionsCompat.java63
-rw-r--r--android/support/v13/view/DragStartHelper.java4
-rw-r--r--android/support/v13/view/inputmethod/EditorInfoCompat.java81
-rw-r--r--android/support/v13/view/inputmethod/InputConnectionCompat.java213
-rw-r--r--android/support/v14/preference/EditTextPreferenceDialogFragment.java6
-rw-r--r--android/support/v14/preference/ListPreferenceDialogFragment.java2
-rw-r--r--android/support/v14/preference/MultiSelectListPreference.java47
-rw-r--r--android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java6
-rw-r--r--android/support/v14/preference/PreferenceDialogFragment.java10
-rw-r--r--android/support/v14/preference/PreferenceFragment.java18
-rw-r--r--android/support/v14/preference/SwitchPreference.java29
-rw-r--r--android/support/v17/leanback/app/PlaybackFragment.java5
-rw-r--r--android/support/v17/leanback/app/PlaybackSupportFragment.java5
-rw-r--r--android/support/v17/leanback/media/PlaybackControlGlue.java2
-rw-r--r--android/support/v17/leanback/media/PlaybackTransportControlGlue.java2
-rw-r--r--android/support/v17/leanback/transition/TransitionEpicenterCallback.java3
-rw-r--r--android/support/v17/leanback/util/MathUtil.java3
-rw-r--r--android/support/v17/leanback/widget/DetailsParallaxDrawable.java2
-rw-r--r--android/support/v17/leanback/widget/GridLayoutManager.java5
-rw-r--r--android/support/v17/leanback/widget/MediaRowFocusView.java4
-rw-r--r--android/support/v17/leanback/widget/ParallaxEffect.java4
-rw-r--r--android/support/v17/leanback/widget/VideoSurfaceView.java2
-rw-r--r--android/support/v4/app/ActivityCompat.java27
-rw-r--r--android/support/v4/app/Fragment.java10
-rw-r--r--android/support/v4/app/FragmentActivity.java1
-rw-r--r--android/support/v4/app/LoaderManager.java124
-rw-r--r--android/support/v4/app/NotificationCompat.java199
-rw-r--r--android/support/v4/app/NotificationCompatBuilder.java9
-rw-r--r--android/support/v4/app/NotificationCompatJellybean.java7
-rw-r--r--android/support/v4/app/NotificationManagerCompat.java2
-rw-r--r--android/support/v4/content/FileProvider.java19
-rw-r--r--android/support/v4/content/Loader.java28
-rw-r--r--android/support/v4/content/WakefulBroadcastReceiver.java5
-rw-r--r--android/support/v4/graphics/TypefaceCompatUtil.java14
-rw-r--r--android/support/v4/graphics/drawable/DrawableCompat.java14
-rw-r--r--android/support/v4/graphics/drawable/WrappedDrawable.java (renamed from android/support/v4/graphics/drawable/DrawableWrapper.java)2
-rw-r--r--android/support/v4/graphics/drawable/WrappedDrawableApi14.java (renamed from android/support/v4/graphics/drawable/DrawableWrapperApi14.java)44
-rw-r--r--android/support/v4/graphics/drawable/WrappedDrawableApi19.java (renamed from android/support/v4/graphics/drawable/DrawableWrapperApi19.java)9
-rw-r--r--android/support/v4/graphics/drawable/WrappedDrawableApi21.java (renamed from android/support/v4/graphics/drawable/DrawableWrapperApi21.java)23
-rw-r--r--android/support/v4/util/ArraySet.java60
-rw-r--r--android/support/v4/util/LongSparseArray.java8
-rw-r--r--android/support/v4/util/ObjectsCompat.java43
-rw-r--r--android/support/v4/util/SparseArrayCompat.java8
-rw-r--r--android/support/v4/view/ViewCompat.java56
-rw-r--r--android/support/v4/view/ViewConfigurationCompat.java12
-rw-r--r--android/support/v4/view/WindowCompat.java27
-rw-r--r--android/support/v4/widget/ContentLoadingProgressBar.java6
-rw-r--r--android/support/v4/widget/CursorAdapter.java18
-rw-r--r--android/support/v4/widget/SimpleCursorAdapter.java4
-rw-r--r--android/support/v4/widget/TextViewCompat.java201
-rw-r--r--android/support/v7/preference/Preference.java3
-rw-r--r--android/support/v7/recyclerview/extensions/ListAdapter.java15
-rw-r--r--android/support/v7/recyclerview/extensions/ListAdapterConfig.java97
-rw-r--r--android/support/v7/recyclerview/extensions/ListAdapterHelper.java91
-rw-r--r--android/support/v7/util/AdapterListUpdateCallback.java63
-rw-r--r--android/support/v7/util/DiffUtil.java32
-rw-r--r--android/support/v7/widget/AppCompatEditText.java16
-rw-r--r--android/support/v7/widget/AppCompatProgressBarHelper.java8
-rw-r--r--android/support/v7/widget/ContentFrameLayout.java2
-rw-r--r--android/support/v7/widget/DrawableUtils.java5
-rw-r--r--android/support/v7/widget/DropDownListView.java437
-rw-r--r--android/support/v7/widget/LinearLayoutCompat.java2
-rw-r--r--android/support/v7/widget/ListViewCompat.java413
-rw-r--r--android/support/v7/widget/RecyclerView.java22
-rw-r--r--android/support/v7/widget/StaggeredGridLayoutManager.java2
-rw-r--r--android/support/v7/widget/TooltipCompatHandler.java38
-rw-r--r--android/support/wear/widget/BoxInsetLayout.java2
137 files changed, 1900 insertions, 18903 deletions
diff --git a/android/support/LibraryVersions.java b/android/support/LibraryVersions.java
index 6d8d6bf5..813d9a85 100644
--- a/android/support/LibraryVersions.java
+++ b/android/support/LibraryVersions.java
@@ -26,19 +26,14 @@ public class LibraryVersions {
public static final Version SUPPORT_LIBRARY = new Version("28.0.0-SNAPSHOT");
/**
- * Version code for flatfoot 1.0 projects (room, lifecycles)
- */
- private static final Version FLATFOOT_1_0_BATCH = new Version("1.0.0");
-
- /**
* Version code for Room
*/
- public static final Version ROOM = FLATFOOT_1_0_BATCH;
+ public static final Version ROOM = new Version("1.1.0-alpha1");
/**
* Version code for Lifecycle extensions (ProcessLifecycleOwner, Fragment support)
*/
- public static final Version LIFECYCLES_EXT = new Version("1.1.0-SNAPSHOT");
+ public static final Version LIFECYCLES_EXT = new Version("1.1.0");
/**
* Version code for Lifecycle LiveData
@@ -53,9 +48,9 @@ public class LibraryVersions {
/**
* Version code for RecyclerView & Room paging
*/
- public static final Version PAGING = new Version("1.0.0-alpha4-1");
+ public static final Version PAGING = new Version("1.0.0-alpha5");
- private static final Version LIFECYCLES = new Version("1.0.3");
+ private static final Version LIFECYCLES = new Version("1.1.0");
/**
* Version code for Lifecycle libs that are required by the support library
@@ -70,15 +65,15 @@ public class LibraryVersions {
/**
* Version code for shared code of flatfoot
*/
- public static final Version ARCH_CORE = new Version("1.0.0");
+ public static final Version ARCH_CORE = new Version("1.1.0");
/**
* Version code for shared code of flatfoot runtime
*/
- public static final Version ARCH_RUNTIME = FLATFOOT_1_0_BATCH;
+ public static final Version ARCH_RUNTIME = ARCH_CORE;
/**
* Version code for shared testing code of flatfoot
*/
- public static final Version ARCH_CORE_TESTING = FLATFOOT_1_0_BATCH;
+ public static final Version ARCH_CORE_TESTING = ARCH_CORE;
}
diff --git a/android/support/Version.java b/android/support/Version.java
deleted file mode 100644
index 36c7728b..00000000
--- a/android/support/Version.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import java.io.File;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Utility class which represents a version
- */
-public class Version implements Comparable<Version> {
- private static final Pattern VERSION_FILE_REGEX = Pattern.compile("^(\\d+\\.\\d+\\.\\d+).txt$");
- private static final Pattern VERSION_REGEX = Pattern
- .compile("^(\\d+)\\.(\\d+)\\.(\\d+)(-.+)?$");
-
- private final int mMajor;
- private final int mMinor;
- private final int mPatch;
- private final String mExtra;
-
- public Version(String versionString) {
- this(checkedMatcher(versionString));
- }
-
- private static Matcher checkedMatcher(String versionString) {
- Matcher matcher = VERSION_REGEX.matcher(versionString);
- if (!matcher.matches()) {
- throw new IllegalArgumentException("Can not parse version: " + versionString);
- }
- return matcher;
- }
-
- private Version(Matcher matcher) {
- mMajor = Integer.parseInt(matcher.group(1));
- mMinor = Integer.parseInt(matcher.group(2));
- mPatch = Integer.parseInt(matcher.group(3));
- mExtra = matcher.groupCount() == 4 ? matcher.group(4) : null;
- }
-
- @Override
- public int compareTo(Version version) {
- if (mMajor != version.mMajor) {
- return mMajor - version.mMajor;
- }
- if (mMinor != version.mMinor) {
- return mMinor - version.mMinor;
- }
- if (mPatch != version.mPatch) {
- return mPatch - version.mPatch;
- }
- if (mExtra == null) {
- if (version.mExtra == null) {
- return 0;
- }
- // not having any extra is always a later version
- return 1;
- } else {
- if (version.mExtra == null) {
- // not having any extra is always a later version
- return -1;
- }
- // gradle uses lexicographic ordering
- return mExtra.compareTo(version.mExtra);
- }
- }
-
- public boolean isPatch() {
- return mPatch != 0;
- }
-
- public boolean isSnapshot() {
- return "-SNAPSHOT".equals(mExtra);
- }
-
- public int getMajor() {
- return mMajor;
- }
-
- public int getMinor() {
- return mMinor;
- }
-
- public int getPatch() {
- return mPatch;
- }
-
- public String getExtra() {
- return mExtra;
- }
-
- @Override
- public String toString() {
- return mMajor + "." + mMinor + "." + mPatch + (mExtra != null ? mExtra : "");
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- Version version = (Version) o;
-
- if (mMajor != version.mMajor) return false;
- if (mMinor != version.mMinor) return false;
- if (mPatch != version.mPatch) return false;
- return mExtra != null ? mExtra.equals(version.mExtra) : version.mExtra == null;
- }
-
- @Override
- public int hashCode() {
- int result = mMajor;
- result = 31 * result + mMinor;
- result = 31 * result + mPatch;
- result = 31 * result + (mExtra != null ? mExtra.hashCode() : 0);
- return result;
- }
-
- /**
- * @return Version or null, if a name of the given file doesn't match
- */
- public static Version from(File file) {
- if (!file.isFile()) {
- return null;
- }
- Matcher matcher = VERSION_FILE_REGEX.matcher(file.getName());
- if (!matcher.matches()) {
- return null;
- }
- return new Version(matcher.group(1));
- }
-
- /**
- * @return Version or null, if the given string doesn't match
- */
- public static Version from(String versionString) {
- Matcher matcher = VERSION_REGEX.matcher(versionString);
- if (!matcher.matches()) {
- return null;
- }
- return new Version(matcher);
- }
-}
diff --git a/android/support/VersionFileWriterTask.java b/android/support/VersionFileWriterTask.java
deleted file mode 100644
index aafa0236..00000000
--- a/android/support/VersionFileWriterTask.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import com.android.build.gradle.LibraryExtension;
-
-import org.gradle.api.Action;
-import org.gradle.api.DefaultTask;
-import org.gradle.api.Project;
-import org.gradle.api.tasks.Input;
-import org.gradle.api.tasks.OutputFile;
-import org.gradle.api.tasks.TaskAction;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-
-/**
- * Task that allows to write a version to a given output file.
- */
-public class VersionFileWriterTask extends DefaultTask {
- public static final String RESOURCE_DIRECTORY = "generatedResources";
- public static final String VERSION_FILE_PATH =
- RESOURCE_DIRECTORY + "/META-INF/%s_%s.version";
-
- private String mVersion;
- private File mOutputFile;
-
- /**
- * Sets up Android Library project to have a task that generates a version file.
- *
- * @param project an Android Library project.
- */
- public static void setUpAndroidLibrary(Project project) {
- project.afterEvaluate(new Action<Project>() {
- @Override
- public void execute(Project project) {
- LibraryExtension library =
- project.getExtensions().findByType(LibraryExtension.class);
-
- String group = (String) project.getProperties().get("group");
- String artifactId = (String) project.getProperties().get("name");
- String version = (String) project.getProperties().get("version");
-
- // Add a java resource file to the library jar for version tracking purposes.
- File artifactName = new File(project.getBuildDir(),
- String.format(VersionFileWriterTask.VERSION_FILE_PATH,
- group, artifactId));
-
- VersionFileWriterTask writeVersionFile =
- project.getTasks().create("writeVersionFile", VersionFileWriterTask.class);
- writeVersionFile.setVersion(version);
- writeVersionFile.setOutputFile(artifactName);
-
- library.getLibraryVariants().all(
- libraryVariant -> libraryVariant.getProcessJavaResources().dependsOn(
- writeVersionFile));
-
- library.getSourceSets().getByName("main").getResources().srcDir(
- new File(project.getBuildDir(), VersionFileWriterTask.RESOURCE_DIRECTORY)
- );
- }
- });
- }
-
- @Input
- public String getVersion() {
- return mVersion;
- }
-
- public void setVersion(String version) {
- mVersion = version;
- }
-
- @OutputFile
- public File getOutputFile() {
- return mOutputFile;
- }
-
- public void setOutputFile(File outputFile) {
- mOutputFile = outputFile;
- }
-
- /**
- * The main method for actually writing out the file.
- *
- * @throws IOException
- */
- @TaskAction
- public void run() throws IOException {
- PrintWriter writer = new PrintWriter(mOutputFile);
- writer.println(mVersion);
- writer.close();
- }
-}
diff --git a/android/support/animation/AnimationHandler.java b/android/support/animation/AnimationHandler.java
index 6c39b23a..24bc43a2 100644
--- a/android/support/animation/AnimationHandler.java
+++ b/android/support/animation/AnimationHandler.java
@@ -35,8 +35,6 @@ import java.util.ArrayList;
* The handler uses the Choreographer by default for doing periodic callbacks. A custom
* AnimationFrameCallbackProvider can be set on the handler to provide timing pulse that
* may be independent of UI frame update. This could be useful in testing.
- *
- * @hide
*/
class AnimationHandler {
/**
@@ -57,7 +55,7 @@ class AnimationHandler {
* the new frame, so that they can update animation values as needed.
*/
class AnimationCallbackDispatcher {
- public void dispatchAnimationFrame() {
+ void dispatchAnimationFrame() {
mCurrentFrameTime = SystemClock.uptimeMillis();
AnimationHandler.this.doAnimationFrame(mCurrentFrameTime);
if (mAnimationCallbacks.size() > 0) {
@@ -72,7 +70,6 @@ class AnimationHandler {
/**
* Internal per-thread collections used to avoid set collisions as animations start and end
* while being processed.
- * @hide
*/
private final SimpleArrayMap<AnimationFrameCallback, Long> mDelayedCallbackStartTime =
new SimpleArrayMap<>();
@@ -249,7 +246,7 @@ class AnimationHandler {
* timing pulse without using Choreographer. That way we could use any arbitrary interval for
* our timing pulse in the tests.
*/
- public abstract static class AnimationFrameCallbackProvider {
+ abstract static class AnimationFrameCallbackProvider {
final AnimationCallbackDispatcher mDispatcher;
AnimationFrameCallbackProvider(AnimationCallbackDispatcher dispatcher) {
mDispatcher = dispatcher;
diff --git a/android/support/animation/DynamicAnimation.java b/android/support/animation/DynamicAnimation.java
index 8ea48b94..7cbd5bb2 100644
--- a/android/support/animation/DynamicAnimation.java
+++ b/android/support/animation/DynamicAnimation.java
@@ -18,6 +18,7 @@ package android.support.animation;
import android.os.Looper;
import android.support.annotation.FloatRange;
+import android.support.annotation.RestrictTo;
import android.support.v4.view.ViewCompat;
import android.util.AndroidRuntimeException;
import android.view.View;
@@ -631,6 +632,7 @@ public abstract class DynamicAnimation<T extends DynamicAnimation<T>>
*
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
@Override
public boolean doAnimationFrame(long frameTime) {
if (mLastFrameTime == 0) {
diff --git a/android/support/animation/SpringForce.java b/android/support/animation/SpringForce.java
index 5f95aa8c..dfb4c674 100644
--- a/android/support/animation/SpringForce.java
+++ b/android/support/animation/SpringForce.java
@@ -17,6 +17,7 @@
package android.support.animation;
import android.support.annotation.FloatRange;
+import android.support.annotation.RestrictTo;
/**
* Spring Force defines the characteristics of the spring being used in the animation.
@@ -210,6 +211,7 @@ public final class SpringForce implements Force {
/**
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
@Override
public float getAcceleration(float lastDisplacement, float lastVelocity) {
@@ -224,6 +226,7 @@ public final class SpringForce implements Force {
/**
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
@Override
public boolean isAtEquilibrium(float value, float velocity) {
if (Math.abs(velocity) < mVelocityThreshold
diff --git a/android/support/customtabs/CustomTabsClient.java b/android/support/customtabs/CustomTabsClient.java
index 2e955cbe..371b5a1f 100644
--- a/android/support/customtabs/CustomTabsClient.java
+++ b/android/support/customtabs/CustomTabsClient.java
@@ -45,7 +45,7 @@ public class CustomTabsClient {
private final ICustomTabsService mService;
private final ComponentName mServiceComponentName;
- /**@hide*/
+ /** @hide */
@RestrictTo(LIBRARY_GROUP)
CustomTabsClient(ICustomTabsService service, ComponentName componentName) {
mService = service;
diff --git a/android/support/design/internal/BaselineLayout.java b/android/support/design/internal/BaselineLayout.java
deleted file mode 100644
index 0bfdf249..00000000
--- a/android/support/design/internal/BaselineLayout.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2016 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.design.internal;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * A simple ViewGroup that aligns all the views inside on a baseline. Note: bottom padding for this
- * view will be measured starting from the baseline.
- *
- * @hide
- */
-public class BaselineLayout extends ViewGroup {
- private int mBaseline = -1;
-
- public BaselineLayout(Context context) {
- super(context, null, 0);
- }
-
- public BaselineLayout(Context context, AttributeSet attrs) {
- super(context, attrs, 0);
- }
-
- public BaselineLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int count = getChildCount();
- int maxWidth = 0;
- int maxHeight = 0;
- int maxChildBaseline = -1;
- int maxChildDescent = -1;
- int childState = 0;
-
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == GONE) {
- continue;
- }
-
- measureChild(child, widthMeasureSpec, heightMeasureSpec);
- final int baseline = child.getBaseline();
- if (baseline != -1) {
- maxChildBaseline = Math.max(maxChildBaseline, baseline);
- maxChildDescent = Math.max(maxChildDescent, child.getMeasuredHeight() - baseline);
- }
- maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
- maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
- childState = View.combineMeasuredStates(childState, child.getMeasuredState());
- }
- if (maxChildBaseline != -1) {
- maxChildDescent = Math.max(maxChildDescent, getPaddingBottom());
- maxHeight = Math.max(maxHeight, maxChildBaseline + maxChildDescent);
- mBaseline = maxChildBaseline;
- }
- maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
- maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
- setMeasuredDimension(
- View.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
- View.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 parentContentWidth = parentRight - parentLeft;
- final int parentTop = getPaddingTop();
-
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == GONE) {
- continue;
- }
-
- final int width = child.getMeasuredWidth();
- final int height = child.getMeasuredHeight();
-
- final int childLeft = parentLeft + (parentContentWidth - width) / 2;
- final int childTop;
- if (mBaseline != -1 && child.getBaseline() != -1) {
- childTop = parentTop + mBaseline - child.getBaseline();
- } else {
- childTop = parentTop;
- }
-
- child.layout(childLeft, childTop, childLeft + width, childTop + height);
- }
- }
-
- @Override
- public int getBaseline() {
- return mBaseline;
- }
-}
diff --git a/android/support/design/internal/BottomNavigationItemView.java b/android/support/design/internal/BottomNavigationItemView.java
deleted file mode 100644
index fe5e636f..00000000
--- a/android/support/design/internal/BottomNavigationItemView.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2016 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.design.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.design.R;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.view.PointerIconCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.view.menu.MenuItemImpl;
-import android.support.v7.view.menu.MenuView;
-import android.support.v7.widget.TooltipCompat;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class BottomNavigationItemView extends FrameLayout implements MenuView.ItemView {
- public static final int INVALID_ITEM_POSITION = -1;
-
- private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };
-
- private final int mDefaultMargin;
- private final int mShiftAmount;
- private final float mScaleUpFactor;
- private final float mScaleDownFactor;
-
- private boolean mShiftingMode;
-
- private ImageView mIcon;
- private final TextView mSmallLabel;
- private final TextView mLargeLabel;
- private int mItemPosition = INVALID_ITEM_POSITION;
-
- private MenuItemImpl mItemData;
-
- private ColorStateList mIconTint;
-
- public BottomNavigationItemView(@NonNull Context context) {
- this(context, null);
- }
-
- public BottomNavigationItemView(@NonNull Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public BottomNavigationItemView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- final Resources res = getResources();
- int inactiveLabelSize =
- res.getDimensionPixelSize(R.dimen.design_bottom_navigation_text_size);
- int activeLabelSize = res.getDimensionPixelSize(
- R.dimen.design_bottom_navigation_active_text_size);
- mDefaultMargin = res.getDimensionPixelSize(R.dimen.design_bottom_navigation_margin);
- mShiftAmount = inactiveLabelSize - activeLabelSize;
- mScaleUpFactor = 1f * activeLabelSize / inactiveLabelSize;
- mScaleDownFactor = 1f * inactiveLabelSize / activeLabelSize;
-
- LayoutInflater.from(context).inflate(R.layout.design_bottom_navigation_item, this, true);
- setBackgroundResource(R.drawable.design_bottom_navigation_item_background);
- mIcon = findViewById(R.id.icon);
- mSmallLabel = findViewById(R.id.smallLabel);
- mLargeLabel = findViewById(R.id.largeLabel);
- }
-
- @Override
- public void initialize(MenuItemImpl itemData, int menuType) {
- mItemData = itemData;
- setCheckable(itemData.isCheckable());
- setChecked(itemData.isChecked());
- setEnabled(itemData.isEnabled());
- setIcon(itemData.getIcon());
- setTitle(itemData.getTitle());
- setId(itemData.getItemId());
- setContentDescription(itemData.getContentDescription());
- TooltipCompat.setTooltipText(this, itemData.getTooltipText());
- }
-
- public void setItemPosition(int position) {
- mItemPosition = position;
- }
-
- public int getItemPosition() {
- return mItemPosition;
- }
-
- public void setShiftingMode(boolean enabled) {
- mShiftingMode = enabled;
- }
-
- @Override
- public MenuItemImpl getItemData() {
- return mItemData;
- }
-
- @Override
- public void setTitle(CharSequence title) {
- mSmallLabel.setText(title);
- mLargeLabel.setText(title);
- }
-
- @Override
- public void setCheckable(boolean checkable) {
- refreshDrawableState();
- }
-
- @Override
- public void setChecked(boolean checked) {
- mLargeLabel.setPivotX(mLargeLabel.getWidth() / 2);
- mLargeLabel.setPivotY(mLargeLabel.getBaseline());
- mSmallLabel.setPivotX(mSmallLabel.getWidth() / 2);
- mSmallLabel.setPivotY(mSmallLabel.getBaseline());
- if (mShiftingMode) {
- if (checked) {
- LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
- iconParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
- iconParams.topMargin = mDefaultMargin;
- mIcon.setLayoutParams(iconParams);
- mLargeLabel.setVisibility(VISIBLE);
- mLargeLabel.setScaleX(1f);
- mLargeLabel.setScaleY(1f);
- } else {
- LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
- iconParams.gravity = Gravity.CENTER;
- iconParams.topMargin = mDefaultMargin;
- mIcon.setLayoutParams(iconParams);
- mLargeLabel.setVisibility(INVISIBLE);
- mLargeLabel.setScaleX(0.5f);
- mLargeLabel.setScaleY(0.5f);
- }
- mSmallLabel.setVisibility(INVISIBLE);
- } else {
- if (checked) {
- LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
- iconParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
- iconParams.topMargin = mDefaultMargin + mShiftAmount;
- mIcon.setLayoutParams(iconParams);
- mLargeLabel.setVisibility(VISIBLE);
- mSmallLabel.setVisibility(INVISIBLE);
-
- mLargeLabel.setScaleX(1f);
- mLargeLabel.setScaleY(1f);
- mSmallLabel.setScaleX(mScaleUpFactor);
- mSmallLabel.setScaleY(mScaleUpFactor);
- } else {
- LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
- iconParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
- iconParams.topMargin = mDefaultMargin;
- mIcon.setLayoutParams(iconParams);
- mLargeLabel.setVisibility(INVISIBLE);
- mSmallLabel.setVisibility(VISIBLE);
-
- mLargeLabel.setScaleX(mScaleDownFactor);
- mLargeLabel.setScaleY(mScaleDownFactor);
- mSmallLabel.setScaleX(1f);
- mSmallLabel.setScaleY(1f);
- }
- }
-
- refreshDrawableState();
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- mSmallLabel.setEnabled(enabled);
- mLargeLabel.setEnabled(enabled);
- mIcon.setEnabled(enabled);
-
- if (enabled) {
- ViewCompat.setPointerIcon(this,
- PointerIconCompat.getSystemIcon(getContext(), PointerIconCompat.TYPE_HAND));
- } else {
- ViewCompat.setPointerIcon(this, null);
- }
-
- }
-
- @Override
- public int[] onCreateDrawableState(final int extraSpace) {
- final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
- if (mItemData != null && mItemData.isCheckable() && mItemData.isChecked()) {
- mergeDrawableStates(drawableState, CHECKED_STATE_SET);
- }
- return drawableState;
- }
-
- @Override
- public void setShortcut(boolean showShortcut, char shortcutKey) {
- }
-
- @Override
- public void setIcon(Drawable icon) {
- if (icon != null) {
- Drawable.ConstantState state = icon.getConstantState();
- icon = DrawableCompat.wrap(state == null ? icon : state.newDrawable()).mutate();
- DrawableCompat.setTintList(icon, mIconTint);
- }
- mIcon.setImageDrawable(icon);
- }
-
- @Override
- public boolean prefersCondensedTitle() {
- return false;
- }
-
- @Override
- public boolean showsIcon() {
- return true;
- }
-
- public void setIconTintList(ColorStateList tint) {
- mIconTint = tint;
- if (mItemData != null) {
- // Update the icon so that the tint takes effect
- setIcon(mItemData.getIcon());
- }
- }
-
- public void setTextColor(ColorStateList color) {
- mSmallLabel.setTextColor(color);
- mLargeLabel.setTextColor(color);
- }
-
- public void setItemBackground(int background) {
- Drawable backgroundDrawable = background == 0
- ? null : ContextCompat.getDrawable(getContext(), background);
- ViewCompat.setBackground(this, backgroundDrawable);
- }
-}
diff --git a/android/support/design/internal/BottomNavigationMenu.java b/android/support/design/internal/BottomNavigationMenu.java
deleted file mode 100644
index a86d2adf..00000000
--- a/android/support/design/internal/BottomNavigationMenu.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2016 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.design.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuItemImpl;
-import android.view.MenuItem;
-import android.view.SubMenu;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public final class BottomNavigationMenu extends MenuBuilder {
- public static final int MAX_ITEM_COUNT = 5;
-
- public BottomNavigationMenu(Context context) {
- super(context);
- }
-
- @Override
- public SubMenu addSubMenu(int group, int id, int categoryOrder, CharSequence title) {
- throw new UnsupportedOperationException("BottomNavigationView does not support submenus");
- }
-
- @Override
- protected MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
- if (size() + 1 > MAX_ITEM_COUNT) {
- throw new IllegalArgumentException(
- "Maximum number of items supported by BottomNavigationView is " + MAX_ITEM_COUNT
- + ". Limit can be checked with BottomNavigationView#getMaxItemCount()");
- }
- stopDispatchingItemsChanged();
- final MenuItem item = super.addInternal(group, id, categoryOrder, title);
- if (item instanceof MenuItemImpl) {
- ((MenuItemImpl) item).setExclusiveCheckable(true);
- }
- startDispatchingItemsChanged();
- return item;
- }
-}
diff --git a/android/support/design/internal/BottomNavigationMenuView.java b/android/support/design/internal/BottomNavigationMenuView.java
deleted file mode 100644
index bf33454e..00000000
--- a/android/support/design/internal/BottomNavigationMenuView.java
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * Copyright (C) 2016 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.design.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.design.R;
-import android.support.transition.AutoTransition;
-import android.support.transition.TransitionManager;
-import android.support.transition.TransitionSet;
-import android.support.v4.util.Pools;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuItemImpl;
-import android.support.v7.view.menu.MenuView;
-import android.util.AttributeSet;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * @hide For internal use only.
- */
-@RestrictTo(LIBRARY_GROUP)
-public class BottomNavigationMenuView extends ViewGroup implements MenuView {
- private static final long ACTIVE_ANIMATION_DURATION_MS = 115L;
-
- private final TransitionSet mSet;
- private final int mInactiveItemMaxWidth;
- private final int mInactiveItemMinWidth;
- private final int mActiveItemMaxWidth;
- private final int mItemHeight;
- private final OnClickListener mOnClickListener;
- private final Pools.Pool<BottomNavigationItemView> mItemPool = new Pools.SynchronizedPool<>(5);
-
- private boolean mShiftingMode = true;
-
- private BottomNavigationItemView[] mButtons;
- private int mSelectedItemId = 0;
- private int mSelectedItemPosition = 0;
- private ColorStateList mItemIconTint;
- private ColorStateList mItemTextColor;
- private int mItemBackgroundRes;
- private int[] mTempChildWidths;
-
- private BottomNavigationPresenter mPresenter;
- private MenuBuilder mMenu;
-
- public BottomNavigationMenuView(Context context) {
- this(context, null);
- }
-
- public BottomNavigationMenuView(Context context, AttributeSet attrs) {
- super(context, attrs);
- final Resources res = getResources();
- mInactiveItemMaxWidth = res.getDimensionPixelSize(
- R.dimen.design_bottom_navigation_item_max_width);
- mInactiveItemMinWidth = res.getDimensionPixelSize(
- R.dimen.design_bottom_navigation_item_min_width);
- mActiveItemMaxWidth = res.getDimensionPixelSize(
- R.dimen.design_bottom_navigation_active_item_max_width);
- mItemHeight = res.getDimensionPixelSize(R.dimen.design_bottom_navigation_height);
-
- mSet = new AutoTransition();
- mSet.setOrdering(TransitionSet.ORDERING_TOGETHER);
- mSet.setDuration(ACTIVE_ANIMATION_DURATION_MS);
- mSet.setInterpolator(new FastOutSlowInInterpolator());
- mSet.addTransition(new TextScale());
-
- mOnClickListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- final BottomNavigationItemView itemView = (BottomNavigationItemView) v;
- MenuItem item = itemView.getItemData();
- if (!mMenu.performItemAction(item, mPresenter, 0)) {
- item.setChecked(true);
- }
- }
- };
- mTempChildWidths = new int[BottomNavigationMenu.MAX_ITEM_COUNT];
- }
-
- @Override
- public void initialize(MenuBuilder menu) {
- mMenu = menu;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int width = MeasureSpec.getSize(widthMeasureSpec);
- final int count = getChildCount();
-
- final int heightSpec = MeasureSpec.makeMeasureSpec(mItemHeight, MeasureSpec.EXACTLY);
-
- if (mShiftingMode) {
- final int inactiveCount = count - 1;
- final int activeMaxAvailable = width - inactiveCount * mInactiveItemMinWidth;
- final int activeWidth = Math.min(activeMaxAvailable, mActiveItemMaxWidth);
- final int inactiveMaxAvailable = (width - activeWidth) / inactiveCount;
- final int inactiveWidth = Math.min(inactiveMaxAvailable, mInactiveItemMaxWidth);
- int extra = width - activeWidth - inactiveWidth * inactiveCount;
- for (int i = 0; i < count; i++) {
- mTempChildWidths[i] = (i == mSelectedItemPosition) ? activeWidth : inactiveWidth;
- if (extra > 0) {
- mTempChildWidths[i]++;
- extra--;
- }
- }
- } else {
- final int maxAvailable = width / (count == 0 ? 1 : count);
- final int childWidth = Math.min(maxAvailable, mActiveItemMaxWidth);
- int extra = width - childWidth * count;
- for (int i = 0; i < count; i++) {
- mTempChildWidths[i] = childWidth;
- if (extra > 0) {
- mTempChildWidths[i]++;
- extra--;
- }
- }
- }
-
- int totalWidth = 0;
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == GONE) {
- continue;
- }
- child.measure(MeasureSpec.makeMeasureSpec(mTempChildWidths[i], MeasureSpec.EXACTLY),
- heightSpec);
- ViewGroup.LayoutParams params = child.getLayoutParams();
- params.width = child.getMeasuredWidth();
- totalWidth += child.getMeasuredWidth();
- }
- setMeasuredDimension(
- View.resolveSizeAndState(totalWidth,
- MeasureSpec.makeMeasureSpec(totalWidth, MeasureSpec.EXACTLY), 0),
- View.resolveSizeAndState(mItemHeight, heightSpec, 0));
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- final int count = getChildCount();
- final int width = right - left;
- final int height = bottom - top;
- int used = 0;
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() == GONE) {
- continue;
- }
- if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL) {
- child.layout(width - used - child.getMeasuredWidth(), 0, width - used, height);
- } else {
- child.layout(used, 0, child.getMeasuredWidth() + used, height);
- }
- used += child.getMeasuredWidth();
- }
- }
-
- @Override
- public int getWindowAnimations() {
- return 0;
- }
-
- /**
- * Sets the tint which is applied to the menu items' icons.
- *
- * @param tint the tint to apply
- */
- public void setIconTintList(ColorStateList tint) {
- mItemIconTint = tint;
- if (mButtons == null) return;
- for (BottomNavigationItemView item : mButtons) {
- item.setIconTintList(tint);
- }
- }
-
- /**
- * Returns the tint which is applied to menu items' icons.
- *
- * @return the ColorStateList that is used to tint menu items' icons
- */
- @Nullable
- public ColorStateList getIconTintList() {
- return mItemIconTint;
- }
-
- /**
- * Sets the text color to be used on menu items.
- *
- * @param color the ColorStateList used for menu items' text.
- */
- public void setItemTextColor(ColorStateList color) {
- mItemTextColor = color;
- if (mButtons == null) return;
- for (BottomNavigationItemView item : mButtons) {
- item.setTextColor(color);
- }
- }
-
- /**
- * Returns the text color used on menu items.
- *
- * @return the ColorStateList used for menu items' text
- */
- public ColorStateList getItemTextColor() {
- return mItemTextColor;
- }
-
- /**
- * Sets the resource ID to be used for item background.
- *
- * @param background the resource ID of the background
- */
- public void setItemBackgroundRes(int background) {
- mItemBackgroundRes = background;
- if (mButtons == null) return;
- for (BottomNavigationItemView item : mButtons) {
- item.setItemBackground(background);
- }
- }
-
- /**
- * Returns the resource ID for the background of the menu items.
- *
- * @return the resource ID for the background
- */
- public int getItemBackgroundRes() {
- return mItemBackgroundRes;
- }
-
- public void setPresenter(BottomNavigationPresenter presenter) {
- mPresenter = presenter;
- }
-
- public void buildMenuView() {
- removeAllViews();
- if (mButtons != null) {
- for (BottomNavigationItemView item : mButtons) {
- mItemPool.release(item);
- }
- }
- if (mMenu.size() == 0) {
- mSelectedItemId = 0;
- mSelectedItemPosition = 0;
- mButtons = null;
- return;
- }
- mButtons = new BottomNavigationItemView[mMenu.size()];
- mShiftingMode = mMenu.size() > 3;
- for (int i = 0; i < mMenu.size(); i++) {
- mPresenter.setUpdateSuspended(true);
- mMenu.getItem(i).setCheckable(true);
- mPresenter.setUpdateSuspended(false);
- BottomNavigationItemView child = getNewItem();
- mButtons[i] = child;
- child.setIconTintList(mItemIconTint);
- child.setTextColor(mItemTextColor);
- child.setItemBackground(mItemBackgroundRes);
- child.setShiftingMode(mShiftingMode);
- child.initialize((MenuItemImpl) mMenu.getItem(i), 0);
- child.setItemPosition(i);
- child.setOnClickListener(mOnClickListener);
- addView(child);
- }
- mSelectedItemPosition = Math.min(mMenu.size() - 1, mSelectedItemPosition);
- mMenu.getItem(mSelectedItemPosition).setChecked(true);
- }
-
- public void updateMenuView() {
- final int menuSize = mMenu.size();
- if (menuSize != mButtons.length) {
- // The size has changed. Rebuild menu view from scratch.
- buildMenuView();
- return;
- }
- int previousSelectedId = mSelectedItemId;
-
- for (int i = 0; i < menuSize; i++) {
- MenuItem item = mMenu.getItem(i);
- if (item.isChecked()) {
- mSelectedItemId = item.getItemId();
- mSelectedItemPosition = i;
- }
- }
- if (previousSelectedId != mSelectedItemId) {
- // Note: this has to be called before BottomNavigationItemView#initialize().
- TransitionManager.beginDelayedTransition(this, mSet);
- }
-
- for (int i = 0; i < menuSize; i++) {
- mPresenter.setUpdateSuspended(true);
- mButtons[i].initialize((MenuItemImpl) mMenu.getItem(i), 0);
- mPresenter.setUpdateSuspended(false);
- }
-
- }
-
- private BottomNavigationItemView getNewItem() {
- BottomNavigationItemView item = mItemPool.acquire();
- if (item == null) {
- item = new BottomNavigationItemView(getContext());
- }
- return item;
- }
-
- public int getSelectedItemId() {
- return mSelectedItemId;
- }
-
- void tryRestoreSelectedItemId(int itemId) {
- final int size = mMenu.size();
- for (int i = 0; i < size; i++) {
- MenuItem item = mMenu.getItem(i);
- if (itemId == item.getItemId()) {
- mSelectedItemId = itemId;
- mSelectedItemPosition = i;
- item.setChecked(true);
- break;
- }
- }
- }
-}
diff --git a/android/support/design/internal/BottomNavigationPresenter.java b/android/support/design/internal/BottomNavigationPresenter.java
deleted file mode 100644
index 1343a4bf..00000000
--- a/android/support/design/internal/BottomNavigationPresenter.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2016 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.design.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuItemImpl;
-import android.support.v7.view.menu.MenuPresenter;
-import android.support.v7.view.menu.MenuView;
-import android.support.v7.view.menu.SubMenuBuilder;
-import android.view.ViewGroup;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class BottomNavigationPresenter implements MenuPresenter {
- private MenuBuilder mMenu;
- private BottomNavigationMenuView mMenuView;
- private boolean mUpdateSuspended = false;
- private int mId;
-
- public void setBottomNavigationMenuView(BottomNavigationMenuView menuView) {
- mMenuView = menuView;
- }
-
- @Override
- public void initForMenu(Context context, MenuBuilder menu) {
- mMenuView.initialize(mMenu);
- mMenu = menu;
- }
-
- @Override
- public MenuView getMenuView(ViewGroup root) {
- return mMenuView;
- }
-
- @Override
- public void updateMenuView(boolean cleared) {
- if (mUpdateSuspended) return;
- if (cleared) {
- mMenuView.buildMenuView();
- } else {
- mMenuView.updateMenuView();
- }
- }
-
- @Override
- public void setCallback(Callback cb) {}
-
- @Override
- public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
- return false;
- }
-
- @Override
- public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {}
-
- @Override
- public boolean flagActionItems() {
- return false;
- }
-
- @Override
- public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
- return false;
- }
-
- @Override
- public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
- return false;
- }
-
- public void setId(int id) {
- mId = id;
- }
-
- @Override
- public int getId() {
- return mId;
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- SavedState savedState = new SavedState();
- savedState.selectedItemId = mMenuView.getSelectedItemId();
- return savedState;
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- if (state instanceof SavedState) {
- mMenuView.tryRestoreSelectedItemId(((SavedState) state).selectedItemId);
- }
- }
-
- public void setUpdateSuspended(boolean updateSuspended) {
- mUpdateSuspended = updateSuspended;
- }
-
- static class SavedState implements Parcelable {
- int selectedItemId;
-
- SavedState() {}
-
- SavedState(Parcel in) {
- selectedItemId = in.readInt();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel out, int flags) {
- out.writeInt(selectedItemId);
- }
-
- public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
- @Override
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
-
- @Override
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-}
diff --git a/android/support/design/internal/ForegroundLinearLayout.java b/android/support/design/internal/ForegroundLinearLayout.java
deleted file mode 100644
index 6d905038..00000000
--- a/android/support/design/internal/ForegroundLinearLayout.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * 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.design.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.support.design.R;
-import android.support.v7.widget.LinearLayoutCompat;
-import android.util.AttributeSet;
-import android.view.Gravity;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ForegroundLinearLayout extends LinearLayoutCompat {
-
- private Drawable mForeground;
-
- private final Rect mSelfBounds = new Rect();
-
- private final Rect mOverlayBounds = new Rect();
-
- private int mForegroundGravity = Gravity.FILL;
-
- protected boolean mForegroundInPadding = true;
-
- boolean mForegroundBoundsChanged = false;
-
- public ForegroundLinearLayout(Context context) {
- this(context, null);
- }
-
- public ForegroundLinearLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public ForegroundLinearLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ForegroundLinearLayout,
- defStyle, 0);
-
- mForegroundGravity = a.getInt(
- R.styleable.ForegroundLinearLayout_android_foregroundGravity, mForegroundGravity);
-
- final Drawable d = a.getDrawable(R.styleable.ForegroundLinearLayout_android_foreground);
- if (d != null) {
- setForeground(d);
- }
-
- mForegroundInPadding = a.getBoolean(
- R.styleable.ForegroundLinearLayout_foregroundInsidePadding, true);
-
- a.recycle();
- }
-
- /**
- * Describes how the foreground is positioned.
- *
- * @return foreground gravity.
- * @see #setForegroundGravity(int)
- */
- @Override
- public int getForegroundGravity() {
- return mForegroundGravity;
- }
-
- /**
- * Describes how the foreground is positioned. Defaults to START and TOP.
- *
- * @param foregroundGravity See {@link android.view.Gravity}
- * @see #getForegroundGravity()
- */
- @Override
- public void setForegroundGravity(int foregroundGravity) {
- if (mForegroundGravity != foregroundGravity) {
- if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
- foregroundGravity |= Gravity.START;
- }
-
- if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
- foregroundGravity |= Gravity.TOP;
- }
-
- mForegroundGravity = foregroundGravity;
-
- if (mForegroundGravity == Gravity.FILL && mForeground != null) {
- Rect padding = new Rect();
- mForeground.getPadding(padding);
- }
-
- requestLayout();
- }
- }
-
- @Override
- protected boolean verifyDrawable(Drawable who) {
- return super.verifyDrawable(who) || (who == mForeground);
- }
-
- @RequiresApi(11)
- @Override
- public void jumpDrawablesToCurrentState() {
- super.jumpDrawablesToCurrentState();
- if (mForeground != null) {
- mForeground.jumpToCurrentState();
- }
- }
-
- @Override
- protected void drawableStateChanged() {
- super.drawableStateChanged();
- if (mForeground != null && mForeground.isStateful()) {
- mForeground.setState(getDrawableState());
- }
- }
-
- /**
- * Supply a Drawable that is to be rendered on top of all of the child
- * views in the frame layout. Any padding in the Drawable will be taken
- * into account by ensuring that the children are inset to be placed
- * inside of the padding area.
- *
- * @param drawable The Drawable to be drawn on top of the children.
- */
- @Override
- public void setForeground(Drawable drawable) {
- if (mForeground != drawable) {
- if (mForeground != null) {
- mForeground.setCallback(null);
- unscheduleDrawable(mForeground);
- }
-
- mForeground = drawable;
-
- if (drawable != null) {
- setWillNotDraw(false);
- drawable.setCallback(this);
- if (drawable.isStateful()) {
- drawable.setState(getDrawableState());
- }
- if (mForegroundGravity == Gravity.FILL) {
- Rect padding = new Rect();
- drawable.getPadding(padding);
- }
- } else {
- setWillNotDraw(true);
- }
- requestLayout();
- invalidate();
- }
- }
-
- /**
- * Returns the drawable used as the foreground of this FrameLayout. The
- * foreground drawable, if non-null, is always drawn on top of the children.
- *
- * @return A Drawable or null if no foreground was set.
- */
- @Override
- public Drawable getForeground() {
- return mForeground;
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- mForegroundBoundsChanged |= changed;
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mForegroundBoundsChanged = true;
- }
-
- @Override
- public void draw(@NonNull Canvas canvas) {
- super.draw(canvas);
-
- if (mForeground != null) {
- final Drawable foreground = mForeground;
-
- if (mForegroundBoundsChanged) {
- mForegroundBoundsChanged = false;
- final Rect selfBounds = mSelfBounds;
- final Rect overlayBounds = mOverlayBounds;
-
- final int w = getRight() - getLeft();
- final int h = getBottom() - getTop();
-
- if (mForegroundInPadding) {
- selfBounds.set(0, 0, w, h);
- } else {
- selfBounds.set(getPaddingLeft(), getPaddingTop(),
- w - getPaddingRight(), h - getPaddingBottom());
- }
-
- Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
- foreground.getIntrinsicHeight(), selfBounds, overlayBounds);
- foreground.setBounds(overlayBounds);
- }
-
- foreground.draw(canvas);
- }
- }
-
- @RequiresApi(21)
- @Override
- public void drawableHotspotChanged(float x, float y) {
- super.drawableHotspotChanged(x, y);
- if (mForeground != null) {
- mForeground.setHotspot(x, y);
- }
- }
-
-}
diff --git a/android/support/design/internal/NavigationMenu.java b/android/support/design/internal/NavigationMenu.java
deleted file mode 100644
index a0ec5e0d..00000000
--- a/android/support/design/internal/NavigationMenu.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.design.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuItemImpl;
-import android.support.v7.view.menu.SubMenuBuilder;
-import android.view.SubMenu;
-
-/**
- * This is a {@link MenuBuilder} that returns an instance of {@link NavigationSubMenu} instead of
- * {@link SubMenuBuilder} when a sub menu is created.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class NavigationMenu extends MenuBuilder {
-
- public NavigationMenu(Context context) {
- super(context);
- }
-
- @Override
- public SubMenu addSubMenu(int group, int id, int categoryOrder, CharSequence title) {
- final MenuItemImpl item = (MenuItemImpl) addInternal(group, id, categoryOrder, title);
- final SubMenuBuilder subMenu = new NavigationSubMenu(getContext(), this, item);
- item.setSubMenu(subMenu);
- return subMenu;
- }
-
-}
diff --git a/android/support/design/internal/NavigationMenuItemView.java b/android/support/design/internal/NavigationMenuItemView.java
deleted file mode 100644
index eea9e90f..00000000
--- a/android/support/design/internal/NavigationMenuItemView.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * 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.design.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
-import android.support.annotation.RestrictTo;
-import android.support.design.R;
-import android.support.v4.content.res.ResourcesCompat;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.widget.TextViewCompat;
-import android.support.v7.view.menu.MenuItemImpl;
-import android.support.v7.view.menu.MenuView;
-import android.support.v7.widget.TooltipCompat;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewStub;
-import android.widget.CheckedTextView;
-import android.widget.FrameLayout;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class NavigationMenuItemView extends ForegroundLinearLayout implements MenuView.ItemView {
-
- private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
-
- private final int mIconSize;
-
- private boolean mNeedsEmptyIcon;
-
- boolean mCheckable;
-
- private final CheckedTextView mTextView;
-
- private FrameLayout mActionArea;
-
- private MenuItemImpl mItemData;
-
- private ColorStateList mIconTintList;
-
- private boolean mHasIconTintList;
-
- private Drawable mEmptyDrawable;
-
- private final AccessibilityDelegateCompat mAccessibilityDelegate
- = new AccessibilityDelegateCompat() {
-
- @Override
- public void onInitializeAccessibilityNodeInfo(View host,
- AccessibilityNodeInfoCompat info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- info.setCheckable(mCheckable);
- }
-
- };
-
- public NavigationMenuItemView(Context context) {
- this(context, null);
- }
-
- public NavigationMenuItemView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public NavigationMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- setOrientation(HORIZONTAL);
- LayoutInflater.from(context).inflate(R.layout.design_navigation_menu_item, this, true);
- mIconSize = context.getResources().getDimensionPixelSize(
- R.dimen.design_navigation_icon_size);
- mTextView = findViewById(R.id.design_menu_item_text);
- mTextView.setDuplicateParentStateEnabled(true);
- ViewCompat.setAccessibilityDelegate(mTextView, mAccessibilityDelegate);
- }
-
- @Override
- public void initialize(MenuItemImpl itemData, int menuType) {
- mItemData = itemData;
-
- setVisibility(itemData.isVisible() ? VISIBLE : GONE);
-
- if (getBackground() == null) {
- ViewCompat.setBackground(this, createDefaultBackground());
- }
-
- setCheckable(itemData.isCheckable());
- setChecked(itemData.isChecked());
- setEnabled(itemData.isEnabled());
- setTitle(itemData.getTitle());
- setIcon(itemData.getIcon());
- setActionView(itemData.getActionView());
- setContentDescription(itemData.getContentDescription());
- TooltipCompat.setTooltipText(this, itemData.getTooltipText());
- adjustAppearance();
- }
-
- private boolean shouldExpandActionArea() {
- return mItemData.getTitle() == null &&
- mItemData.getIcon() == null &&
- mItemData.getActionView() != null;
- }
-
- private void adjustAppearance() {
- if (shouldExpandActionArea()) {
- // Expand the actionView area
- mTextView.setVisibility(View.GONE);
- if (mActionArea != null) {
- LayoutParams params = (LayoutParams) mActionArea.getLayoutParams();
- params.width = LayoutParams.MATCH_PARENT;
- mActionArea.setLayoutParams(params);
- }
- } else {
- mTextView.setVisibility(View.VISIBLE);
- if (mActionArea != null) {
- LayoutParams params = (LayoutParams) mActionArea.getLayoutParams();
- params.width = LayoutParams.WRAP_CONTENT;
- mActionArea.setLayoutParams(params);
- }
- }
- }
-
- public void recycle() {
- if (mActionArea != null) {
- mActionArea.removeAllViews();
- }
- mTextView.setCompoundDrawables(null, null, null, null);
- }
-
- private void setActionView(View actionView) {
- if (actionView != null) {
- if (mActionArea == null) {
- mActionArea = (FrameLayout) ((ViewStub) findViewById(
- R.id.design_menu_item_action_area_stub)).inflate();
- }
- mActionArea.removeAllViews();
- mActionArea.addView(actionView);
- }
- }
-
- private StateListDrawable createDefaultBackground() {
- TypedValue value = new TypedValue();
- if (getContext().getTheme().resolveAttribute(
- android.support.v7.appcompat.R.attr.colorControlHighlight, value, true)) {
- StateListDrawable drawable = new StateListDrawable();
- drawable.addState(CHECKED_STATE_SET, new ColorDrawable(value.data));
- drawable.addState(EMPTY_STATE_SET, new ColorDrawable(Color.TRANSPARENT));
- return drawable;
- }
- return null;
- }
-
- @Override
- public MenuItemImpl getItemData() {
- return mItemData;
- }
-
- @Override
- public void setTitle(CharSequence title) {
- mTextView.setText(title);
- }
-
- @Override
- public void setCheckable(boolean checkable) {
- refreshDrawableState();
- if (mCheckable != checkable) {
- mCheckable = checkable;
- mAccessibilityDelegate.sendAccessibilityEvent(mTextView,
- AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
- }
- }
-
- @Override
- public void setChecked(boolean checked) {
- refreshDrawableState();
- mTextView.setChecked(checked);
- }
-
- @Override
- public void setShortcut(boolean showShortcut, char shortcutKey) {
- }
-
- @Override
- public void setIcon(Drawable icon) {
- if (icon != null) {
- if (mHasIconTintList) {
- Drawable.ConstantState state = icon.getConstantState();
- icon = DrawableCompat.wrap(state == null ? icon : state.newDrawable()).mutate();
- DrawableCompat.setTintList(icon, mIconTintList);
- }
- icon.setBounds(0, 0, mIconSize, mIconSize);
- } else if (mNeedsEmptyIcon) {
- if (mEmptyDrawable == null) {
- mEmptyDrawable = ResourcesCompat.getDrawable(getResources(),
- R.drawable.navigation_empty_icon, getContext().getTheme());
- if (mEmptyDrawable != null) {
- mEmptyDrawable.setBounds(0, 0, mIconSize, mIconSize);
- }
- }
- icon = mEmptyDrawable;
- }
- TextViewCompat.setCompoundDrawablesRelative(mTextView, icon, null, null, null);
- }
-
- @Override
- public boolean prefersCondensedTitle() {
- return false;
- }
-
- @Override
- public boolean showsIcon() {
- return true;
- }
-
- @Override
- protected int[] onCreateDrawableState(int extraSpace) {
- final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
- if (mItemData != null && mItemData.isCheckable() && mItemData.isChecked()) {
- mergeDrawableStates(drawableState, CHECKED_STATE_SET);
- }
- return drawableState;
- }
-
- void setIconTintList(ColorStateList tintList) {
- mIconTintList = tintList;
- mHasIconTintList = mIconTintList != null;
- if (mItemData != null) {
- // Update the icon so that the tint takes effect
- setIcon(mItemData.getIcon());
- }
- }
-
- public void setTextAppearance(int textAppearance) {
- TextViewCompat.setTextAppearance(mTextView, textAppearance);
- }
-
- public void setTextColor(ColorStateList colors) {
- mTextView.setTextColor(colors);
- }
-
- public void setNeedsEmptyIcon(boolean needsEmptyIcon) {
- mNeedsEmptyIcon = needsEmptyIcon;
- }
-
-}
diff --git a/android/support/design/internal/NavigationMenuPresenter.java b/android/support/design/internal/NavigationMenuPresenter.java
deleted file mode 100644
index 98ad4688..00000000
--- a/android/support/design/internal/NavigationMenuPresenter.java
+++ /dev/null
@@ -1,686 +0,0 @@
-/*
- * 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.design.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StyleRes;
-import android.support.design.R;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.WindowInsetsCompat;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuItemImpl;
-import android.support.v7.view.menu.MenuPresenter;
-import android.support.v7.view.menu.MenuView;
-import android.support.v7.view.menu.SubMenuBuilder;
-import android.support.v7.widget.RecyclerView;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.SubMenu;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class NavigationMenuPresenter implements MenuPresenter {
-
- private static final String STATE_HIERARCHY = "android:menu:list";
- private static final String STATE_ADAPTER = "android:menu:adapter";
- private static final String STATE_HEADER = "android:menu:header";
-
- private NavigationMenuView mMenuView;
- LinearLayout mHeaderLayout;
-
- private Callback mCallback;
- MenuBuilder mMenu;
- private int mId;
-
- NavigationMenuAdapter mAdapter;
- LayoutInflater mLayoutInflater;
-
- int mTextAppearance;
- boolean mTextAppearanceSet;
- ColorStateList mTextColor;
- ColorStateList mIconTintList;
- Drawable mItemBackground;
-
- /**
- * Padding to be inserted at the top of the list to avoid the first menu item
- * from being placed underneath the status bar.
- */
- private int mPaddingTopDefault;
-
- /**
- * Padding for separators between items
- */
- int mPaddingSeparator;
-
- @Override
- public void initForMenu(Context context, MenuBuilder menu) {
- mLayoutInflater = LayoutInflater.from(context);
- mMenu = menu;
- Resources res = context.getResources();
- mPaddingSeparator = res.getDimensionPixelOffset(
- R.dimen.design_navigation_separator_vertical_padding);
- }
-
- @Override
- public MenuView getMenuView(ViewGroup root) {
- if (mMenuView == null) {
- mMenuView = (NavigationMenuView) mLayoutInflater.inflate(
- R.layout.design_navigation_menu, root, false);
- if (mAdapter == null) {
- mAdapter = new NavigationMenuAdapter();
- }
- mHeaderLayout = (LinearLayout) mLayoutInflater
- .inflate(R.layout.design_navigation_item_header,
- mMenuView, false);
- mMenuView.setAdapter(mAdapter);
- }
- return mMenuView;
- }
-
- @Override
- public void updateMenuView(boolean cleared) {
- if (mAdapter != null) {
- mAdapter.update();
- }
- }
-
- @Override
- public void setCallback(Callback cb) {
- mCallback = cb;
- }
-
- @Override
- public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
- return false;
- }
-
- @Override
- public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
- if (mCallback != null) {
- mCallback.onCloseMenu(menu, allMenusAreClosing);
- }
- }
-
- @Override
- public boolean flagActionItems() {
- return false;
- }
-
- @Override
- public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
- return false;
- }
-
- @Override
- public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
- return false;
- }
-
- @Override
- public int getId() {
- return mId;
- }
-
- public void setId(int id) {
- mId = id;
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- if (Build.VERSION.SDK_INT >= 11) {
- // API 9-10 does not support ClassLoaderCreator, therefore things can crash if they're
- // loaded via different loaders. Rather than crash we just won't save state on those
- // platforms
- final Bundle state = new Bundle();
- if (mMenuView != null) {
- SparseArray<Parcelable> hierarchy = new SparseArray<>();
- mMenuView.saveHierarchyState(hierarchy);
- state.putSparseParcelableArray(STATE_HIERARCHY, hierarchy);
- }
- if (mAdapter != null) {
- state.putBundle(STATE_ADAPTER, mAdapter.createInstanceState());
- }
- if (mHeaderLayout != null) {
- SparseArray<Parcelable> header = new SparseArray<>();
- mHeaderLayout.saveHierarchyState(header);
- state.putSparseParcelableArray(STATE_HEADER, header);
- }
- return state;
- }
- return null;
- }
-
- @Override
- public void onRestoreInstanceState(final Parcelable parcelable) {
- if (parcelable instanceof Bundle) {
- Bundle state = (Bundle) parcelable;
- SparseArray<Parcelable> hierarchy = state.getSparseParcelableArray(STATE_HIERARCHY);
- if (hierarchy != null) {
- mMenuView.restoreHierarchyState(hierarchy);
- }
- Bundle adapterState = state.getBundle(STATE_ADAPTER);
- if (adapterState != null) {
- mAdapter.restoreInstanceState(adapterState);
- }
- SparseArray<Parcelable> header = state.getSparseParcelableArray(STATE_HEADER);
- if (header != null) {
- mHeaderLayout.restoreHierarchyState(header);
- }
- }
- }
-
- public void setCheckedItem(MenuItemImpl item) {
- mAdapter.setCheckedItem(item);
- }
-
- public View inflateHeaderView(@LayoutRes int res) {
- View view = mLayoutInflater.inflate(res, mHeaderLayout, false);
- addHeaderView(view);
- return view;
- }
-
- public void addHeaderView(@NonNull View view) {
- mHeaderLayout.addView(view);
- // The padding on top should be cleared.
- mMenuView.setPadding(0, 0, 0, mMenuView.getPaddingBottom());
- }
-
- public void removeHeaderView(@NonNull View view) {
- mHeaderLayout.removeView(view);
- if (mHeaderLayout.getChildCount() == 0) {
- mMenuView.setPadding(0, mPaddingTopDefault, 0, mMenuView.getPaddingBottom());
- }
- }
-
- public int getHeaderCount() {
- return mHeaderLayout.getChildCount();
- }
-
- public View getHeaderView(int index) {
- return mHeaderLayout.getChildAt(index);
- }
-
- @Nullable
- public ColorStateList getItemTintList() {
- return mIconTintList;
- }
-
- public void setItemIconTintList(@Nullable ColorStateList tint) {
- mIconTintList = tint;
- updateMenuView(false);
- }
-
- @Nullable
- public ColorStateList getItemTextColor() {
- return mTextColor;
- }
-
- public void setItemTextColor(@Nullable ColorStateList textColor) {
- mTextColor = textColor;
- updateMenuView(false);
- }
-
- public void setItemTextAppearance(@StyleRes int resId) {
- mTextAppearance = resId;
- mTextAppearanceSet = true;
- updateMenuView(false);
- }
-
- @Nullable
- public Drawable getItemBackground() {
- return mItemBackground;
- }
-
- public void setItemBackground(@Nullable Drawable itemBackground) {
- mItemBackground = itemBackground;
- updateMenuView(false);
- }
-
- public void setUpdateSuspended(boolean updateSuspended) {
- if (mAdapter != null) {
- mAdapter.setUpdateSuspended(updateSuspended);
- }
- }
-
- public void dispatchApplyWindowInsets(WindowInsetsCompat insets) {
- int top = insets.getSystemWindowInsetTop();
- if (mPaddingTopDefault != top) {
- mPaddingTopDefault = top;
- if (mHeaderLayout.getChildCount() == 0) {
- mMenuView.setPadding(0, mPaddingTopDefault, 0, mMenuView.getPaddingBottom());
- }
- }
- ViewCompat.dispatchApplyWindowInsets(mHeaderLayout, insets);
- }
-
- private abstract static class ViewHolder extends RecyclerView.ViewHolder {
-
- public ViewHolder(View itemView) {
- super(itemView);
- }
-
- }
-
- private static class NormalViewHolder extends ViewHolder {
-
- public NormalViewHolder(LayoutInflater inflater, ViewGroup parent,
- View.OnClickListener listener) {
- super(inflater.inflate(R.layout.design_navigation_item, parent, false));
- itemView.setOnClickListener(listener);
- }
-
- }
-
- private static class SubheaderViewHolder extends ViewHolder {
-
- public SubheaderViewHolder(LayoutInflater inflater, ViewGroup parent) {
- super(inflater.inflate(R.layout.design_navigation_item_subheader, parent, false));
- }
-
- }
-
- private static class SeparatorViewHolder extends ViewHolder {
-
- public SeparatorViewHolder(LayoutInflater inflater, ViewGroup parent) {
- super(inflater.inflate(R.layout.design_navigation_item_separator, parent, false));
- }
-
- }
-
- private static class HeaderViewHolder extends ViewHolder {
-
- public HeaderViewHolder(View itemView) {
- super(itemView);
- }
-
- }
-
- /**
- * Handles click events for the menu items. The items has to be {@link NavigationMenuItemView}.
- */
- final View.OnClickListener mOnClickListener = new View.OnClickListener() {
-
- @Override
- public void onClick(View v) {
- NavigationMenuItemView itemView = (NavigationMenuItemView) v;
- setUpdateSuspended(true);
- MenuItemImpl item = itemView.getItemData();
- boolean result = mMenu.performItemAction(item, NavigationMenuPresenter.this, 0);
- if (item != null && item.isCheckable() && result) {
- mAdapter.setCheckedItem(item);
- }
- setUpdateSuspended(false);
- updateMenuView(false);
- }
-
- };
-
- private class NavigationMenuAdapter extends RecyclerView.Adapter<ViewHolder> {
-
- private static final String STATE_CHECKED_ITEM = "android:menu:checked";
-
- private static final String STATE_ACTION_VIEWS = "android:menu:action_views";
- private static final int VIEW_TYPE_NORMAL = 0;
- private static final int VIEW_TYPE_SUBHEADER = 1;
- private static final int VIEW_TYPE_SEPARATOR = 2;
- private static final int VIEW_TYPE_HEADER = 3;
-
- private final ArrayList<NavigationMenuItem> mItems = new ArrayList<>();
- private MenuItemImpl mCheckedItem;
- private boolean mUpdateSuspended;
-
- NavigationMenuAdapter() {
- prepareMenuItems();
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public int getItemCount() {
- return mItems.size();
- }
-
- @Override
- public int getItemViewType(int position) {
- NavigationMenuItem item = mItems.get(position);
- if (item instanceof NavigationMenuSeparatorItem) {
- return VIEW_TYPE_SEPARATOR;
- } else if (item instanceof NavigationMenuHeaderItem) {
- return VIEW_TYPE_HEADER;
- } else if (item instanceof NavigationMenuTextItem) {
- NavigationMenuTextItem textItem = (NavigationMenuTextItem) item;
- if (textItem.getMenuItem().hasSubMenu()) {
- return VIEW_TYPE_SUBHEADER;
- } else {
- return VIEW_TYPE_NORMAL;
- }
- }
- throw new RuntimeException("Unknown item type.");
- }
-
- @Override
- public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- switch (viewType) {
- case VIEW_TYPE_NORMAL:
- return new NormalViewHolder(mLayoutInflater, parent, mOnClickListener);
- case VIEW_TYPE_SUBHEADER:
- return new SubheaderViewHolder(mLayoutInflater, parent);
- case VIEW_TYPE_SEPARATOR:
- return new SeparatorViewHolder(mLayoutInflater, parent);
- case VIEW_TYPE_HEADER:
- return new HeaderViewHolder(mHeaderLayout);
- }
- return null;
- }
-
- @Override
- public void onBindViewHolder(ViewHolder holder, int position) {
- switch (getItemViewType(position)) {
- case VIEW_TYPE_NORMAL: {
- NavigationMenuItemView itemView = (NavigationMenuItemView) holder.itemView;
- itemView.setIconTintList(mIconTintList);
- if (mTextAppearanceSet) {
- itemView.setTextAppearance(mTextAppearance);
- }
- if (mTextColor != null) {
- itemView.setTextColor(mTextColor);
- }
- ViewCompat.setBackground(itemView, mItemBackground != null ?
- mItemBackground.getConstantState().newDrawable() : null);
- NavigationMenuTextItem item = (NavigationMenuTextItem) mItems.get(position);
- itemView.setNeedsEmptyIcon(item.needsEmptyIcon);
- itemView.initialize(item.getMenuItem(), 0);
- break;
- }
- case VIEW_TYPE_SUBHEADER: {
- TextView subHeader = (TextView) holder.itemView;
- NavigationMenuTextItem item = (NavigationMenuTextItem) mItems.get(position);
- subHeader.setText(item.getMenuItem().getTitle());
- break;
- }
- case VIEW_TYPE_SEPARATOR: {
- NavigationMenuSeparatorItem item =
- (NavigationMenuSeparatorItem) mItems.get(position);
- holder.itemView.setPadding(0, item.getPaddingTop(), 0,
- item.getPaddingBottom());
- break;
- }
- case VIEW_TYPE_HEADER: {
- break;
- }
- }
-
- }
-
- @Override
- public void onViewRecycled(ViewHolder holder) {
- if (holder instanceof NormalViewHolder) {
- ((NavigationMenuItemView) holder.itemView).recycle();
- }
- }
-
- public void update() {
- prepareMenuItems();
- notifyDataSetChanged();
- }
-
- /**
- * Flattens the visible menu items of {@link #mMenu} into {@link #mItems},
- * while inserting separators between items when necessary.
- */
- private void prepareMenuItems() {
- if (mUpdateSuspended) {
- return;
- }
- mUpdateSuspended = true;
- mItems.clear();
- mItems.add(new NavigationMenuHeaderItem());
-
- int currentGroupId = -1;
- int currentGroupStart = 0;
- boolean currentGroupHasIcon = false;
- for (int i = 0, totalSize = mMenu.getVisibleItems().size(); i < totalSize; i++) {
- MenuItemImpl item = mMenu.getVisibleItems().get(i);
- if (item.isChecked()) {
- setCheckedItem(item);
- }
- if (item.isCheckable()) {
- item.setExclusiveCheckable(false);
- }
- if (item.hasSubMenu()) {
- SubMenu subMenu = item.getSubMenu();
- if (subMenu.hasVisibleItems()) {
- if (i != 0) {
- mItems.add(new NavigationMenuSeparatorItem(mPaddingSeparator, 0));
- }
- mItems.add(new NavigationMenuTextItem(item));
- boolean subMenuHasIcon = false;
- int subMenuStart = mItems.size();
- for (int j = 0, size = subMenu.size(); j < size; j++) {
- MenuItemImpl subMenuItem = (MenuItemImpl) subMenu.getItem(j);
- if (subMenuItem.isVisible()) {
- if (!subMenuHasIcon && subMenuItem.getIcon() != null) {
- subMenuHasIcon = true;
- }
- if (subMenuItem.isCheckable()) {
- subMenuItem.setExclusiveCheckable(false);
- }
- if (item.isChecked()) {
- setCheckedItem(item);
- }
- mItems.add(new NavigationMenuTextItem(subMenuItem));
- }
- }
- if (subMenuHasIcon) {
- appendTransparentIconIfMissing(subMenuStart, mItems.size());
- }
- }
- } else {
- int groupId = item.getGroupId();
- if (groupId != currentGroupId) { // first item in group
- currentGroupStart = mItems.size();
- currentGroupHasIcon = item.getIcon() != null;
- if (i != 0) {
- currentGroupStart++;
- mItems.add(new NavigationMenuSeparatorItem(
- mPaddingSeparator, mPaddingSeparator));
- }
- } else if (!currentGroupHasIcon && item.getIcon() != null) {
- currentGroupHasIcon = true;
- appendTransparentIconIfMissing(currentGroupStart, mItems.size());
- }
- NavigationMenuTextItem textItem = new NavigationMenuTextItem(item);
- textItem.needsEmptyIcon = currentGroupHasIcon;
- mItems.add(textItem);
- currentGroupId = groupId;
- }
- }
- mUpdateSuspended = false;
- }
-
- private void appendTransparentIconIfMissing(int startIndex, int endIndex) {
- for (int i = startIndex; i < endIndex; i++) {
- NavigationMenuTextItem textItem = (NavigationMenuTextItem) mItems.get(i);
- textItem.needsEmptyIcon = true;
- }
- }
-
- public void setCheckedItem(MenuItemImpl checkedItem) {
- if (mCheckedItem == checkedItem || !checkedItem.isCheckable()) {
- return;
- }
- if (mCheckedItem != null) {
- mCheckedItem.setChecked(false);
- }
- mCheckedItem = checkedItem;
- checkedItem.setChecked(true);
- }
-
- public Bundle createInstanceState() {
- Bundle state = new Bundle();
- if (mCheckedItem != null) {
- state.putInt(STATE_CHECKED_ITEM, mCheckedItem.getItemId());
- }
- // Store the states of the action views.
- SparseArray<ParcelableSparseArray> actionViewStates = new SparseArray<>();
- for (int i = 0, size = mItems.size(); i < size; i++) {
- NavigationMenuItem navigationMenuItem = mItems.get(i);
- if (navigationMenuItem instanceof NavigationMenuTextItem) {
- MenuItemImpl item = ((NavigationMenuTextItem) navigationMenuItem).getMenuItem();
- View actionView = item != null ? item.getActionView() : null;
- if (actionView != null) {
- ParcelableSparseArray container = new ParcelableSparseArray();
- actionView.saveHierarchyState(container);
- actionViewStates.put(item.getItemId(), container);
- }
- }
- }
- state.putSparseParcelableArray(STATE_ACTION_VIEWS, actionViewStates);
- return state;
- }
-
- public void restoreInstanceState(Bundle state) {
- int checkedItem = state.getInt(STATE_CHECKED_ITEM, 0);
- if (checkedItem != 0) {
- mUpdateSuspended = true;
- for (int i = 0, size = mItems.size(); i < size; i++) {
- NavigationMenuItem item = mItems.get(i);
- if (item instanceof NavigationMenuTextItem) {
- MenuItemImpl menuItem = ((NavigationMenuTextItem) item).getMenuItem();
- if (menuItem != null && menuItem.getItemId() == checkedItem) {
- setCheckedItem(menuItem);
- break;
- }
- }
- }
- mUpdateSuspended = false;
- prepareMenuItems();
- }
- // Restore the states of the action views.
- SparseArray<ParcelableSparseArray> actionViewStates = state
- .getSparseParcelableArray(STATE_ACTION_VIEWS);
- if (actionViewStates != null) {
- for (int i = 0, size = mItems.size(); i < size; i++) {
- NavigationMenuItem navigationMenuItem = mItems.get(i);
- if (!(navigationMenuItem instanceof NavigationMenuTextItem)) {
- continue;
- }
- MenuItemImpl item = ((NavigationMenuTextItem) navigationMenuItem).getMenuItem();
- if (item == null) {
- continue;
- }
- View actionView = item.getActionView();
- if (actionView == null) {
- continue;
- }
- ParcelableSparseArray container = actionViewStates.get(item.getItemId());
- if (container == null) {
- continue;
- }
- actionView.restoreHierarchyState(container);
- }
- }
- }
-
- public void setUpdateSuspended(boolean updateSuspended) {
- mUpdateSuspended = updateSuspended;
- }
-
- }
-
- /**
- * Unified data model for all sorts of navigation menu items.
- */
- private interface NavigationMenuItem {
- }
-
- /**
- * Normal or subheader items.
- */
- private static class NavigationMenuTextItem implements NavigationMenuItem {
-
- private final MenuItemImpl mMenuItem;
-
- boolean needsEmptyIcon;
-
- NavigationMenuTextItem(MenuItemImpl item) {
- mMenuItem = item;
- }
-
- public MenuItemImpl getMenuItem() {
- return mMenuItem;
- }
-
- }
-
- /**
- * Separator items.
- */
- private static class NavigationMenuSeparatorItem implements NavigationMenuItem {
-
- private final int mPaddingTop;
-
- private final int mPaddingBottom;
-
- public NavigationMenuSeparatorItem(int paddingTop, int paddingBottom) {
- mPaddingTop = paddingTop;
- mPaddingBottom = paddingBottom;
- }
-
- public int getPaddingTop() {
- return mPaddingTop;
- }
-
- public int getPaddingBottom() {
- return mPaddingBottom;
- }
-
- }
-
- /**
- * Header (not subheader) items.
- */
- private static class NavigationMenuHeaderItem implements NavigationMenuItem {
- NavigationMenuHeaderItem() {
- }
- // The actual content is hold by NavigationMenuPresenter#mHeaderLayout.
- }
-
-}
diff --git a/android/support/design/internal/NavigationMenuView.java b/android/support/design/internal/NavigationMenuView.java
deleted file mode 100644
index 711f71ee..00000000
--- a/android/support/design/internal/NavigationMenuView.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.design.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuView;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.util.AttributeSet;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class NavigationMenuView extends RecyclerView implements MenuView {
-
- public NavigationMenuView(Context context) {
- this(context, null);
- }
-
- public NavigationMenuView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public NavigationMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
- }
-
- @Override
- public void initialize(MenuBuilder menu) {
-
- }
-
- @Override
- public int getWindowAnimations() {
- return 0;
- }
-
-}
diff --git a/android/support/design/internal/NavigationSubMenu.java b/android/support/design/internal/NavigationSubMenu.java
deleted file mode 100644
index 1ff1e4f4..00000000
--- a/android/support/design/internal/NavigationSubMenu.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.design.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuItemImpl;
-import android.support.v7.view.menu.SubMenuBuilder;
-
-/**
- * This is a {@link SubMenuBuilder} that it notifies the parent {@link NavigationMenu} of its menu
- * updates.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class NavigationSubMenu extends SubMenuBuilder {
-
- public NavigationSubMenu(Context context, NavigationMenu menu, MenuItemImpl item) {
- super(context, menu, item);
- }
-
- @Override
- public void onItemsChanged(boolean structureChanged) {
- super.onItemsChanged(structureChanged);
- ((MenuBuilder) getParentMenu()).onItemsChanged(structureChanged);
- }
-
-}
diff --git a/android/support/design/internal/ParcelableSparseArray.java b/android/support/design/internal/ParcelableSparseArray.java
deleted file mode 100644
index b29000e6..00000000
--- a/android/support/design/internal/ParcelableSparseArray.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.design.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.RestrictTo;
-import android.util.SparseArray;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ParcelableSparseArray extends SparseArray<Parcelable> implements Parcelable {
-
- public ParcelableSparseArray() {
- super();
- }
-
- public ParcelableSparseArray(Parcel source, ClassLoader loader) {
- super();
- int size = source.readInt();
- int[] keys = new int[size];
- source.readIntArray(keys);
- Parcelable[] values = source.readParcelableArray(loader);
- for (int i = 0; i < size; ++i) {
- put(keys[i], values[i]);
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- int size = size();
- int[] keys = new int[size];
- Parcelable[] values = new Parcelable[size];
- for (int i = 0; i < size; ++i) {
- keys[i] = keyAt(i);
- values[i] = valueAt(i);
- }
- parcel.writeInt(size);
- parcel.writeIntArray(keys);
- parcel.writeParcelableArray(values, flags);
- }
-
- public static final Creator<ParcelableSparseArray> CREATOR =
- new ClassLoaderCreator<ParcelableSparseArray>() {
- @Override
- public ParcelableSparseArray createFromParcel(Parcel source, ClassLoader loader) {
- return new ParcelableSparseArray(source, loader);
- }
-
- @Override
- public ParcelableSparseArray createFromParcel(Parcel source) {
- return new ParcelableSparseArray(source, null);
- }
-
- @Override
- public ParcelableSparseArray[] newArray(int size) {
- return new ParcelableSparseArray[size];
- }
- };
-}
diff --git a/android/support/design/internal/ScrimInsetsFrameLayout.java b/android/support/design/internal/ScrimInsetsFrameLayout.java
deleted file mode 100644
index 38f5b29b..00000000
--- a/android/support/design/internal/ScrimInsetsFrameLayout.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * 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.design.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.design.R;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.WindowInsetsCompat;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ScrimInsetsFrameLayout extends FrameLayout {
-
- Drawable mInsetForeground;
-
- Rect mInsets;
-
- private Rect mTempRect = new Rect();
-
- public ScrimInsetsFrameLayout(Context context) {
- this(context, null);
- }
-
- public ScrimInsetsFrameLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public ScrimInsetsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- final TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.ScrimInsetsFrameLayout, defStyleAttr,
- R.style.Widget_Design_ScrimInsetsFrameLayout);
- mInsetForeground = a.getDrawable(R.styleable.ScrimInsetsFrameLayout_insetForeground);
- a.recycle();
- setWillNotDraw(true); // No need to draw until the insets are adjusted
-
- ViewCompat.setOnApplyWindowInsetsListener(this,
- new android.support.v4.view.OnApplyWindowInsetsListener() {
- @Override
- public WindowInsetsCompat onApplyWindowInsets(View v,
- WindowInsetsCompat insets) {
- if (null == mInsets) {
- mInsets = new Rect();
- }
- mInsets.set(insets.getSystemWindowInsetLeft(),
- insets.getSystemWindowInsetTop(),
- insets.getSystemWindowInsetRight(),
- insets.getSystemWindowInsetBottom());
- onInsetsChanged(insets);
- setWillNotDraw(!insets.hasSystemWindowInsets() || mInsetForeground == null);
- ViewCompat.postInvalidateOnAnimation(ScrimInsetsFrameLayout.this);
- return insets.consumeSystemWindowInsets();
- }
- });
- }
-
- @Override
- public void draw(@NonNull Canvas canvas) {
- super.draw(canvas);
-
- int width = getWidth();
- int height = getHeight();
- if (mInsets != null && mInsetForeground != null) {
- int sc = canvas.save();
- canvas.translate(getScrollX(), getScrollY());
-
- // Top
- mTempRect.set(0, 0, width, mInsets.top);
- mInsetForeground.setBounds(mTempRect);
- mInsetForeground.draw(canvas);
-
- // Bottom
- mTempRect.set(0, height - mInsets.bottom, width, height);
- mInsetForeground.setBounds(mTempRect);
- mInsetForeground.draw(canvas);
-
- // Left
- mTempRect.set(0, mInsets.top, mInsets.left, height - mInsets.bottom);
- mInsetForeground.setBounds(mTempRect);
- mInsetForeground.draw(canvas);
-
- // Right
- mTempRect.set(width - mInsets.right, mInsets.top, width, height - mInsets.bottom);
- mInsetForeground.setBounds(mTempRect);
- mInsetForeground.draw(canvas);
-
- canvas.restoreToCount(sc);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mInsetForeground != null) {
- mInsetForeground.setCallback(this);
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mInsetForeground != null) {
- mInsetForeground.setCallback(null);
- }
- }
-
- protected void onInsetsChanged(WindowInsetsCompat insets) {
- }
-
-}
diff --git a/android/support/design/internal/SnackbarContentLayout.java b/android/support/design/internal/SnackbarContentLayout.java
deleted file mode 100644
index 2abf0127..00000000
--- a/android/support/design/internal/SnackbarContentLayout.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2016 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.design.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.annotation.RestrictTo;
-import android.support.design.R;
-import android.support.design.widget.BaseTransientBottomBar;
-import android.support.v4.view.ViewCompat;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class SnackbarContentLayout extends LinearLayout implements
- BaseTransientBottomBar.ContentViewCallback {
- private TextView mMessageView;
- private Button mActionView;
-
- private int mMaxWidth;
- private int mMaxInlineActionWidth;
-
- public SnackbarContentLayout(Context context) {
- this(context, null);
- }
-
- public SnackbarContentLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SnackbarLayout);
- mMaxWidth = a.getDimensionPixelSize(R.styleable.SnackbarLayout_android_maxWidth, -1);
- mMaxInlineActionWidth = a.getDimensionPixelSize(
- R.styleable.SnackbarLayout_maxActionInlineWidth, -1);
- a.recycle();
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mMessageView = findViewById(R.id.snackbar_text);
- mActionView = findViewById(R.id.snackbar_action);
- }
-
- public TextView getMessageView() {
- return mMessageView;
- }
-
- public Button getActionView() {
- return mActionView;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- if (mMaxWidth > 0 && getMeasuredWidth() > mMaxWidth) {
- widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, MeasureSpec.EXACTLY);
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- final int multiLineVPadding = getResources().getDimensionPixelSize(
- R.dimen.design_snackbar_padding_vertical_2lines);
- final int singleLineVPadding = getResources().getDimensionPixelSize(
- R.dimen.design_snackbar_padding_vertical);
- final boolean isMultiLine = mMessageView.getLayout().getLineCount() > 1;
-
- boolean remeasure = false;
- if (isMultiLine && mMaxInlineActionWidth > 0
- && mActionView.getMeasuredWidth() > mMaxInlineActionWidth) {
- if (updateViewsWithinLayout(VERTICAL, multiLineVPadding,
- multiLineVPadding - singleLineVPadding)) {
- remeasure = true;
- }
- } else {
- final int messagePadding = isMultiLine ? multiLineVPadding : singleLineVPadding;
- if (updateViewsWithinLayout(HORIZONTAL, messagePadding, messagePadding)) {
- remeasure = true;
- }
- }
-
- if (remeasure) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- private boolean updateViewsWithinLayout(final int orientation,
- final int messagePadTop, final int messagePadBottom) {
- boolean changed = false;
- if (orientation != getOrientation()) {
- setOrientation(orientation);
- changed = true;
- }
- if (mMessageView.getPaddingTop() != messagePadTop
- || mMessageView.getPaddingBottom() != messagePadBottom) {
- updateTopBottomPadding(mMessageView, messagePadTop, messagePadBottom);
- changed = true;
- }
- return changed;
- }
-
- private static void updateTopBottomPadding(View view, int topPadding, int bottomPadding) {
- if (ViewCompat.isPaddingRelative(view)) {
- ViewCompat.setPaddingRelative(view,
- ViewCompat.getPaddingStart(view), topPadding,
- ViewCompat.getPaddingEnd(view), bottomPadding);
- } else {
- view.setPadding(view.getPaddingLeft(), topPadding,
- view.getPaddingRight(), bottomPadding);
- }
- }
-
- @Override
- public void animateContentIn(int delay, int duration) {
- mMessageView.setAlpha(0f);
- mMessageView.animate().alpha(1f).setDuration(duration)
- .setStartDelay(delay).start();
-
- if (mActionView.getVisibility() == VISIBLE) {
- mActionView.setAlpha(0f);
- mActionView.animate().alpha(1f).setDuration(duration)
- .setStartDelay(delay).start();
- }
- }
-
- @Override
- public void animateContentOut(int delay, int duration) {
- mMessageView.setAlpha(1f);
- mMessageView.animate().alpha(0f).setDuration(duration)
- .setStartDelay(delay).start();
-
- if (mActionView.getVisibility() == VISIBLE) {
- mActionView.setAlpha(1f);
- mActionView.animate().alpha(0f).setDuration(duration)
- .setStartDelay(delay).start();
- }
- }
-}
diff --git a/android/support/design/internal/TextScale.java b/android/support/design/internal/TextScale.java
deleted file mode 100644
index 06c94729..00000000
--- a/android/support/design/internal/TextScale.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2016 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.design.internal;
-
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.support.transition.Transition;
-import android.support.transition.TransitionValues;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import java.util.Map;
-
-/**
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-@RequiresApi(14)
-public class TextScale extends Transition {
- private static final String PROPNAME_SCALE = "android:textscale:scale";
-
- @Override
- public void captureStartValues(TransitionValues transitionValues) {
- captureValues(transitionValues);
- }
-
- @Override
- public void captureEndValues(TransitionValues transitionValues) {
- captureValues(transitionValues);
- }
-
- private void captureValues(TransitionValues transitionValues) {
- if (transitionValues.view instanceof TextView) {
- TextView textview = (TextView) transitionValues.view;
- transitionValues.values.put(PROPNAME_SCALE, textview.getScaleX());
- }
- }
-
- @Override
- public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
- TransitionValues endValues) {
- if (startValues == null || endValues == null || !(startValues.view instanceof TextView)
- || !(endValues.view instanceof TextView)) {
- return null;
- }
- final TextView view = (TextView) endValues.view;
- Map<String, Object> startVals = startValues.values;
- Map<String, Object> endVals = endValues.values;
- final float startSize = startVals.get(PROPNAME_SCALE) != null ? (float) startVals.get(
- PROPNAME_SCALE) : 1f;
- final float endSize = endVals.get(PROPNAME_SCALE) != null ? (float) endVals.get(
- PROPNAME_SCALE) : 1f;
- if (startSize == endSize) {
- return null;
- }
-
- ValueAnimator animator = ValueAnimator.ofFloat(startSize, endSize);
-
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- float animatedValue = (float) valueAnimator.getAnimatedValue();
- view.setScaleX(animatedValue);
- view.setScaleY(animatedValue);
- }
- });
- return animator;
- }
-}
diff --git a/android/support/design/internal/package-info.java b/android/support/design/internal/package-info.java
deleted file mode 100644
index 6b6f7bbd..00000000
--- a/android/support/design/internal/package-info.java
+++ /dev/null
@@ -1,9 +0,0 @@
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-package android.support.design.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
diff --git a/android/support/design/widget/AnimationUtils.java b/android/support/design/widget/AnimationUtils.java
deleted file mode 100644
index 3613afd8..00000000
--- a/android/support/design/widget/AnimationUtils.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.support.v4.view.animation.FastOutLinearInInterpolator;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
-import android.support.v4.view.animation.LinearOutSlowInInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-
-class AnimationUtils {
-
- static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
- static final Interpolator FAST_OUT_SLOW_IN_INTERPOLATOR = new FastOutSlowInInterpolator();
- static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR = new FastOutLinearInInterpolator();
- static final Interpolator LINEAR_OUT_SLOW_IN_INTERPOLATOR = new LinearOutSlowInInterpolator();
- static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
-
- /**
- * Linear interpolation between {@code startValue} and {@code endValue} by {@code fraction}.
- */
- static float lerp(float startValue, float endValue, float fraction) {
- return startValue + (fraction * (endValue - startValue));
- }
-
- static int lerp(int startValue, int endValue, float fraction) {
- return startValue + Math.round(fraction * (endValue - startValue));
- }
-
-}
diff --git a/android/support/design/widget/AppBarLayout.java b/android/support/design/widget/AppBarLayout.java
deleted file mode 100644
index 8304cd6a..00000000
--- a/android/support/design/widget/AppBarLayout.java
+++ /dev/null
@@ -1,1474 +0,0 @@
-/*
- * 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.design.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.VisibleForTesting;
-import android.support.design.R;
-import android.support.v4.math.MathUtils;
-import android.support.v4.util.ObjectsCompat;
-import android.support.v4.view.AbsSavedState;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.WindowInsetsCompat;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.Interpolator;
-import android.widget.LinearLayout;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * AppBarLayout is a vertical {@link LinearLayout} which implements many of the features of
- * material designs app bar concept, namely scrolling gestures.
- * <p>
- * Children should provide their desired scrolling behavior through
- * {@link LayoutParams#setScrollFlags(int)} and the associated layout xml attribute:
- * {@code app:layout_scrollFlags}.
- *
- * <p>
- * This view depends heavily on being used as a direct child within a {@link CoordinatorLayout}.
- * If you use AppBarLayout within a different {@link ViewGroup}, most of it's functionality will
- * not work.
- * <p>
- * AppBarLayout also requires a separate scrolling sibling in order to know when to scroll.
- * The binding is done through the {@link ScrollingViewBehavior} behavior class, meaning that you
- * should set your scrolling view's behavior to be an instance of {@link ScrollingViewBehavior}.
- * A string resource containing the full class name is available.
- *
- * <pre>
- * &lt;android.support.design.widget.CoordinatorLayout
- * xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
- * xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
- * android:layout_width=&quot;match_parent&quot;
- * android:layout_height=&quot;match_parent&quot;&gt;
- *
- * &lt;android.support.v4.widget.NestedScrollView
- * android:layout_width=&quot;match_parent&quot;
- * android:layout_height=&quot;match_parent&quot;
- * app:layout_behavior=&quot;@string/appbar_scrolling_view_behavior&quot;&gt;
- *
- * &lt;!-- Your scrolling content --&gt;
- *
- * &lt;/android.support.v4.widget.NestedScrollView&gt;
- *
- * &lt;android.support.design.widget.AppBarLayout
- * android:layout_height=&quot;wrap_content&quot;
- * android:layout_width=&quot;match_parent&quot;&gt;
- *
- * &lt;android.support.v7.widget.Toolbar
- * ...
- * app:layout_scrollFlags=&quot;scroll|enterAlways&quot;/&gt;
- *
- * &lt;android.support.design.widget.TabLayout
- * ...
- * app:layout_scrollFlags=&quot;scroll|enterAlways&quot;/&gt;
- *
- * &lt;/android.support.design.widget.AppBarLayout&gt;
- *
- * &lt;/android.support.design.widget.CoordinatorLayout&gt;
- * </pre>
- *
- * @see <a href="http://www.google.com/design/spec/layout/structure.html#structure-app-bar">
- * http://www.google.com/design/spec/layout/structure.html#structure-app-bar</a>
- */
-@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
-public class AppBarLayout extends LinearLayout {
-
- static final int PENDING_ACTION_NONE = 0x0;
- static final int PENDING_ACTION_EXPANDED = 0x1;
- static final int PENDING_ACTION_COLLAPSED = 0x2;
- static final int PENDING_ACTION_ANIMATE_ENABLED = 0x4;
- static final int PENDING_ACTION_FORCE = 0x8;
-
- /**
- * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical
- * offset changes.
- */
- public interface OnOffsetChangedListener {
- /**
- * Called when the {@link AppBarLayout}'s layout offset has been changed. This allows
- * child views to implement custom behavior based on the offset (for instance pinning a
- * view at a certain y value).
- *
- * @param appBarLayout the {@link AppBarLayout} which offset has changed
- * @param verticalOffset the vertical offset for the parent {@link AppBarLayout}, in px
- */
- void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset);
- }
-
- private static final int INVALID_SCROLL_RANGE = -1;
-
- private int mTotalScrollRange = INVALID_SCROLL_RANGE;
- private int mDownPreScrollRange = INVALID_SCROLL_RANGE;
- private int mDownScrollRange = INVALID_SCROLL_RANGE;
-
- private boolean mHaveChildWithInterpolator;
-
- private int mPendingAction = PENDING_ACTION_NONE;
-
- private WindowInsetsCompat mLastInsets;
-
- private List<OnOffsetChangedListener> mListeners;
-
- private boolean mCollapsible;
- private boolean mCollapsed;
-
- private int[] mTmpStatesArray;
-
- public AppBarLayout(Context context) {
- this(context, null);
- }
-
- public AppBarLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- setOrientation(VERTICAL);
-
- ThemeUtils.checkAppCompatTheme(context);
-
- if (Build.VERSION.SDK_INT >= 21) {
- // Use the bounds view outline provider so that we cast a shadow, even without a
- // background
- ViewUtilsLollipop.setBoundsViewOutlineProvider(this);
-
- // If we're running on API 21+, we should reset any state list animator from our
- // default style
- ViewUtilsLollipop.setStateListAnimatorFromAttrs(this, attrs, 0,
- R.style.Widget_Design_AppBarLayout);
- }
-
- final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppBarLayout,
- 0, R.style.Widget_Design_AppBarLayout);
- ViewCompat.setBackground(this, a.getDrawable(R.styleable.AppBarLayout_android_background));
- if (a.hasValue(R.styleable.AppBarLayout_expanded)) {
- setExpanded(a.getBoolean(R.styleable.AppBarLayout_expanded, false), false, false);
- }
- if (Build.VERSION.SDK_INT >= 21 && a.hasValue(R.styleable.AppBarLayout_elevation)) {
- ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator(
- this, a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0));
- }
- if (Build.VERSION.SDK_INT >= 26) {
- // In O+, we have these values set in the style. Since there is no defStyleAttr for
- // AppBarLayout at the AppCompat level, check for these attributes here.
- if (a.hasValue(R.styleable.AppBarLayout_android_keyboardNavigationCluster)) {
- this.setKeyboardNavigationCluster(a.getBoolean(
- R.styleable.AppBarLayout_android_keyboardNavigationCluster, false));
- }
- if (a.hasValue(R.styleable.AppBarLayout_android_touchscreenBlocksFocus)) {
- this.setTouchscreenBlocksFocus(a.getBoolean(
- R.styleable.AppBarLayout_android_touchscreenBlocksFocus, false));
- }
- }
- a.recycle();
-
- ViewCompat.setOnApplyWindowInsetsListener(this,
- new android.support.v4.view.OnApplyWindowInsetsListener() {
- @Override
- public WindowInsetsCompat onApplyWindowInsets(View v,
- WindowInsetsCompat insets) {
- return onWindowInsetChanged(insets);
- }
- });
- }
-
- /**
- * Add a listener that will be called when the offset of this {@link AppBarLayout} changes.
- *
- * @param listener The listener that will be called when the offset changes.]
- *
- * @see #removeOnOffsetChangedListener(OnOffsetChangedListener)
- */
- public void addOnOffsetChangedListener(OnOffsetChangedListener listener) {
- if (mListeners == null) {
- mListeners = new ArrayList<>();
- }
- if (listener != null && !mListeners.contains(listener)) {
- mListeners.add(listener);
- }
- }
-
- /**
- * Remove the previously added {@link OnOffsetChangedListener}.
- *
- * @param listener the listener to remove.
- */
- public void removeOnOffsetChangedListener(OnOffsetChangedListener listener) {
- if (mListeners != null && listener != null) {
- mListeners.remove(listener);
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- invalidateScrollRanges();
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- invalidateScrollRanges();
-
- mHaveChildWithInterpolator = false;
- for (int i = 0, z = getChildCount(); i < z; i++) {
- final View child = getChildAt(i);
- final LayoutParams childLp = (LayoutParams) child.getLayoutParams();
- final Interpolator interpolator = childLp.getScrollInterpolator();
-
- if (interpolator != null) {
- mHaveChildWithInterpolator = true;
- break;
- }
- }
-
- updateCollapsible();
- }
-
- private void updateCollapsible() {
- boolean haveCollapsibleChild = false;
- for (int i = 0, z = getChildCount(); i < z; i++) {
- if (((LayoutParams) getChildAt(i).getLayoutParams()).isCollapsible()) {
- haveCollapsibleChild = true;
- break;
- }
- }
- setCollapsibleState(haveCollapsibleChild);
- }
-
- private void invalidateScrollRanges() {
- // Invalidate the scroll ranges
- mTotalScrollRange = INVALID_SCROLL_RANGE;
- mDownPreScrollRange = INVALID_SCROLL_RANGE;
- mDownScrollRange = INVALID_SCROLL_RANGE;
- }
-
- @Override
- public void setOrientation(int orientation) {
- if (orientation != VERTICAL) {
- throw new IllegalArgumentException("AppBarLayout is always vertical and does"
- + " not support horizontal orientation");
- }
- super.setOrientation(orientation);
- }
-
- /**
- * Sets whether this {@link AppBarLayout} is expanded or not, animating if it has already
- * been laid out.
- *
- * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a
- * direct child of a {@link CoordinatorLayout}.</p>
- *
- * @param expanded true if the layout should be fully expanded, false if it should
- * be fully collapsed
- *
- * @attr ref android.support.design.R.styleable#AppBarLayout_expanded
- */
- public void setExpanded(boolean expanded) {
- setExpanded(expanded, ViewCompat.isLaidOut(this));
- }
-
- /**
- * Sets whether this {@link AppBarLayout} is expanded or not.
- *
- * <p>As with {@link AppBarLayout}'s scrolling, this method relies on this layout being a
- * direct child of a {@link CoordinatorLayout}.</p>
- *
- * @param expanded true if the layout should be fully expanded, false if it should
- * be fully collapsed
- * @param animate Whether to animate to the new state
- *
- * @attr ref android.support.design.R.styleable#AppBarLayout_expanded
- */
- public void setExpanded(boolean expanded, boolean animate) {
- setExpanded(expanded, animate, true);
- }
-
- private void setExpanded(boolean expanded, boolean animate, boolean force) {
- mPendingAction = (expanded ? PENDING_ACTION_EXPANDED : PENDING_ACTION_COLLAPSED)
- | (animate ? PENDING_ACTION_ANIMATE_ENABLED : 0)
- | (force ? PENDING_ACTION_FORCE : 0);
- requestLayout();
- }
-
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams;
- }
-
- @Override
- protected LayoutParams generateDefaultLayoutParams() {
- return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
- }
-
- @Override
- public LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new LayoutParams(getContext(), attrs);
- }
-
- @Override
- protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- if (Build.VERSION.SDK_INT >= 19 && p instanceof LinearLayout.LayoutParams) {
- return new LayoutParams((LinearLayout.LayoutParams) p);
- } else if (p instanceof MarginLayoutParams) {
- return new LayoutParams((MarginLayoutParams) p);
- }
- return new LayoutParams(p);
- }
-
- boolean hasChildWithInterpolator() {
- return mHaveChildWithInterpolator;
- }
-
- /**
- * Returns the scroll range of all children.
- *
- * @return the scroll range in px
- */
- public final int getTotalScrollRange() {
- if (mTotalScrollRange != INVALID_SCROLL_RANGE) {
- return mTotalScrollRange;
- }
-
- int range = 0;
- for (int i = 0, z = getChildCount(); i < z; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- final int childHeight = child.getMeasuredHeight();
- final int flags = lp.mScrollFlags;
-
- if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
- // We're set to scroll so add the child's height
- range += childHeight + lp.topMargin + lp.bottomMargin;
-
- if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
- // For a collapsing scroll, we to take the collapsed height into account.
- // We also break straight away since later views can't scroll beneath
- // us
- range -= ViewCompat.getMinimumHeight(child);
- break;
- }
- } else {
- // As soon as a view doesn't have the scroll flag, we end the range calculation.
- // This is because views below can not scroll under a fixed view.
- break;
- }
- }
- return mTotalScrollRange = Math.max(0, range - getTopInset());
- }
-
- boolean hasScrollableChildren() {
- return getTotalScrollRange() != 0;
- }
-
- /**
- * Return the scroll range when scrolling up from a nested pre-scroll.
- */
- int getUpNestedPreScrollRange() {
- return getTotalScrollRange();
- }
-
- /**
- * Return the scroll range when scrolling down from a nested pre-scroll.
- */
- int getDownNestedPreScrollRange() {
- if (mDownPreScrollRange != INVALID_SCROLL_RANGE) {
- // If we already have a valid value, return it
- return mDownPreScrollRange;
- }
-
- int range = 0;
- for (int i = getChildCount() - 1; i >= 0; i--) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- final int childHeight = child.getMeasuredHeight();
- final int flags = lp.mScrollFlags;
-
- if ((flags & LayoutParams.FLAG_QUICK_RETURN) == LayoutParams.FLAG_QUICK_RETURN) {
- // First take the margin into account
- range += lp.topMargin + lp.bottomMargin;
- // The view has the quick return flag combination...
- if ((flags & LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED) != 0) {
- // If they're set to enter collapsed, use the minimum height
- range += ViewCompat.getMinimumHeight(child);
- } else if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
- // Only enter by the amount of the collapsed height
- range += childHeight - ViewCompat.getMinimumHeight(child);
- } else {
- // Else use the full height (minus the top inset)
- range += childHeight - getTopInset();
- }
- } else if (range > 0) {
- // If we've hit an non-quick return scrollable view, and we've already hit a
- // quick return view, return now
- break;
- }
- }
- return mDownPreScrollRange = Math.max(0, range);
- }
-
- /**
- * Return the scroll range when scrolling down from a nested scroll.
- */
- int getDownNestedScrollRange() {
- if (mDownScrollRange != INVALID_SCROLL_RANGE) {
- // If we already have a valid value, return it
- return mDownScrollRange;
- }
-
- int range = 0;
- for (int i = 0, z = getChildCount(); i < z; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- int childHeight = child.getMeasuredHeight();
- childHeight += lp.topMargin + lp.bottomMargin;
-
- final int flags = lp.mScrollFlags;
-
- if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
- // We're set to scroll so add the child's height
- range += childHeight;
-
- if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
- // For a collapsing exit scroll, we to take the collapsed height into account.
- // We also break the range straight away since later views can't scroll
- // beneath us
- range -= ViewCompat.getMinimumHeight(child) + getTopInset();
- break;
- }
- } else {
- // As soon as a view doesn't have the scroll flag, we end the range calculation.
- // This is because views below can not scroll under a fixed view.
- break;
- }
- }
- return mDownScrollRange = Math.max(0, range);
- }
-
- void dispatchOffsetUpdates(int offset) {
- // Iterate backwards through the list so that most recently added listeners
- // get the first chance to decide
- if (mListeners != null) {
- for (int i = 0, z = mListeners.size(); i < z; i++) {
- final OnOffsetChangedListener listener = mListeners.get(i);
- if (listener != null) {
- listener.onOffsetChanged(this, offset);
- }
- }
- }
- }
-
- final int getMinimumHeightForVisibleOverlappingContent() {
- final int topInset = getTopInset();
- final int minHeight = ViewCompat.getMinimumHeight(this);
- if (minHeight != 0) {
- // If this layout has a min height, use it (doubled)
- return (minHeight * 2) + topInset;
- }
-
- // Otherwise, we'll use twice the min height of our last child
- final int childCount = getChildCount();
- final int lastChildMinHeight = childCount >= 1
- ? ViewCompat.getMinimumHeight(getChildAt(childCount - 1)) : 0;
- if (lastChildMinHeight != 0) {
- return (lastChildMinHeight * 2) + topInset;
- }
-
- // If we reach here then we don't have a min height explicitly set. Instead we'll take a
- // guess at 1/3 of our height being visible
- return getHeight() / 3;
- }
-
- @Override
- protected int[] onCreateDrawableState(int extraSpace) {
- if (mTmpStatesArray == null) {
- // Note that we can't allocate this at the class level (in declaration) since
- // some paths in super View constructor are going to call this method before
- // that
- mTmpStatesArray = new int[2];
- }
- final int[] extraStates = mTmpStatesArray;
- final int[] states = super.onCreateDrawableState(extraSpace + extraStates.length);
-
- extraStates[0] = mCollapsible ? R.attr.state_collapsible : -R.attr.state_collapsible;
- extraStates[1] = mCollapsible && mCollapsed
- ? R.attr.state_collapsed : -R.attr.state_collapsed;
-
- return mergeDrawableStates(states, extraStates);
- }
-
- /**
- * Sets whether the AppBarLayout has collapsible children or not.
- *
- * @return true if the collapsible state changed
- */
- private boolean setCollapsibleState(boolean collapsible) {
- if (mCollapsible != collapsible) {
- mCollapsible = collapsible;
- refreshDrawableState();
- return true;
- }
- return false;
- }
-
- /**
- * Sets whether the AppBarLayout is in a collapsed state or not.
- *
- * @return true if the collapsed state changed
- */
- boolean setCollapsedState(boolean collapsed) {
- if (mCollapsed != collapsed) {
- mCollapsed = collapsed;
- refreshDrawableState();
- return true;
- }
- return false;
- }
-
- /**
- * @deprecated target elevation is now deprecated. AppBarLayout's elevation is now
- * controlled via a {@link android.animation.StateListAnimator}. If a target
- * elevation is set, either by this method or the {@code app:elevation} attribute,
- * a new state list animator is created which uses the given {@code elevation} value.
- *
- * @attr ref android.support.design.R.styleable#AppBarLayout_elevation
- */
- @Deprecated
- public void setTargetElevation(float elevation) {
- if (Build.VERSION.SDK_INT >= 21) {
- ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator(this, elevation);
- }
- }
-
- /**
- * @deprecated target elevation is now deprecated. AppBarLayout's elevation is now
- * controlled via a {@link android.animation.StateListAnimator}. This method now
- * always returns 0.
- */
- @Deprecated
- public float getTargetElevation() {
- return 0;
- }
-
- int getPendingAction() {
- return mPendingAction;
- }
-
- void resetPendingAction() {
- mPendingAction = PENDING_ACTION_NONE;
- }
-
- @VisibleForTesting
- final int getTopInset() {
- return mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
- }
-
- WindowInsetsCompat onWindowInsetChanged(final WindowInsetsCompat insets) {
- WindowInsetsCompat newInsets = null;
-
- if (ViewCompat.getFitsSystemWindows(this)) {
- // If we're set to fit system windows, keep the insets
- newInsets = insets;
- }
-
- // If our insets have changed, keep them and invalidate the scroll ranges...
- if (!ObjectsCompat.equals(mLastInsets, newInsets)) {
- mLastInsets = newInsets;
- invalidateScrollRanges();
- }
-
- return insets;
- }
-
- public static class LayoutParams extends LinearLayout.LayoutParams {
-
- /** @hide */
- @RestrictTo(LIBRARY_GROUP)
- @IntDef(flag=true, value={
- SCROLL_FLAG_SCROLL,
- SCROLL_FLAG_EXIT_UNTIL_COLLAPSED,
- SCROLL_FLAG_ENTER_ALWAYS,
- SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED,
- SCROLL_FLAG_SNAP
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ScrollFlags {}
-
- /**
- * The view will be scroll in direct relation to scroll events. This flag needs to be
- * set for any of the other flags to take effect. If any sibling views
- * before this one do not have this flag, then this value has no effect.
- */
- public static final int SCROLL_FLAG_SCROLL = 0x1;
-
- /**
- * When exiting (scrolling off screen) the view will be scrolled until it is
- * 'collapsed'. The collapsed height is defined by the view's minimum height.
- *
- * @see ViewCompat#getMinimumHeight(View)
- * @see View#setMinimumHeight(int)
- */
- public static final int SCROLL_FLAG_EXIT_UNTIL_COLLAPSED = 0x2;
-
- /**
- * When entering (scrolling on screen) the view will scroll on any downwards
- * scroll event, regardless of whether the scrolling view is also scrolling. This
- * is commonly referred to as the 'quick return' pattern.
- */
- public static final int SCROLL_FLAG_ENTER_ALWAYS = 0x4;
-
- /**
- * An additional flag for 'enterAlways' which modifies the returning view to
- * only initially scroll back to it's collapsed height. Once the scrolling view has
- * reached the end of it's scroll range, the remainder of this view will be scrolled
- * into view. The collapsed height is defined by the view's minimum height.
- *
- * @see ViewCompat#getMinimumHeight(View)
- * @see View#setMinimumHeight(int)
- */
- public static final int SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED = 0x8;
-
- /**
- * Upon a scroll ending, if the view is only partially visible then it will be snapped
- * and scrolled to it's closest edge. For example, if the view only has it's bottom 25%
- * displayed, it will be scrolled off screen completely. Conversely, if it's bottom 75%
- * is visible then it will be scrolled fully into view.
- */
- public static final int SCROLL_FLAG_SNAP = 0x10;
-
- /**
- * Internal flags which allows quick checking features
- */
- static final int FLAG_QUICK_RETURN = SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS;
- static final int FLAG_SNAP = SCROLL_FLAG_SCROLL | SCROLL_FLAG_SNAP;
- static final int COLLAPSIBLE_FLAGS = SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
- | SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED;
-
- int mScrollFlags = SCROLL_FLAG_SCROLL;
- Interpolator mScrollInterpolator;
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
- TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AppBarLayout_Layout);
- mScrollFlags = a.getInt(R.styleable.AppBarLayout_Layout_layout_scrollFlags, 0);
- if (a.hasValue(R.styleable.AppBarLayout_Layout_layout_scrollInterpolator)) {
- int resId = a.getResourceId(
- R.styleable.AppBarLayout_Layout_layout_scrollInterpolator, 0);
- mScrollInterpolator = android.view.animation.AnimationUtils.loadInterpolator(
- c, resId);
- }
- a.recycle();
- }
-
- public LayoutParams(int width, int height) {
- super(width, height);
- }
-
- public LayoutParams(int width, int height, float weight) {
- super(width, height, weight);
- }
-
- public LayoutParams(ViewGroup.LayoutParams p) {
- super(p);
- }
-
- public LayoutParams(MarginLayoutParams source) {
- super(source);
- }
-
- @RequiresApi(19)
- public LayoutParams(LinearLayout.LayoutParams source) {
- // The copy constructor called here only exists on API 19+.
- super(source);
- }
-
- @RequiresApi(19)
- public LayoutParams(LayoutParams source) {
- // The copy constructor called here only exists on API 19+.
- super(source);
- mScrollFlags = source.mScrollFlags;
- mScrollInterpolator = source.mScrollInterpolator;
- }
-
- /**
- * Set the scrolling flags.
- *
- * @param flags bitwise int of {@link #SCROLL_FLAG_SCROLL},
- * {@link #SCROLL_FLAG_EXIT_UNTIL_COLLAPSED}, {@link #SCROLL_FLAG_ENTER_ALWAYS},
- * {@link #SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED} and {@link #SCROLL_FLAG_SNAP }.
- *
- * @see #getScrollFlags()
- *
- * @attr ref android.support.design.R.styleable#AppBarLayout_Layout_layout_scrollFlags
- */
- public void setScrollFlags(@ScrollFlags int flags) {
- mScrollFlags = flags;
- }
-
- /**
- * Returns the scrolling flags.
- *
- * @see #setScrollFlags(int)
- *
- * @attr ref android.support.design.R.styleable#AppBarLayout_Layout_layout_scrollFlags
- */
- @ScrollFlags
- public int getScrollFlags() {
- return mScrollFlags;
- }
-
- /**
- * Set the interpolator to when scrolling the view associated with this
- * {@link LayoutParams}.
- *
- * @param interpolator the interpolator to use, or null to use normal 1-to-1 scrolling.
- *
- * @attr ref android.support.design.R.styleable#AppBarLayout_Layout_layout_scrollInterpolator
- * @see #getScrollInterpolator()
- */
- public void setScrollInterpolator(Interpolator interpolator) {
- mScrollInterpolator = interpolator;
- }
-
- /**
- * Returns the {@link Interpolator} being used for scrolling the view associated with this
- * {@link LayoutParams}. Null indicates 'normal' 1-to-1 scrolling.
- *
- * @attr ref android.support.design.R.styleable#AppBarLayout_Layout_layout_scrollInterpolator
- * @see #setScrollInterpolator(Interpolator)
- */
- public Interpolator getScrollInterpolator() {
- return mScrollInterpolator;
- }
-
- /**
- * Returns true if the scroll flags are compatible for 'collapsing'
- */
- boolean isCollapsible() {
- return (mScrollFlags & SCROLL_FLAG_SCROLL) == SCROLL_FLAG_SCROLL
- && (mScrollFlags & COLLAPSIBLE_FLAGS) != 0;
- }
- }
-
- /**
- * The default {@link Behavior} for {@link AppBarLayout}. Implements the necessary nested
- * scroll handling with offsetting.
- */
- public static class Behavior extends HeaderBehavior<AppBarLayout> {
- private static final int MAX_OFFSET_ANIMATION_DURATION = 600; // ms
- private static final int INVALID_POSITION = -1;
-
- /**
- * Callback to allow control over any {@link AppBarLayout} dragging.
- */
- public static abstract class DragCallback {
- /**
- * Allows control over whether the given {@link AppBarLayout} can be dragged or not.
- *
- * <p>Dragging is defined as a direct touch on the AppBarLayout with movement. This
- * call does not affect any nested scrolling.</p>
- *
- * @return true if we are in a position to scroll the AppBarLayout via a drag, false
- * if not.
- */
- public abstract boolean canDrag(@NonNull AppBarLayout appBarLayout);
- }
-
- private int mOffsetDelta;
- private ValueAnimator mOffsetAnimator;
-
- private int mOffsetToChildIndexOnLayout = INVALID_POSITION;
- private boolean mOffsetToChildIndexOnLayoutIsMinHeight;
- private float mOffsetToChildIndexOnLayoutPerc;
-
- private WeakReference<View> mLastNestedScrollingChildRef;
- private DragCallback mOnDragCallback;
-
- public Behavior() {}
-
- public Behavior(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child,
- View directTargetChild, View target, int nestedScrollAxes, int type) {
- // Return true if we're nested scrolling vertically, and we have scrollable children
- // and the scrolling view is big enough to scroll
- final boolean started = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0
- && child.hasScrollableChildren()
- && parent.getHeight() - directTargetChild.getHeight() <= child.getHeight();
-
- if (started && mOffsetAnimator != null) {
- // Cancel any offset animation
- mOffsetAnimator.cancel();
- }
-
- // A new nested scroll has started so clear out the previous ref
- mLastNestedScrollingChildRef = null;
-
- return started;
- }
-
- @Override
- public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
- View target, int dx, int dy, int[] consumed, int type) {
- if (dy != 0) {
- int min, max;
- if (dy < 0) {
- // We're scrolling down
- min = -child.getTotalScrollRange();
- max = min + child.getDownNestedPreScrollRange();
- } else {
- // We're scrolling up
- min = -child.getUpNestedPreScrollRange();
- max = 0;
- }
- if (min != max) {
- consumed[1] = scroll(coordinatorLayout, child, dy, min, max);
- }
- }
- }
-
- @Override
- public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
- View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed,
- int type) {
- if (dyUnconsumed < 0) {
- // If the scrolling view is scrolling down but not consuming, it's probably be at
- // the top of it's content
- scroll(coordinatorLayout, child, dyUnconsumed,
- -child.getDownNestedScrollRange(), 0);
- }
- }
-
- @Override
- public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl,
- View target, int type) {
- if (type == ViewCompat.TYPE_TOUCH) {
- // If we haven't been flung then let's see if the current view has been set to snap
- snapToChildIfNeeded(coordinatorLayout, abl);
- }
-
- // Keep a reference to the previous nested scrolling child
- mLastNestedScrollingChildRef = new WeakReference<>(target);
- }
-
- /**
- * Set a callback to control any {@link AppBarLayout} dragging.
- *
- * @param callback the callback to use, or {@code null} to use the default behavior.
- */
- public void setDragCallback(@Nullable DragCallback callback) {
- mOnDragCallback = callback;
- }
-
- private void animateOffsetTo(final CoordinatorLayout coordinatorLayout,
- final AppBarLayout child, final int offset, float velocity) {
- final int distance = Math.abs(getTopBottomOffsetForScrollingSibling() - offset);
-
- final int duration;
- velocity = Math.abs(velocity);
- if (velocity > 0) {
- duration = 3 * Math.round(1000 * (distance / velocity));
- } else {
- final float distanceRatio = (float) distance / child.getHeight();
- duration = (int) ((distanceRatio + 1) * 150);
- }
-
- animateOffsetWithDuration(coordinatorLayout, child, offset, duration);
- }
-
- private void animateOffsetWithDuration(final CoordinatorLayout coordinatorLayout,
- final AppBarLayout child, final int offset, final int duration) {
- final int currentOffset = getTopBottomOffsetForScrollingSibling();
- if (currentOffset == offset) {
- if (mOffsetAnimator != null && mOffsetAnimator.isRunning()) {
- mOffsetAnimator.cancel();
- }
- return;
- }
-
- if (mOffsetAnimator == null) {
- mOffsetAnimator = new ValueAnimator();
- mOffsetAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
- mOffsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- setHeaderTopBottomOffset(coordinatorLayout, child,
- (int) animation.getAnimatedValue());
- }
- });
- } else {
- mOffsetAnimator.cancel();
- }
-
- mOffsetAnimator.setDuration(Math.min(duration, MAX_OFFSET_ANIMATION_DURATION));
- mOffsetAnimator.setIntValues(currentOffset, offset);
- mOffsetAnimator.start();
- }
-
- private int getChildIndexOnOffset(AppBarLayout abl, final int offset) {
- for (int i = 0, count = abl.getChildCount(); i < count; i++) {
- View child = abl.getChildAt(i);
- if (child.getTop() <= -offset && child.getBottom() >= -offset) {
- return i;
- }
- }
- return -1;
- }
-
- private void snapToChildIfNeeded(CoordinatorLayout coordinatorLayout, AppBarLayout abl) {
- final int offset = getTopBottomOffsetForScrollingSibling();
- final int offsetChildIndex = getChildIndexOnOffset(abl, offset);
- if (offsetChildIndex >= 0) {
- final View offsetChild = abl.getChildAt(offsetChildIndex);
- final LayoutParams lp = (LayoutParams) offsetChild.getLayoutParams();
- final int flags = lp.getScrollFlags();
-
- if ((flags & LayoutParams.FLAG_SNAP) == LayoutParams.FLAG_SNAP) {
- // We're set the snap, so animate the offset to the nearest edge
- int snapTop = -offsetChild.getTop();
- int snapBottom = -offsetChild.getBottom();
-
- if (offsetChildIndex == abl.getChildCount() - 1) {
- // If this is the last child, we need to take the top inset into account
- snapBottom += abl.getTopInset();
- }
-
- if (checkFlag(flags, LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED)) {
- // If the view is set only exit until it is collapsed, we'll abide by that
- snapBottom += ViewCompat.getMinimumHeight(offsetChild);
- } else if (checkFlag(flags, LayoutParams.FLAG_QUICK_RETURN
- | LayoutParams.SCROLL_FLAG_ENTER_ALWAYS)) {
- // If it's set to always enter collapsed, it actually has two states. We
- // select the state and then snap within the state
- final int seam = snapBottom + ViewCompat.getMinimumHeight(offsetChild);
- if (offset < seam) {
- snapTop = seam;
- } else {
- snapBottom = seam;
- }
- }
-
- final int newOffset = offset < (snapBottom + snapTop) / 2
- ? snapBottom
- : snapTop;
- animateOffsetTo(coordinatorLayout, abl,
- MathUtils.clamp(newOffset, -abl.getTotalScrollRange(), 0), 0);
- }
- }
- }
-
- private static boolean checkFlag(final int flags, final int check) {
- return (flags & check) == check;
- }
-
- @Override
- public boolean onMeasureChild(CoordinatorLayout parent, AppBarLayout child,
- int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec,
- int heightUsed) {
- final CoordinatorLayout.LayoutParams lp =
- (CoordinatorLayout.LayoutParams) child.getLayoutParams();
- if (lp.height == CoordinatorLayout.LayoutParams.WRAP_CONTENT) {
- // If the view is set to wrap on it's height, CoordinatorLayout by default will
- // cap the view at the CoL's height. Since the AppBarLayout can scroll, this isn't
- // what we actually want, so we measure it ourselves with an unspecified spec to
- // allow the child to be larger than it's parent
- parent.onMeasureChild(child, parentWidthMeasureSpec, widthUsed,
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightUsed);
- return true;
- }
-
- // Let the parent handle it as normal
- return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed,
- parentHeightMeasureSpec, heightUsed);
- }
-
- @Override
- public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl,
- int layoutDirection) {
- boolean handled = super.onLayoutChild(parent, abl, layoutDirection);
-
- // The priority for for actions here is (first which is true wins):
- // 1. forced pending actions
- // 2. offsets for restorations
- // 3. non-forced pending actions
- final int pendingAction = abl.getPendingAction();
- if (mOffsetToChildIndexOnLayout >= 0 && (pendingAction & PENDING_ACTION_FORCE) == 0) {
- View child = abl.getChildAt(mOffsetToChildIndexOnLayout);
- int offset = -child.getBottom();
- if (mOffsetToChildIndexOnLayoutIsMinHeight) {
- offset += ViewCompat.getMinimumHeight(child) + abl.getTopInset();
- } else {
- offset += Math.round(child.getHeight() * mOffsetToChildIndexOnLayoutPerc);
- }
- setHeaderTopBottomOffset(parent, abl, offset);
- } else if (pendingAction != PENDING_ACTION_NONE) {
- final boolean animate = (pendingAction & PENDING_ACTION_ANIMATE_ENABLED) != 0;
- if ((pendingAction & PENDING_ACTION_COLLAPSED) != 0) {
- final int offset = -abl.getUpNestedPreScrollRange();
- if (animate) {
- animateOffsetTo(parent, abl, offset, 0);
- } else {
- setHeaderTopBottomOffset(parent, abl, offset);
- }
- } else if ((pendingAction & PENDING_ACTION_EXPANDED) != 0) {
- if (animate) {
- animateOffsetTo(parent, abl, 0, 0);
- } else {
- setHeaderTopBottomOffset(parent, abl, 0);
- }
- }
- }
-
- // Finally reset any pending states
- abl.resetPendingAction();
- mOffsetToChildIndexOnLayout = INVALID_POSITION;
-
- // We may have changed size, so let's constrain the top and bottom offset correctly,
- // just in case we're out of the bounds
- setTopAndBottomOffset(
- MathUtils.clamp(getTopAndBottomOffset(), -abl.getTotalScrollRange(), 0));
-
- // Update the AppBarLayout's drawable state for any elevation changes.
- // This is needed so that the elevation is set in the first layout, so that
- // we don't get a visual elevation jump pre-N (due to the draw dispatch skip)
- updateAppBarLayoutDrawableState(parent, abl, getTopAndBottomOffset(), 0, true);
-
- // Make sure we dispatch the offset update
- abl.dispatchOffsetUpdates(getTopAndBottomOffset());
-
- return handled;
- }
-
- @Override
- boolean canDragView(AppBarLayout view) {
- if (mOnDragCallback != null) {
- // If there is a drag callback set, it's in control
- return mOnDragCallback.canDrag(view);
- }
-
- // Else we'll use the default behaviour of seeing if it can scroll down
- if (mLastNestedScrollingChildRef != null) {
- // If we have a reference to a scrolling view, check it
- final View scrollingView = mLastNestedScrollingChildRef.get();
- return scrollingView != null && scrollingView.isShown()
- && !scrollingView.canScrollVertically(-1);
- } else {
- // Otherwise we assume that the scrolling view hasn't been scrolled and can drag.
- return true;
- }
- }
-
- @Override
- void onFlingFinished(CoordinatorLayout parent, AppBarLayout layout) {
- // At the end of a manual fling, check to see if we need to snap to the edge-child
- snapToChildIfNeeded(parent, layout);
- }
-
- @Override
- int getMaxDragOffset(AppBarLayout view) {
- return -view.getDownNestedScrollRange();
- }
-
- @Override
- int getScrollRangeForDragFling(AppBarLayout view) {
- return view.getTotalScrollRange();
- }
-
- @Override
- int setHeaderTopBottomOffset(CoordinatorLayout coordinatorLayout,
- AppBarLayout appBarLayout, int newOffset, int minOffset, int maxOffset) {
- final int curOffset = getTopBottomOffsetForScrollingSibling();
- int consumed = 0;
-
- if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
- // If we have some scrolling range, and we're currently within the min and max
- // offsets, calculate a new offset
- newOffset = MathUtils.clamp(newOffset, minOffset, maxOffset);
- if (curOffset != newOffset) {
- final int interpolatedOffset = appBarLayout.hasChildWithInterpolator()
- ? interpolateOffset(appBarLayout, newOffset)
- : newOffset;
-
- final boolean offsetChanged = setTopAndBottomOffset(interpolatedOffset);
-
- // Update how much dy we have consumed
- consumed = curOffset - newOffset;
- // Update the stored sibling offset
- mOffsetDelta = newOffset - interpolatedOffset;
-
- if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) {
- // If the offset hasn't changed and we're using an interpolated scroll
- // then we need to keep any dependent views updated. CoL will do this for
- // us when we move, but we need to do it manually when we don't (as an
- // interpolated scroll may finish early).
- coordinatorLayout.dispatchDependentViewsChanged(appBarLayout);
- }
-
- // Dispatch the updates to any listeners
- appBarLayout.dispatchOffsetUpdates(getTopAndBottomOffset());
-
- // Update the AppBarLayout's drawable state (for any elevation changes)
- updateAppBarLayoutDrawableState(coordinatorLayout, appBarLayout, newOffset,
- newOffset < curOffset ? -1 : 1, false);
- }
- } else {
- // Reset the offset delta
- mOffsetDelta = 0;
- }
-
- return consumed;
- }
-
- @VisibleForTesting
- boolean isOffsetAnimatorRunning() {
- return mOffsetAnimator != null && mOffsetAnimator.isRunning();
- }
-
- private int interpolateOffset(AppBarLayout layout, final int offset) {
- final int absOffset = Math.abs(offset);
-
- for (int i = 0, z = layout.getChildCount(); i < z; i++) {
- final View child = layout.getChildAt(i);
- final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams();
- final Interpolator interpolator = childLp.getScrollInterpolator();
-
- if (absOffset >= child.getTop() && absOffset <= child.getBottom()) {
- if (interpolator != null) {
- int childScrollableHeight = 0;
- final int flags = childLp.getScrollFlags();
- if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
- // We're set to scroll so add the child's height plus margin
- childScrollableHeight += child.getHeight() + childLp.topMargin
- + childLp.bottomMargin;
-
- if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
- // For a collapsing scroll, we to take the collapsed height
- // into account.
- childScrollableHeight -= ViewCompat.getMinimumHeight(child);
- }
- }
-
- if (ViewCompat.getFitsSystemWindows(child)) {
- childScrollableHeight -= layout.getTopInset();
- }
-
- if (childScrollableHeight > 0) {
- final int offsetForView = absOffset - child.getTop();
- final int interpolatedDiff = Math.round(childScrollableHeight *
- interpolator.getInterpolation(
- offsetForView / (float) childScrollableHeight));
-
- return Integer.signum(offset) * (child.getTop() + interpolatedDiff);
- }
- }
-
- // If we get to here then the view on the offset isn't suitable for interpolated
- // scrolling. So break out of the loop
- break;
- }
- }
-
- return offset;
- }
-
- private void updateAppBarLayoutDrawableState(final CoordinatorLayout parent,
- final AppBarLayout layout, final int offset, final int direction,
- final boolean forceJump) {
- final View child = getAppBarChildOnOffset(layout, offset);
- if (child != null) {
- final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams();
- final int flags = childLp.getScrollFlags();
- boolean collapsed = false;
-
- if ((flags & LayoutParams.SCROLL_FLAG_SCROLL) != 0) {
- final int minHeight = ViewCompat.getMinimumHeight(child);
-
- if (direction > 0 && (flags & (LayoutParams.SCROLL_FLAG_ENTER_ALWAYS
- | LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED)) != 0) {
- // We're set to enter always collapsed so we are only collapsed when
- // being scrolled down, and in a collapsed offset
- collapsed = -offset >= child.getBottom() - minHeight - layout.getTopInset();
- } else if ((flags & LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED) != 0) {
- // We're set to exit until collapsed, so any offset which results in
- // the minimum height (or less) being shown is collapsed
- collapsed = -offset >= child.getBottom() - minHeight - layout.getTopInset();
- }
- }
-
- final boolean changed = layout.setCollapsedState(collapsed);
-
- if (Build.VERSION.SDK_INT >= 11 && (forceJump
- || (changed && shouldJumpElevationState(parent, layout)))) {
- // If the collapsed state changed, we may need to
- // jump to the current state if we have an overlapping view
- layout.jumpDrawablesToCurrentState();
- }
- }
- }
-
- private boolean shouldJumpElevationState(CoordinatorLayout parent, AppBarLayout layout) {
- // We should jump the elevated state if we have a dependent scrolling view which has
- // an overlapping top (i.e. overlaps us)
- final List<View> dependencies = parent.getDependents(layout);
- for (int i = 0, size = dependencies.size(); i < size; i++) {
- final View dependency = dependencies.get(i);
- final CoordinatorLayout.LayoutParams lp =
- (CoordinatorLayout.LayoutParams) dependency.getLayoutParams();
- final CoordinatorLayout.Behavior behavior = lp.getBehavior();
-
- if (behavior instanceof ScrollingViewBehavior) {
- return ((ScrollingViewBehavior) behavior).getOverlayTop() != 0;
- }
- }
- return false;
- }
-
- private static View getAppBarChildOnOffset(final AppBarLayout layout, final int offset) {
- final int absOffset = Math.abs(offset);
- for (int i = 0, z = layout.getChildCount(); i < z; i++) {
- final View child = layout.getChildAt(i);
- if (absOffset >= child.getTop() && absOffset <= child.getBottom()) {
- return child;
- }
- }
- return null;
- }
-
- @Override
- int getTopBottomOffsetForScrollingSibling() {
- return getTopAndBottomOffset() + mOffsetDelta;
- }
-
- @Override
- public Parcelable onSaveInstanceState(CoordinatorLayout parent, AppBarLayout abl) {
- final Parcelable superState = super.onSaveInstanceState(parent, abl);
- final int offset = getTopAndBottomOffset();
-
- // Try and find the first visible child...
- for (int i = 0, count = abl.getChildCount(); i < count; i++) {
- View child = abl.getChildAt(i);
- final int visBottom = child.getBottom() + offset;
-
- if (child.getTop() + offset <= 0 && visBottom >= 0) {
- final SavedState ss = new SavedState(superState);
- ss.firstVisibleChildIndex = i;
- ss.firstVisibleChildAtMinimumHeight =
- visBottom == (ViewCompat.getMinimumHeight(child) + abl.getTopInset());
- ss.firstVisibleChildPercentageShown = visBottom / (float) child.getHeight();
- return ss;
- }
- }
-
- // Else we'll just return the super state
- return superState;
- }
-
- @Override
- public void onRestoreInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout,
- Parcelable state) {
- if (state instanceof SavedState) {
- final SavedState ss = (SavedState) state;
- super.onRestoreInstanceState(parent, appBarLayout, ss.getSuperState());
- mOffsetToChildIndexOnLayout = ss.firstVisibleChildIndex;
- mOffsetToChildIndexOnLayoutPerc = ss.firstVisibleChildPercentageShown;
- mOffsetToChildIndexOnLayoutIsMinHeight = ss.firstVisibleChildAtMinimumHeight;
- } else {
- super.onRestoreInstanceState(parent, appBarLayout, state);
- mOffsetToChildIndexOnLayout = INVALID_POSITION;
- }
- }
-
- protected static class SavedState extends AbsSavedState {
- int firstVisibleChildIndex;
- float firstVisibleChildPercentageShown;
- boolean firstVisibleChildAtMinimumHeight;
-
- public SavedState(Parcel source, ClassLoader loader) {
- super(source, loader);
- firstVisibleChildIndex = source.readInt();
- firstVisibleChildPercentageShown = source.readFloat();
- firstVisibleChildAtMinimumHeight = source.readByte() != 0;
- }
-
- public SavedState(Parcelable superState) {
- super(superState);
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeInt(firstVisibleChildIndex);
- dest.writeFloat(firstVisibleChildPercentageShown);
- dest.writeByte((byte) (firstVisibleChildAtMinimumHeight ? 1 : 0));
- }
-
- public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
- @Override
- public SavedState createFromParcel(Parcel source, ClassLoader loader) {
- return new SavedState(source, loader);
- }
-
- @Override
- public SavedState createFromParcel(Parcel source) {
- return new SavedState(source, null);
- }
-
- @Override
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
- }
-
- /**
- * Behavior which should be used by {@link View}s which can scroll vertically and support
- * nested scrolling to automatically scroll any {@link AppBarLayout} siblings.
- */
- public static class ScrollingViewBehavior extends HeaderScrollingViewBehavior {
-
- public ScrollingViewBehavior() {}
-
- public ScrollingViewBehavior(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- final TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.ScrollingViewBehavior_Layout);
- setOverlayTop(a.getDimensionPixelSize(
- R.styleable.ScrollingViewBehavior_Layout_behavior_overlapTop, 0));
- a.recycle();
- }
-
- @Override
- public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
- // We depend on any AppBarLayouts
- return dependency instanceof AppBarLayout;
- }
-
- @Override
- public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
- View dependency) {
- offsetChildAsNeeded(parent, child, dependency);
- return false;
- }
-
- @Override
- public boolean onRequestChildRectangleOnScreen(CoordinatorLayout parent, View child,
- Rect rectangle, boolean immediate) {
- final AppBarLayout header = findFirstDependency(parent.getDependencies(child));
- if (header != null) {
- // Offset the rect by the child's left/top
- rectangle.offset(child.getLeft(), child.getTop());
-
- final Rect parentRect = mTempRect1;
- parentRect.set(0, 0, parent.getWidth(), parent.getHeight());
-
- if (!parentRect.contains(rectangle)) {
- // If the rectangle can not be fully seen the visible bounds, collapse
- // the AppBarLayout
- header.setExpanded(false, !immediate);
- return true;
- }
- }
- return false;
- }
-
- private void offsetChildAsNeeded(CoordinatorLayout parent, View child, View dependency) {
- final CoordinatorLayout.Behavior behavior =
- ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior();
- if (behavior instanceof Behavior) {
- // Offset the child, pinning it to the bottom the header-dependency, maintaining
- // any vertical gap and overlap
- final Behavior ablBehavior = (Behavior) behavior;
- ViewCompat.offsetTopAndBottom(child, (dependency.getBottom() - child.getTop())
- + ablBehavior.mOffsetDelta
- + getVerticalLayoutGap()
- - getOverlapPixelsForOffset(dependency));
- }
- }
-
- @Override
- float getOverlapRatioForOffset(final View header) {
- if (header instanceof AppBarLayout) {
- final AppBarLayout abl = (AppBarLayout) header;
- final int totalScrollRange = abl.getTotalScrollRange();
- final int preScrollDown = abl.getDownNestedPreScrollRange();
- final int offset = getAppBarLayoutOffset(abl);
-
- if (preScrollDown != 0 && (totalScrollRange + offset) <= preScrollDown) {
- // If we're in a pre-scroll down. Don't use the offset at all.
- return 0;
- } else {
- final int availScrollRange = totalScrollRange - preScrollDown;
- if (availScrollRange != 0) {
- // Else we'll use a interpolated ratio of the overlap, depending on offset
- return 1f + (offset / (float) availScrollRange);
- }
- }
- }
- return 0f;
- }
-
- private static int getAppBarLayoutOffset(AppBarLayout abl) {
- final CoordinatorLayout.Behavior behavior =
- ((CoordinatorLayout.LayoutParams) abl.getLayoutParams()).getBehavior();
- if (behavior instanceof Behavior) {
- return ((Behavior) behavior).getTopBottomOffsetForScrollingSibling();
- }
- return 0;
- }
-
- @Override
- AppBarLayout findFirstDependency(List<View> views) {
- for (int i = 0, z = views.size(); i < z; i++) {
- View view = views.get(i);
- if (view instanceof AppBarLayout) {
- return (AppBarLayout) view;
- }
- }
- return null;
- }
-
- @Override
- int getScrollRange(View v) {
- if (v instanceof AppBarLayout) {
- return ((AppBarLayout) v).getTotalScrollRange();
- } else {
- return super.getScrollRange(v);
- }
- }
- }
-}
diff --git a/android/support/design/widget/BaseTransientBottomBar.java b/android/support/design/widget/BaseTransientBottomBar.java
deleted file mode 100644
index 18c9ef9d..00000000
--- a/android/support/design/widget/BaseTransientBottomBar.java
+++ /dev/null
@@ -1,753 +0,0 @@
-/*
- * 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.design.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.support.design.widget.AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.support.annotation.IntDef;
-import android.support.annotation.IntRange;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.design.R;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.WindowInsetsCompat;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.FrameLayout;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Base class for lightweight transient bars that are displayed along the bottom edge of the
- * application window.
- *
- * @param <B> The transient bottom bar subclass.
- */
-public abstract class BaseTransientBottomBar<B extends BaseTransientBottomBar<B>> {
- /**
- * Base class for {@link BaseTransientBottomBar} callbacks.
- *
- * @param <B> The transient bottom bar subclass.
- * @see BaseTransientBottomBar#addCallback(BaseCallback)
- */
- public abstract static class BaseCallback<B> {
- /** Indicates that the Snackbar was dismissed via a swipe.*/
- public static final int DISMISS_EVENT_SWIPE = 0;
- /** Indicates that the Snackbar was dismissed via an action click.*/
- public static final int DISMISS_EVENT_ACTION = 1;
- /** Indicates that the Snackbar was dismissed via a timeout.*/
- public static final int DISMISS_EVENT_TIMEOUT = 2;
- /** Indicates that the Snackbar was dismissed via a call to {@link #dismiss()}.*/
- public static final int DISMISS_EVENT_MANUAL = 3;
- /** Indicates that the Snackbar was dismissed from a new Snackbar being shown.*/
- public static final int DISMISS_EVENT_CONSECUTIVE = 4;
-
- /** @hide */
- @RestrictTo(LIBRARY_GROUP)
- @IntDef({DISMISS_EVENT_SWIPE, DISMISS_EVENT_ACTION, DISMISS_EVENT_TIMEOUT,
- DISMISS_EVENT_MANUAL, DISMISS_EVENT_CONSECUTIVE})
- @Retention(RetentionPolicy.SOURCE)
- public @interface DismissEvent {}
-
- /**
- * Called when the given {@link BaseTransientBottomBar} has been dismissed, either
- * through a time-out, having been manually dismissed, or an action being clicked.
- *
- * @param transientBottomBar The transient bottom bar which has been dismissed.
- * @param event The event which caused the dismissal. One of either:
- * {@link #DISMISS_EVENT_SWIPE}, {@link #DISMISS_EVENT_ACTION},
- * {@link #DISMISS_EVENT_TIMEOUT}, {@link #DISMISS_EVENT_MANUAL} or
- * {@link #DISMISS_EVENT_CONSECUTIVE}.
- *
- * @see BaseTransientBottomBar#dismiss()
- */
- public void onDismissed(B transientBottomBar, @DismissEvent int event) {
- // empty
- }
-
- /**
- * Called when the given {@link BaseTransientBottomBar} is visible.
- *
- * @param transientBottomBar The transient bottom bar which is now visible.
- * @see BaseTransientBottomBar#show()
- */
- public void onShown(B transientBottomBar) {
- // empty
- }
- }
-
- /**
- * Interface that defines the behavior of the main content of a transient bottom bar.
- */
- public interface ContentViewCallback {
- /**
- * Animates the content of the transient bottom bar in.
- *
- * @param delay Animation delay.
- * @param duration Animation duration.
- */
- void animateContentIn(int delay, int duration);
-
- /**
- * Animates the content of the transient bottom bar out.
- *
- * @param delay Animation delay.
- * @param duration Animation duration.
- */
- void animateContentOut(int delay, int duration);
- }
-
- /**
- * @hide
- */
- @RestrictTo(LIBRARY_GROUP)
- @IntDef({LENGTH_INDEFINITE, LENGTH_SHORT, LENGTH_LONG})
- @IntRange(from = 1)
- @Retention(RetentionPolicy.SOURCE)
- public @interface Duration {}
-
- /**
- * Show the Snackbar indefinitely. This means that the Snackbar will be displayed from the time
- * that is {@link #show() shown} until either it is dismissed, or another Snackbar is shown.
- *
- * @see #setDuration
- */
- public static final int LENGTH_INDEFINITE = -2;
-
- /**
- * Show the Snackbar for a short period of time.
- *
- * @see #setDuration
- */
- public static final int LENGTH_SHORT = -1;
-
- /**
- * Show the Snackbar for a long period of time.
- *
- * @see #setDuration
- */
- public static final int LENGTH_LONG = 0;
-
- static final int ANIMATION_DURATION = 250;
- static final int ANIMATION_FADE_DURATION = 180;
-
- static final Handler sHandler;
- static final int MSG_SHOW = 0;
- static final int MSG_DISMISS = 1;
-
- // On JB/KK versions of the platform sometimes View.setTranslationY does not
- // result in layout / draw pass, and CoordinatorLayout relies on a draw pass to
- // happen to sync vertical positioning of all its child views
- private static final boolean USE_OFFSET_API = (Build.VERSION.SDK_INT >= 16)
- && (Build.VERSION.SDK_INT <= 19);
-
- static {
- sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
- @Override
- public boolean handleMessage(Message message) {
- switch (message.what) {
- case MSG_SHOW:
- ((BaseTransientBottomBar) message.obj).showView();
- return true;
- case MSG_DISMISS:
- ((BaseTransientBottomBar) message.obj).hideView(message.arg1);
- return true;
- }
- return false;
- }
- });
- }
-
- private final ViewGroup mTargetParent;
- private final Context mContext;
- final SnackbarBaseLayout mView;
- private final ContentViewCallback mContentViewCallback;
- private int mDuration;
-
- private List<BaseCallback<B>> mCallbacks;
-
- private final AccessibilityManager mAccessibilityManager;
-
- /**
- * @hide
- */
- @RestrictTo(LIBRARY_GROUP)
- interface OnLayoutChangeListener {
- void onLayoutChange(View view, int left, int top, int right, int bottom);
- }
-
- /**
- * @hide
- */
- @RestrictTo(LIBRARY_GROUP)
- interface OnAttachStateChangeListener {
- void onViewAttachedToWindow(View v);
- void onViewDetachedFromWindow(View v);
- }
-
- /**
- * Constructor for the transient bottom bar.
- *
- * @param parent The parent for this transient bottom bar.
- * @param content The content view for this transient bottom bar.
- * @param contentViewCallback The content view callback for this transient bottom bar.
- */
- protected BaseTransientBottomBar(@NonNull ViewGroup parent, @NonNull View content,
- @NonNull ContentViewCallback contentViewCallback) {
- if (parent == null) {
- throw new IllegalArgumentException("Transient bottom bar must have non-null parent");
- }
- if (content == null) {
- throw new IllegalArgumentException("Transient bottom bar must have non-null content");
- }
- if (contentViewCallback == null) {
- throw new IllegalArgumentException("Transient bottom bar must have non-null callback");
- }
-
- mTargetParent = parent;
- mContentViewCallback = contentViewCallback;
- mContext = parent.getContext();
-
- ThemeUtils.checkAppCompatTheme(mContext);
-
- LayoutInflater inflater = LayoutInflater.from(mContext);
- // Note that for backwards compatibility reasons we inflate a layout that is defined
- // in the extending Snackbar class. This is to prevent breakage of apps that have custom
- // coordinator layout behaviors that depend on that layout.
- mView = (SnackbarBaseLayout) inflater.inflate(
- R.layout.design_layout_snackbar, mTargetParent, false);
- mView.addView(content);
-
- ViewCompat.setAccessibilityLiveRegion(mView,
- ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
- ViewCompat.setImportantForAccessibility(mView,
- ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
-
- // Make sure that we fit system windows and have a listener to apply any insets
- ViewCompat.setFitsSystemWindows(mView, true);
- ViewCompat.setOnApplyWindowInsetsListener(mView,
- new android.support.v4.view.OnApplyWindowInsetsListener() {
- @Override
- public WindowInsetsCompat onApplyWindowInsets(View v,
- WindowInsetsCompat insets) {
- // Copy over the bottom inset as padding so that we're displayed
- // above the navigation bar
- v.setPadding(v.getPaddingLeft(), v.getPaddingTop(),
- v.getPaddingRight(), insets.getSystemWindowInsetBottom());
- return insets;
- }
- });
-
- mAccessibilityManager = (AccessibilityManager)
- mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- }
-
- /**
- * Set how long to show the view for.
- *
- * @param duration either be one of the predefined lengths:
- * {@link #LENGTH_SHORT}, {@link #LENGTH_LONG}, or a custom duration
- * in milliseconds.
- */
- @NonNull
- public B setDuration(@Duration int duration) {
- mDuration = duration;
- return (B) this;
- }
-
- /**
- * Return the duration.
- *
- * @see #setDuration
- */
- @Duration
- public int getDuration() {
- return mDuration;
- }
-
- /**
- * Returns the {@link BaseTransientBottomBar}'s context.
- */
- @NonNull
- public Context getContext() {
- return mContext;
- }
-
- /**
- * Returns the {@link BaseTransientBottomBar}'s view.
- */
- @NonNull
- public View getView() {
- return mView;
- }
-
- /**
- * Show the {@link BaseTransientBottomBar}.
- */
- public void show() {
- SnackbarManager.getInstance().show(mDuration, mManagerCallback);
- }
-
- /**
- * Dismiss the {@link BaseTransientBottomBar}.
- */
- public void dismiss() {
- dispatchDismiss(BaseCallback.DISMISS_EVENT_MANUAL);
- }
-
- void dispatchDismiss(@BaseCallback.DismissEvent int event) {
- SnackbarManager.getInstance().dismiss(mManagerCallback, event);
- }
-
- /**
- * Adds the specified callback to the list of callbacks that will be notified of transient
- * bottom bar events.
- *
- * @param callback Callback to notify when transient bottom bar events occur.
- * @see #removeCallback(BaseCallback)
- */
- @NonNull
- public B addCallback(@NonNull BaseCallback<B> callback) {
- if (callback == null) {
- return (B) this;
- }
- if (mCallbacks == null) {
- mCallbacks = new ArrayList<BaseCallback<B>>();
- }
- mCallbacks.add(callback);
- return (B) this;
- }
-
- /**
- * Removes the specified callback from the list of callbacks that will be notified of transient
- * bottom bar events.
- *
- * @param callback Callback to remove from being notified of transient bottom bar events
- * @see #addCallback(BaseCallback)
- */
- @NonNull
- public B removeCallback(@NonNull BaseCallback<B> callback) {
- if (callback == null) {
- return (B) this;
- }
- if (mCallbacks == null) {
- // This can happen if this method is called before the first call to addCallback
- return (B) this;
- }
- mCallbacks.remove(callback);
- return (B) this;
- }
-
- /**
- * Return whether this {@link BaseTransientBottomBar} is currently being shown.
- */
- public boolean isShown() {
- return SnackbarManager.getInstance().isCurrent(mManagerCallback);
- }
-
- /**
- * Returns whether this {@link BaseTransientBottomBar} is currently being shown, or is queued
- * to be shown next.
- */
- public boolean isShownOrQueued() {
- return SnackbarManager.getInstance().isCurrentOrNext(mManagerCallback);
- }
-
- final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
- @Override
- public void show() {
- sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW, BaseTransientBottomBar.this));
- }
-
- @Override
- public void dismiss(int event) {
- sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, event, 0,
- BaseTransientBottomBar.this));
- }
- };
-
- final void showView() {
- if (mView.getParent() == null) {
- final ViewGroup.LayoutParams lp = mView.getLayoutParams();
-
- if (lp instanceof CoordinatorLayout.LayoutParams) {
- // If our LayoutParams are from a CoordinatorLayout, we'll setup our Behavior
- final CoordinatorLayout.LayoutParams clp = (CoordinatorLayout.LayoutParams) lp;
-
- final Behavior behavior = new Behavior();
- behavior.setStartAlphaSwipeDistance(0.1f);
- behavior.setEndAlphaSwipeDistance(0.6f);
- behavior.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_START_TO_END);
- behavior.setListener(new SwipeDismissBehavior.OnDismissListener() {
- @Override
- public void onDismiss(View view) {
- view.setVisibility(View.GONE);
- dispatchDismiss(BaseCallback.DISMISS_EVENT_SWIPE);
- }
-
- @Override
- public void onDragStateChanged(int state) {
- switch (state) {
- case SwipeDismissBehavior.STATE_DRAGGING:
- case SwipeDismissBehavior.STATE_SETTLING:
- // If the view is being dragged or settling, pause the timeout
- SnackbarManager.getInstance().pauseTimeout(mManagerCallback);
- break;
- case SwipeDismissBehavior.STATE_IDLE:
- // If the view has been released and is idle, restore the timeout
- SnackbarManager.getInstance()
- .restoreTimeoutIfPaused(mManagerCallback);
- break;
- }
- }
- });
- clp.setBehavior(behavior);
- // Also set the inset edge so that views can dodge the bar correctly
- clp.insetEdge = Gravity.BOTTOM;
- }
-
- mTargetParent.addView(mView);
- }
-
- mView.setOnAttachStateChangeListener(
- new BaseTransientBottomBar.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {}
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- if (isShownOrQueued()) {
- // If we haven't already been dismissed then this event is coming from a
- // non-user initiated action. Hence we need to make sure that we callback
- // and keep our state up to date. We need to post the call since
- // removeView() will call through to onDetachedFromWindow and thus overflow.
- sHandler.post(new Runnable() {
- @Override
- public void run() {
- onViewHidden(BaseCallback.DISMISS_EVENT_MANUAL);
- }
- });
- }
- }
- });
-
- if (ViewCompat.isLaidOut(mView)) {
- if (shouldAnimate()) {
- // If animations are enabled, animate it in
- animateViewIn();
- } else {
- // Else if anims are disabled just call back now
- onViewShown();
- }
- } else {
- // Otherwise, add one of our layout change listeners and show it in when laid out
- mView.setOnLayoutChangeListener(new BaseTransientBottomBar.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View view, int left, int top, int right, int bottom) {
- mView.setOnLayoutChangeListener(null);
-
- if (shouldAnimate()) {
- // If animations are enabled, animate it in
- animateViewIn();
- } else {
- // Else if anims are disabled just call back now
- onViewShown();
- }
- }
- });
- }
- }
-
- void animateViewIn() {
- if (Build.VERSION.SDK_INT >= 12) {
- final int viewHeight = mView.getHeight();
- if (USE_OFFSET_API) {
- ViewCompat.offsetTopAndBottom(mView, viewHeight);
- } else {
- mView.setTranslationY(viewHeight);
- }
- final ValueAnimator animator = new ValueAnimator();
- animator.setIntValues(viewHeight, 0);
- animator.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
- animator.setDuration(ANIMATION_DURATION);
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- mContentViewCallback.animateContentIn(
- ANIMATION_DURATION - ANIMATION_FADE_DURATION,
- ANIMATION_FADE_DURATION);
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- onViewShown();
- }
- });
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- private int mPreviousAnimatedIntValue = viewHeight;
-
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- int currentAnimatedIntValue = (int) animator.getAnimatedValue();
- if (USE_OFFSET_API) {
- ViewCompat.offsetTopAndBottom(mView,
- currentAnimatedIntValue - mPreviousAnimatedIntValue);
- } else {
- mView.setTranslationY(currentAnimatedIntValue);
- }
- mPreviousAnimatedIntValue = currentAnimatedIntValue;
- }
- });
- animator.start();
- } else {
- final Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
- R.anim.design_snackbar_in);
- anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
- anim.setDuration(ANIMATION_DURATION);
- anim.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationEnd(Animation animation) {
- onViewShown();
- }
-
- @Override
- public void onAnimationStart(Animation animation) {}
-
- @Override
- public void onAnimationRepeat(Animation animation) {}
- });
- mView.startAnimation(anim);
- }
- }
-
- private void animateViewOut(final int event) {
- if (Build.VERSION.SDK_INT >= 12) {
- final ValueAnimator animator = new ValueAnimator();
- animator.setIntValues(0, mView.getHeight());
- animator.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
- animator.setDuration(ANIMATION_DURATION);
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- mContentViewCallback.animateContentOut(0, ANIMATION_FADE_DURATION);
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- onViewHidden(event);
- }
- });
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- private int mPreviousAnimatedIntValue = 0;
-
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- int currentAnimatedIntValue = (int) animator.getAnimatedValue();
- if (USE_OFFSET_API) {
- ViewCompat.offsetTopAndBottom(mView,
- currentAnimatedIntValue - mPreviousAnimatedIntValue);
- } else {
- mView.setTranslationY(currentAnimatedIntValue);
- }
- mPreviousAnimatedIntValue = currentAnimatedIntValue;
- }
- });
- animator.start();
- } else {
- final Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
- R.anim.design_snackbar_out);
- anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
- anim.setDuration(ANIMATION_DURATION);
- anim.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationEnd(Animation animation) {
- onViewHidden(event);
- }
-
- @Override
- public void onAnimationStart(Animation animation) {}
-
- @Override
- public void onAnimationRepeat(Animation animation) {}
- });
- mView.startAnimation(anim);
- }
- }
-
- final void hideView(@BaseCallback.DismissEvent final int event) {
- if (shouldAnimate() && mView.getVisibility() == View.VISIBLE) {
- animateViewOut(event);
- } else {
- // If anims are disabled or the view isn't visible, just call back now
- onViewHidden(event);
- }
- }
-
- void onViewShown() {
- SnackbarManager.getInstance().onShown(mManagerCallback);
- if (mCallbacks != null) {
- // Notify the callbacks. Do that from the end of the list so that if a callback
- // removes itself as the result of being called, it won't mess up with our iteration
- int callbackCount = mCallbacks.size();
- for (int i = callbackCount - 1; i >= 0; i--) {
- mCallbacks.get(i).onShown((B) this);
- }
- }
- }
-
- void onViewHidden(int event) {
- // First tell the SnackbarManager that it has been dismissed
- SnackbarManager.getInstance().onDismissed(mManagerCallback);
- if (mCallbacks != null) {
- // Notify the callbacks. Do that from the end of the list so that if a callback
- // removes itself as the result of being called, it won't mess up with our iteration
- int callbackCount = mCallbacks.size();
- for (int i = callbackCount - 1; i >= 0; i--) {
- mCallbacks.get(i).onDismissed((B) this, event);
- }
- }
- if (Build.VERSION.SDK_INT < 11) {
- // We need to hide the Snackbar on pre-v11 since it uses an old style Animation.
- // ViewGroup has special handling in removeView() when getAnimation() != null in
- // that it waits. This then means that the calculated insets are wrong and the
- // any dodging views do not return. We workaround it by setting the view to gone while
- // ViewGroup actually gets around to removing it.
- mView.setVisibility(View.GONE);
- }
- // Lastly, hide and remove the view from the parent (if attached)
- final ViewParent parent = mView.getParent();
- if (parent instanceof ViewGroup) {
- ((ViewGroup) parent).removeView(mView);
- }
- }
-
- /**
- * Returns true if we should animate the Snackbar view in/out.
- */
- boolean shouldAnimate() {
- return !mAccessibilityManager.isEnabled();
- }
-
- /**
- * @hide
- */
- @RestrictTo(LIBRARY_GROUP)
- static class SnackbarBaseLayout extends FrameLayout {
- private BaseTransientBottomBar.OnLayoutChangeListener mOnLayoutChangeListener;
- private BaseTransientBottomBar.OnAttachStateChangeListener mOnAttachStateChangeListener;
-
- SnackbarBaseLayout(Context context) {
- this(context, null);
- }
-
- SnackbarBaseLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SnackbarLayout);
- if (a.hasValue(R.styleable.SnackbarLayout_elevation)) {
- ViewCompat.setElevation(this, a.getDimensionPixelSize(
- R.styleable.SnackbarLayout_elevation, 0));
- }
- a.recycle();
-
- setClickable(true);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- if (mOnLayoutChangeListener != null) {
- mOnLayoutChangeListener.onLayoutChange(this, l, t, r, b);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mOnAttachStateChangeListener != null) {
- mOnAttachStateChangeListener.onViewAttachedToWindow(this);
- }
-
- ViewCompat.requestApplyInsets(this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mOnAttachStateChangeListener != null) {
- mOnAttachStateChangeListener.onViewDetachedFromWindow(this);
- }
- }
-
- void setOnLayoutChangeListener(
- BaseTransientBottomBar.OnLayoutChangeListener onLayoutChangeListener) {
- mOnLayoutChangeListener = onLayoutChangeListener;
- }
-
- void setOnAttachStateChangeListener(
- BaseTransientBottomBar.OnAttachStateChangeListener listener) {
- mOnAttachStateChangeListener = listener;
- }
- }
-
- final class Behavior extends SwipeDismissBehavior<SnackbarBaseLayout> {
- @Override
- public boolean canSwipeDismissView(View child) {
- return child instanceof SnackbarBaseLayout;
- }
-
- @Override
- public boolean onInterceptTouchEvent(CoordinatorLayout parent, SnackbarBaseLayout child,
- MotionEvent event) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- // We want to make sure that we disable any Snackbar timeouts if the user is
- // currently touching the Snackbar. We restore the timeout when complete
- if (parent.isPointInChildBounds(child, (int) event.getX(),
- (int) event.getY())) {
- SnackbarManager.getInstance().pauseTimeout(mManagerCallback);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- SnackbarManager.getInstance().restoreTimeoutIfPaused(mManagerCallback);
- break;
- }
- return super.onInterceptTouchEvent(parent, child, event);
- }
- }
-}
diff --git a/android/support/design/widget/BottomNavigationView.java b/android/support/design/widget/BottomNavigationView.java
deleted file mode 100644
index 61dba876..00000000
--- a/android/support/design/widget/BottomNavigationView.java
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Copyright (C) 2016 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.design.widget;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.IdRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.design.R;
-import android.support.design.internal.BottomNavigationMenu;
-import android.support.design.internal.BottomNavigationMenuView;
-import android.support.design.internal.BottomNavigationPresenter;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.view.AbsSavedState;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.content.res.AppCompatResources;
-import android.support.v7.view.SupportMenuInflater;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.widget.TintTypedArray;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-/**
- * <p>
- * Represents a standard bottom navigation bar for application. It is an implementation of
- * <a href="https://material.google.com/components/bottom-navigation.html">material design bottom
- * navigation</a>.
- * </p>
- *
- * <p>
- * Bottom navigation bars make it easy for users to explore and switch between top-level views in
- * a single tap. It should be used when application has three to five top-level destinations.
- * </p>
- *
- * <p>
- * The bar contents can be populated by specifying a menu resource file. Each menu item title, icon
- * and enabled state will be used for displaying bottom navigation bar items. Menu items can also be
- * used for programmatically selecting which destination is currently active. It can be done using
- * {@code MenuItem#setChecked(true)}
- * </p>
- *
- * <pre>
- * layout resource file:
- * &lt;android.support.design.widget.BottomNavigationView
- * xmlns:android="http://schemas.android.com/apk/res/android"
- * xmlns:app="http://schemas.android.com/apk/res-auto"
- * android:id="@+id/navigation"
- * android:layout_width="match_parent"
- * android:layout_height="56dp"
- * android:layout_gravity="start"
- * app:menu="@menu/my_navigation_items" /&gt;
- *
- * res/menu/my_navigation_items.xml:
- * &lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
- * &lt;item android:id="@+id/action_search"
- * android:title="@string/menu_search"
- * android:icon="@drawable/ic_search" /&gt;
- * &lt;item android:id="@+id/action_settings"
- * android:title="@string/menu_settings"
- * android:icon="@drawable/ic_add" /&gt;
- * &lt;item android:id="@+id/action_navigation"
- * android:title="@string/menu_navigation"
- * android:icon="@drawable/ic_action_navigation_menu" /&gt;
- * &lt;/menu&gt;
- * </pre>
- */
-public class BottomNavigationView extends FrameLayout {
-
- private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
- private static final int[] DISABLED_STATE_SET = {-android.R.attr.state_enabled};
-
- private static final int MENU_PRESENTER_ID = 1;
-
- private final MenuBuilder mMenu;
- private final BottomNavigationMenuView mMenuView;
- private final BottomNavigationPresenter mPresenter = new BottomNavigationPresenter();
- private MenuInflater mMenuInflater;
-
- private OnNavigationItemSelectedListener mSelectedListener;
- private OnNavigationItemReselectedListener mReselectedListener;
-
- public BottomNavigationView(Context context) {
- this(context, null);
- }
-
- public BottomNavigationView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public BottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- ThemeUtils.checkAppCompatTheme(context);
-
- // Create the menu
- mMenu = new BottomNavigationMenu(context);
-
- mMenuView = new BottomNavigationMenuView(context);
- FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- params.gravity = Gravity.CENTER;
- mMenuView.setLayoutParams(params);
-
- mPresenter.setBottomNavigationMenuView(mMenuView);
- mPresenter.setId(MENU_PRESENTER_ID);
- mMenuView.setPresenter(mPresenter);
- mMenu.addMenuPresenter(mPresenter);
- mPresenter.initForMenu(getContext(), mMenu);
-
- // Custom attributes
- TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
- R.styleable.BottomNavigationView, defStyleAttr,
- R.style.Widget_Design_BottomNavigationView);
-
- if (a.hasValue(R.styleable.BottomNavigationView_itemIconTint)) {
- mMenuView.setIconTintList(
- a.getColorStateList(R.styleable.BottomNavigationView_itemIconTint));
- } else {
- mMenuView.setIconTintList(
- createDefaultColorStateList(android.R.attr.textColorSecondary));
- }
- if (a.hasValue(R.styleable.BottomNavigationView_itemTextColor)) {
- mMenuView.setItemTextColor(
- a.getColorStateList(R.styleable.BottomNavigationView_itemTextColor));
- } else {
- mMenuView.setItemTextColor(
- createDefaultColorStateList(android.R.attr.textColorSecondary));
- }
- if (a.hasValue(R.styleable.BottomNavigationView_elevation)) {
- ViewCompat.setElevation(this, a.getDimensionPixelSize(
- R.styleable.BottomNavigationView_elevation, 0));
- }
-
- int itemBackground = a.getResourceId(R.styleable.BottomNavigationView_itemBackground, 0);
- mMenuView.setItemBackgroundRes(itemBackground);
-
- if (a.hasValue(R.styleable.BottomNavigationView_menu)) {
- inflateMenu(a.getResourceId(R.styleable.BottomNavigationView_menu, 0));
- }
- a.recycle();
-
- addView(mMenuView, params);
- if (Build.VERSION.SDK_INT < 21) {
- addCompatibilityTopDivider(context);
- }
-
- mMenu.setCallback(new MenuBuilder.Callback() {
- @Override
- public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
- if (mReselectedListener != null && item.getItemId() == getSelectedItemId()) {
- mReselectedListener.onNavigationItemReselected(item);
- return true; // item is already selected
- }
- return mSelectedListener != null
- && !mSelectedListener.onNavigationItemSelected(item);
- }
-
- @Override
- public void onMenuModeChange(MenuBuilder menu) {}
- });
- }
-
- /**
- * Set a listener that will be notified when a bottom navigation item is selected. This listener
- * will also be notified when the currently selected item is reselected, unless an
- * {@link OnNavigationItemReselectedListener} has also been set.
- *
- * @param listener The listener to notify
- *
- * @see #setOnNavigationItemReselectedListener(OnNavigationItemReselectedListener)
- */
- public void setOnNavigationItemSelectedListener(
- @Nullable OnNavigationItemSelectedListener listener) {
- mSelectedListener = listener;
- }
-
- /**
- * Set a listener that will be notified when the currently selected bottom navigation item is
- * reselected. This does not require an {@link OnNavigationItemSelectedListener} to be set.
- *
- * @param listener The listener to notify
- *
- * @see #setOnNavigationItemSelectedListener(OnNavigationItemSelectedListener)
- */
- public void setOnNavigationItemReselectedListener(
- @Nullable OnNavigationItemReselectedListener listener) {
- mReselectedListener = listener;
- }
-
- /**
- * Returns the {@link Menu} instance associated with this bottom navigation bar.
- */
- @NonNull
- public Menu getMenu() {
- return mMenu;
- }
-
- /**
- * Inflate a menu resource into this navigation view.
- *
- * <p>Existing items in the menu will not be modified or removed.</p>
- *
- * @param resId ID of a menu resource to inflate
- */
- public void inflateMenu(int resId) {
- mPresenter.setUpdateSuspended(true);
- getMenuInflater().inflate(resId, mMenu);
- mPresenter.setUpdateSuspended(false);
- mPresenter.updateMenuView(true);
- }
-
- /**
- * @return The maximum number of items that can be shown in BottomNavigationView.
- */
- public int getMaxItemCount() {
- return BottomNavigationMenu.MAX_ITEM_COUNT;
- }
-
- /**
- * Returns the tint which is applied to our menu items' icons.
- *
- * @see #setItemIconTintList(ColorStateList)
- *
- * @attr ref R.styleable#BottomNavigationView_itemIconTint
- */
- @Nullable
- public ColorStateList getItemIconTintList() {
- return mMenuView.getIconTintList();
- }
-
- /**
- * Set the tint which is applied to our menu items' icons.
- *
- * @param tint the tint to apply.
- *
- * @attr ref R.styleable#BottomNavigationView_itemIconTint
- */
- public void setItemIconTintList(@Nullable ColorStateList tint) {
- mMenuView.setIconTintList(tint);
- }
-
- /**
- * Returns colors used for the different states (normal, selected, focused, etc.) of the menu
- * item text.
- *
- * @see #setItemTextColor(ColorStateList)
- *
- * @return the ColorStateList of colors used for the different states of the menu items text.
- *
- * @attr ref R.styleable#BottomNavigationView_itemTextColor
- */
- @Nullable
- public ColorStateList getItemTextColor() {
- return mMenuView.getItemTextColor();
- }
-
- /**
- * Set the colors to use for the different states (normal, selected, focused, etc.) of the menu
- * item text.
- *
- * @see #getItemTextColor()
- *
- * @attr ref R.styleable#BottomNavigationView_itemTextColor
- */
- public void setItemTextColor(@Nullable ColorStateList textColor) {
- mMenuView.setItemTextColor(textColor);
- }
-
- /**
- * Returns the background resource of the menu items.
- *
- * @see #setItemBackgroundResource(int)
- *
- * @attr ref R.styleable#BottomNavigationView_itemBackground
- */
- @DrawableRes
- public int getItemBackgroundResource() {
- return mMenuView.getItemBackgroundRes();
- }
-
- /**
- * Set the background of our menu items to the given resource.
- *
- * @param resId The identifier of the resource.
- *
- * @attr ref R.styleable#BottomNavigationView_itemBackground
- */
- public void setItemBackgroundResource(@DrawableRes int resId) {
- mMenuView.setItemBackgroundRes(resId);
- }
-
- /**
- * Returns the currently selected menu item ID, or zero if there is no menu.
- *
- * @see #setSelectedItemId(int)
- */
- @IdRes
- public int getSelectedItemId() {
- return mMenuView.getSelectedItemId();
- }
-
- /**
- * Set the selected menu item ID. This behaves the same as tapping on an item.
- *
- * @param itemId The menu item ID. If no item has this ID, the current selection is unchanged.
- *
- * @see #getSelectedItemId()
- */
- public void setSelectedItemId(@IdRes int itemId) {
- MenuItem item = mMenu.findItem(itemId);
- if (item != null) {
- if (!mMenu.performItemAction(item, mPresenter, 0)) {
- item.setChecked(true);
- }
- }
- }
-
- /**
- * Listener for handling selection events on bottom navigation items.
- */
- public interface OnNavigationItemSelectedListener {
-
- /**
- * Called when an item in the bottom navigation menu is selected.
- *
- * @param item The selected item
- *
- * @return true to display the item as the selected item and false if the item should not
- * be selected. Consider setting non-selectable items as disabled preemptively to
- * make them appear non-interactive.
- */
- boolean onNavigationItemSelected(@NonNull MenuItem item);
- }
-
- /**
- * Listener for handling reselection events on bottom navigation items.
- */
- public interface OnNavigationItemReselectedListener {
-
- /**
- * Called when the currently selected item in the bottom navigation menu is selected again.
- *
- * @param item The selected item
- */
- void onNavigationItemReselected(@NonNull MenuItem item);
- }
-
- private void addCompatibilityTopDivider(Context context) {
- View divider = new View(context);
- divider.setBackgroundColor(
- ContextCompat.getColor(context, R.color.design_bottom_navigation_shadow_color));
- FrameLayout.LayoutParams dividerParams = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- getResources().getDimensionPixelSize(
- R.dimen.design_bottom_navigation_shadow_height));
- divider.setLayoutParams(dividerParams);
- addView(divider);
- }
-
- private MenuInflater getMenuInflater() {
- if (mMenuInflater == null) {
- mMenuInflater = new SupportMenuInflater(getContext());
- }
- return mMenuInflater;
- }
-
- private ColorStateList createDefaultColorStateList(int baseColorThemeAttr) {
- final TypedValue value = new TypedValue();
- if (!getContext().getTheme().resolveAttribute(baseColorThemeAttr, value, true)) {
- return null;
- }
- ColorStateList baseColor = AppCompatResources.getColorStateList(
- getContext(), value.resourceId);
- if (!getContext().getTheme().resolveAttribute(
- android.support.v7.appcompat.R.attr.colorPrimary, value, true)) {
- return null;
- }
- int colorPrimary = value.data;
- int defaultColor = baseColor.getDefaultColor();
- return new ColorStateList(new int[][]{
- DISABLED_STATE_SET,
- CHECKED_STATE_SET,
- EMPTY_STATE_SET
- }, new int[]{
- baseColor.getColorForState(DISABLED_STATE_SET, defaultColor),
- colorPrimary,
- defaultColor
- });
- }
-
- @Override
- protected Parcelable onSaveInstanceState() {
- Parcelable superState = super.onSaveInstanceState();
- SavedState savedState = new SavedState(superState);
- savedState.menuPresenterState = new Bundle();
- mMenu.savePresenterStates(savedState.menuPresenterState);
- return savedState;
- }
-
- @Override
- protected void onRestoreInstanceState(Parcelable state) {
- if (!(state instanceof SavedState)) {
- super.onRestoreInstanceState(state);
- return;
- }
- SavedState savedState = (SavedState) state;
- super.onRestoreInstanceState(savedState.getSuperState());
- mMenu.restorePresenterStates(savedState.menuPresenterState);
- }
-
- static class SavedState extends AbsSavedState {
- Bundle menuPresenterState;
-
- public SavedState(Parcelable superState) {
- super(superState);
- }
-
- public SavedState(Parcel source, ClassLoader loader) {
- super(source, loader);
- readFromParcel(source, loader);
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel out, int flags) {
- super.writeToParcel(out, flags);
- out.writeBundle(menuPresenterState);
- }
-
- private void readFromParcel(Parcel in, ClassLoader loader) {
- menuPresenterState = in.readBundle(loader);
- }
-
- public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
- @Override
- public SavedState createFromParcel(Parcel in, ClassLoader loader) {
- return new SavedState(in, loader);
- }
-
- @Override
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in, null);
- }
-
- @Override
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-}
diff --git a/android/support/design/widget/BottomSheetBehavior.java b/android/support/design/widget/BottomSheetBehavior.java
deleted file mode 100644
index 00ce8f90..00000000
--- a/android/support/design/widget/BottomSheetBehavior.java
+++ /dev/null
@@ -1,829 +0,0 @@
-/*
- * 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.design.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.VisibleForTesting;
-import android.support.design.R;
-import android.support.v4.math.MathUtils;
-import android.support.v4.view.AbsSavedState;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.widget.ViewDragHelper;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-
-
-/**
- * An interaction behavior plugin for a child view of {@link CoordinatorLayout} to make it work as
- * a bottom sheet.
- */
-public class BottomSheetBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {
-
- /**
- * Callback for monitoring events about bottom sheets.
- */
- public abstract static class BottomSheetCallback {
-
- /**
- * Called when the bottom sheet changes its state.
- *
- * @param bottomSheet The bottom sheet view.
- * @param newState The new state. This will be one of {@link #STATE_DRAGGING},
- * {@link #STATE_SETTLING}, {@link #STATE_EXPANDED},
- * {@link #STATE_COLLAPSED}, or {@link #STATE_HIDDEN}.
- */
- public abstract void onStateChanged(@NonNull View bottomSheet, @State int newState);
-
- /**
- * Called when the bottom sheet is being dragged.
- *
- * @param bottomSheet The bottom sheet view.
- * @param slideOffset The new offset of this bottom sheet within [-1,1] range. Offset
- * increases as this bottom sheet is moving upward. From 0 to 1 the sheet
- * is between collapsed and expanded states and from -1 to 0 it is
- * between hidden and collapsed states.
- */
- public abstract void onSlide(@NonNull View bottomSheet, float slideOffset);
- }
-
- /**
- * The bottom sheet is dragging.
- */
- public static final int STATE_DRAGGING = 1;
-
- /**
- * The bottom sheet is settling.
- */
- public static final int STATE_SETTLING = 2;
-
- /**
- * The bottom sheet is expanded.
- */
- public static final int STATE_EXPANDED = 3;
-
- /**
- * The bottom sheet is collapsed.
- */
- public static final int STATE_COLLAPSED = 4;
-
- /**
- * The bottom sheet is hidden.
- */
- public static final int STATE_HIDDEN = 5;
-
- /** @hide */
- @RestrictTo(LIBRARY_GROUP)
- @IntDef({STATE_EXPANDED, STATE_COLLAPSED, STATE_DRAGGING, STATE_SETTLING, STATE_HIDDEN})
- @Retention(RetentionPolicy.SOURCE)
- public @interface State {}
-
- /**
- * Peek at the 16:9 ratio keyline of its parent.
- *
- * <p>This can be used as a parameter for {@link #setPeekHeight(int)}.
- * {@link #getPeekHeight()} will return this when the value is set.</p>
- */
- public static final int PEEK_HEIGHT_AUTO = -1;
-
- private static final float HIDE_THRESHOLD = 0.5f;
-
- private static final float HIDE_FRICTION = 0.1f;
-
- private float mMaximumVelocity;
-
- private int mPeekHeight;
-
- private boolean mPeekHeightAuto;
-
- private int mPeekHeightMin;
-
- int mMinOffset;
-
- int mMaxOffset;
-
- boolean mHideable;
-
- private boolean mSkipCollapsed;
-
- @State
- int mState = STATE_COLLAPSED;
-
- ViewDragHelper mViewDragHelper;
-
- private boolean mIgnoreEvents;
-
- private int mLastNestedScrollDy;
-
- private boolean mNestedScrolled;
-
- int mParentHeight;
-
- WeakReference<V> mViewRef;
-
- WeakReference<View> mNestedScrollingChildRef;
-
- private BottomSheetCallback mCallback;
-
- private VelocityTracker mVelocityTracker;
-
- int mActivePointerId;
-
- private int mInitialY;
-
- boolean mTouchingScrollingChild;
-
- /**
- * Default constructor for instantiating BottomSheetBehaviors.
- */
- public BottomSheetBehavior() {
- }
-
- /**
- * Default constructor for inflating BottomSheetBehaviors from layout.
- *
- * @param context The {@link Context}.
- * @param attrs The {@link AttributeSet}.
- */
- public BottomSheetBehavior(Context context, AttributeSet attrs) {
- super(context, attrs);
- TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.BottomSheetBehavior_Layout);
- TypedValue value = a.peekValue(R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight);
- if (value != null && value.data == PEEK_HEIGHT_AUTO) {
- setPeekHeight(value.data);
- } else {
- setPeekHeight(a.getDimensionPixelSize(
- R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight, PEEK_HEIGHT_AUTO));
- }
- setHideable(a.getBoolean(R.styleable.BottomSheetBehavior_Layout_behavior_hideable, false));
- setSkipCollapsed(a.getBoolean(R.styleable.BottomSheetBehavior_Layout_behavior_skipCollapsed,
- false));
- a.recycle();
- ViewConfiguration configuration = ViewConfiguration.get(context);
- mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
- }
-
- @Override
- public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) {
- return new SavedState(super.onSaveInstanceState(parent, child), mState);
- }
-
- @Override
- public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) {
- SavedState ss = (SavedState) state;
- super.onRestoreInstanceState(parent, child, ss.getSuperState());
- // Intermediate states are restored as collapsed state
- if (ss.state == STATE_DRAGGING || ss.state == STATE_SETTLING) {
- mState = STATE_COLLAPSED;
- } else {
- mState = ss.state;
- }
- }
-
- @Override
- public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
- if (ViewCompat.getFitsSystemWindows(parent) && !ViewCompat.getFitsSystemWindows(child)) {
- ViewCompat.setFitsSystemWindows(child, true);
- }
- int savedTop = child.getTop();
- // First let the parent lay it out
- parent.onLayoutChild(child, layoutDirection);
- // Offset the bottom sheet
- mParentHeight = parent.getHeight();
- int peekHeight;
- if (mPeekHeightAuto) {
- if (mPeekHeightMin == 0) {
- mPeekHeightMin = parent.getResources().getDimensionPixelSize(
- R.dimen.design_bottom_sheet_peek_height_min);
- }
- peekHeight = Math.max(mPeekHeightMin, mParentHeight - parent.getWidth() * 9 / 16);
- } else {
- peekHeight = mPeekHeight;
- }
- mMinOffset = Math.max(0, mParentHeight - child.getHeight());
- mMaxOffset = Math.max(mParentHeight - peekHeight, mMinOffset);
- if (mState == STATE_EXPANDED) {
- ViewCompat.offsetTopAndBottom(child, mMinOffset);
- } else if (mHideable && mState == STATE_HIDDEN) {
- ViewCompat.offsetTopAndBottom(child, mParentHeight);
- } else if (mState == STATE_COLLAPSED) {
- ViewCompat.offsetTopAndBottom(child, mMaxOffset);
- } else if (mState == STATE_DRAGGING || mState == STATE_SETTLING) {
- ViewCompat.offsetTopAndBottom(child, savedTop - child.getTop());
- }
- if (mViewDragHelper == null) {
- mViewDragHelper = ViewDragHelper.create(parent, mDragCallback);
- }
- mViewRef = new WeakReference<>(child);
- mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(child));
- return true;
- }
-
- @Override
- public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
- if (!child.isShown()) {
- mIgnoreEvents = true;
- return false;
- }
- int action = event.getActionMasked();
- // Record the velocity
- if (action == MotionEvent.ACTION_DOWN) {
- reset();
- }
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(event);
- switch (action) {
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mTouchingScrollingChild = false;
- mActivePointerId = MotionEvent.INVALID_POINTER_ID;
- // Reset the ignore flag
- if (mIgnoreEvents) {
- mIgnoreEvents = false;
- return false;
- }
- break;
- case MotionEvent.ACTION_DOWN:
- int initialX = (int) event.getX();
- mInitialY = (int) event.getY();
- View scroll = mNestedScrollingChildRef != null
- ? mNestedScrollingChildRef.get() : null;
- if (scroll != null && parent.isPointInChildBounds(scroll, initialX, mInitialY)) {
- mActivePointerId = event.getPointerId(event.getActionIndex());
- mTouchingScrollingChild = true;
- }
- mIgnoreEvents = mActivePointerId == MotionEvent.INVALID_POINTER_ID &&
- !parent.isPointInChildBounds(child, initialX, mInitialY);
- break;
- }
- if (!mIgnoreEvents && mViewDragHelper.shouldInterceptTouchEvent(event)) {
- return true;
- }
- // We have to handle cases that the ViewDragHelper does not capture the bottom sheet because
- // it is not the top most view of its parent. This is not necessary when the touch event is
- // happening over the scrolling content as nested scrolling logic handles that case.
- View scroll = mNestedScrollingChildRef.get();
- return action == MotionEvent.ACTION_MOVE && scroll != null &&
- !mIgnoreEvents && mState != STATE_DRAGGING &&
- !parent.isPointInChildBounds(scroll, (int) event.getX(), (int) event.getY()) &&
- Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop();
- }
-
- @Override
- public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
- if (!child.isShown()) {
- return false;
- }
- int action = event.getActionMasked();
- if (mState == STATE_DRAGGING && action == MotionEvent.ACTION_DOWN) {
- return true;
- }
- if (mViewDragHelper != null) {
- mViewDragHelper.processTouchEvent(event);
- }
- // Record the velocity
- if (action == MotionEvent.ACTION_DOWN) {
- reset();
- }
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(event);
- // The ViewDragHelper tries to capture only the top-most View. We have to explicitly tell it
- // to capture the bottom sheet in case it is not captured and the touch slop is passed.
- if (action == MotionEvent.ACTION_MOVE && !mIgnoreEvents) {
- if (Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop()) {
- mViewDragHelper.captureChildView(child, event.getPointerId(event.getActionIndex()));
- }
- }
- return !mIgnoreEvents;
- }
-
- @Override
- public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child,
- View directTargetChild, View target, int nestedScrollAxes) {
- mLastNestedScrollDy = 0;
- mNestedScrolled = false;
- return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
- }
-
- @Override
- public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx,
- int dy, int[] consumed) {
- View scrollingChild = mNestedScrollingChildRef.get();
- if (target != scrollingChild) {
- return;
- }
- int currentTop = child.getTop();
- int newTop = currentTop - dy;
- if (dy > 0) { // Upward
- if (newTop < mMinOffset) {
- consumed[1] = currentTop - mMinOffset;
- ViewCompat.offsetTopAndBottom(child, -consumed[1]);
- setStateInternal(STATE_EXPANDED);
- } else {
- consumed[1] = dy;
- ViewCompat.offsetTopAndBottom(child, -dy);
- setStateInternal(STATE_DRAGGING);
- }
- } else if (dy < 0) { // Downward
- if (!target.canScrollVertically(-1)) {
- if (newTop <= mMaxOffset || mHideable) {
- consumed[1] = dy;
- ViewCompat.offsetTopAndBottom(child, -dy);
- setStateInternal(STATE_DRAGGING);
- } else {
- consumed[1] = currentTop - mMaxOffset;
- ViewCompat.offsetTopAndBottom(child, -consumed[1]);
- setStateInternal(STATE_COLLAPSED);
- }
- }
- }
- dispatchOnSlide(child.getTop());
- mLastNestedScrollDy = dy;
- mNestedScrolled = true;
- }
-
- @Override
- public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
- if (child.getTop() == mMinOffset) {
- setStateInternal(STATE_EXPANDED);
- return;
- }
- if (mNestedScrollingChildRef == null || target != mNestedScrollingChildRef.get()
- || !mNestedScrolled) {
- return;
- }
- int top;
- int targetState;
- if (mLastNestedScrollDy > 0) {
- top = mMinOffset;
- targetState = STATE_EXPANDED;
- } else if (mHideable && shouldHide(child, getYVelocity())) {
- top = mParentHeight;
- targetState = STATE_HIDDEN;
- } else if (mLastNestedScrollDy == 0) {
- int currentTop = child.getTop();
- if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) {
- top = mMinOffset;
- targetState = STATE_EXPANDED;
- } else {
- top = mMaxOffset;
- targetState = STATE_COLLAPSED;
- }
- } else {
- top = mMaxOffset;
- targetState = STATE_COLLAPSED;
- }
- if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
- setStateInternal(STATE_SETTLING);
- ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState));
- } else {
- setStateInternal(targetState);
- }
- mNestedScrolled = false;
- }
-
- @Override
- public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target,
- float velocityX, float velocityY) {
- return target == mNestedScrollingChildRef.get() &&
- (mState != STATE_EXPANDED ||
- super.onNestedPreFling(coordinatorLayout, child, target,
- velocityX, velocityY));
- }
-
- /**
- * Sets the height of the bottom sheet when it is collapsed.
- *
- * @param peekHeight The height of the collapsed bottom sheet in pixels, or
- * {@link #PEEK_HEIGHT_AUTO} to configure the sheet to peek automatically
- * at 16:9 ratio keyline.
- * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_peekHeight
- */
- public final void setPeekHeight(int peekHeight) {
- boolean layout = false;
- if (peekHeight == PEEK_HEIGHT_AUTO) {
- if (!mPeekHeightAuto) {
- mPeekHeightAuto = true;
- layout = true;
- }
- } else if (mPeekHeightAuto || mPeekHeight != peekHeight) {
- mPeekHeightAuto = false;
- mPeekHeight = Math.max(0, peekHeight);
- mMaxOffset = mParentHeight - peekHeight;
- layout = true;
- }
- if (layout && mState == STATE_COLLAPSED && mViewRef != null) {
- V view = mViewRef.get();
- if (view != null) {
- view.requestLayout();
- }
- }
- }
-
- /**
- * Gets the height of the bottom sheet when it is collapsed.
- *
- * @return The height of the collapsed bottom sheet in pixels, or {@link #PEEK_HEIGHT_AUTO}
- * if the sheet is configured to peek automatically at 16:9 ratio keyline
- * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_peekHeight
- */
- public final int getPeekHeight() {
- return mPeekHeightAuto ? PEEK_HEIGHT_AUTO : mPeekHeight;
- }
-
- /**
- * Sets whether this bottom sheet can hide when it is swiped down.
- *
- * @param hideable {@code true} to make this bottom sheet hideable.
- * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_hideable
- */
- public void setHideable(boolean hideable) {
- mHideable = hideable;
- }
-
- /**
- * Gets whether this bottom sheet can hide when it is swiped down.
- *
- * @return {@code true} if this bottom sheet can hide.
- * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_hideable
- */
- public boolean isHideable() {
- return mHideable;
- }
-
- /**
- * Sets whether this bottom sheet should skip the collapsed state when it is being hidden
- * after it is expanded once. Setting this to true has no effect unless the sheet is hideable.
- *
- * @param skipCollapsed True if the bottom sheet should skip the collapsed state.
- * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_skipCollapsed
- */
- public void setSkipCollapsed(boolean skipCollapsed) {
- mSkipCollapsed = skipCollapsed;
- }
-
- /**
- * Sets whether this bottom sheet should skip the collapsed state when it is being hidden
- * after it is expanded once.
- *
- * @return Whether the bottom sheet should skip the collapsed state.
- * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_skipCollapsed
- */
- public boolean getSkipCollapsed() {
- return mSkipCollapsed;
- }
-
- /**
- * Sets a callback to be notified of bottom sheet events.
- *
- * @param callback The callback to notify when bottom sheet events occur.
- */
- public void setBottomSheetCallback(BottomSheetCallback callback) {
- mCallback = callback;
- }
-
- /**
- * Sets the state of the bottom sheet. The bottom sheet will transition to that state with
- * animation.
- *
- * @param state One of {@link #STATE_COLLAPSED}, {@link #STATE_EXPANDED}, or
- * {@link #STATE_HIDDEN}.
- */
- public final void setState(final @State int state) {
- if (state == mState) {
- return;
- }
- if (mViewRef == null) {
- // The view is not laid out yet; modify mState and let onLayoutChild handle it later
- if (state == STATE_COLLAPSED || state == STATE_EXPANDED ||
- (mHideable && state == STATE_HIDDEN)) {
- mState = state;
- }
- return;
- }
- final V child = mViewRef.get();
- if (child == null) {
- return;
- }
- // Start the animation; wait until a pending layout if there is one.
- ViewParent parent = child.getParent();
- if (parent != null && parent.isLayoutRequested() && ViewCompat.isAttachedToWindow(child)) {
- child.post(new Runnable() {
- @Override
- public void run() {
- startSettlingAnimation(child, state);
- }
- });
- } else {
- startSettlingAnimation(child, state);
- }
- }
-
- /**
- * Gets the current state of the bottom sheet.
- *
- * @return One of {@link #STATE_EXPANDED}, {@link #STATE_COLLAPSED}, {@link #STATE_DRAGGING},
- * {@link #STATE_SETTLING}, and {@link #STATE_HIDDEN}.
- */
- @State
- public final int getState() {
- return mState;
- }
-
- void setStateInternal(@State int state) {
- if (mState == state) {
- return;
- }
- mState = state;
- View bottomSheet = mViewRef.get();
- if (bottomSheet != null && mCallback != null) {
- mCallback.onStateChanged(bottomSheet, state);
- }
- }
-
- private void reset() {
- mActivePointerId = ViewDragHelper.INVALID_POINTER;
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- }
-
- boolean shouldHide(View child, float yvel) {
- if (mSkipCollapsed) {
- return true;
- }
- if (child.getTop() < mMaxOffset) {
- // It should not hide, but collapse.
- return false;
- }
- final float newTop = child.getTop() + yvel * HIDE_FRICTION;
- return Math.abs(newTop - mMaxOffset) / (float) mPeekHeight > HIDE_THRESHOLD;
- }
-
- @VisibleForTesting
- View findScrollingChild(View view) {
- if (ViewCompat.isNestedScrollingEnabled(view)) {
- return view;
- }
- if (view instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) view;
- for (int i = 0, count = group.getChildCount(); i < count; i++) {
- View scrollingChild = findScrollingChild(group.getChildAt(i));
- if (scrollingChild != null) {
- return scrollingChild;
- }
- }
- }
- return null;
- }
-
- private float getYVelocity() {
- mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- return mVelocityTracker.getYVelocity(mActivePointerId);
- }
-
- void startSettlingAnimation(View child, int state) {
- int top;
- if (state == STATE_COLLAPSED) {
- top = mMaxOffset;
- } else if (state == STATE_EXPANDED) {
- top = mMinOffset;
- } else if (mHideable && state == STATE_HIDDEN) {
- top = mParentHeight;
- } else {
- throw new IllegalArgumentException("Illegal state argument: " + state);
- }
- if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
- setStateInternal(STATE_SETTLING);
- ViewCompat.postOnAnimation(child, new SettleRunnable(child, state));
- } else {
- setStateInternal(state);
- }
- }
-
- private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() {
-
- @Override
- public boolean tryCaptureView(View child, int pointerId) {
- if (mState == STATE_DRAGGING) {
- return false;
- }
- if (mTouchingScrollingChild) {
- return false;
- }
- if (mState == STATE_EXPANDED && mActivePointerId == pointerId) {
- View scroll = mNestedScrollingChildRef.get();
- if (scroll != null && scroll.canScrollVertically(-1)) {
- // Let the content scroll up
- return false;
- }
- }
- return mViewRef != null && mViewRef.get() == child;
- }
-
- @Override
- public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
- dispatchOnSlide(top);
- }
-
- @Override
- public void onViewDragStateChanged(int state) {
- if (state == ViewDragHelper.STATE_DRAGGING) {
- setStateInternal(STATE_DRAGGING);
- }
- }
-
- @Override
- public void onViewReleased(View releasedChild, float xvel, float yvel) {
- int top;
- @State int targetState;
- if (yvel < 0) { // Moving up
- top = mMinOffset;
- targetState = STATE_EXPANDED;
- } else if (mHideable && shouldHide(releasedChild, yvel)) {
- top = mParentHeight;
- targetState = STATE_HIDDEN;
- } else if (yvel == 0.f) {
- int currentTop = releasedChild.getTop();
- if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) {
- top = mMinOffset;
- targetState = STATE_EXPANDED;
- } else {
- top = mMaxOffset;
- targetState = STATE_COLLAPSED;
- }
- } else {
- top = mMaxOffset;
- targetState = STATE_COLLAPSED;
- }
- if (mViewDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top)) {
- setStateInternal(STATE_SETTLING);
- ViewCompat.postOnAnimation(releasedChild,
- new SettleRunnable(releasedChild, targetState));
- } else {
- setStateInternal(targetState);
- }
- }
-
- @Override
- public int clampViewPositionVertical(View child, int top, int dy) {
- return MathUtils.clamp(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset);
- }
-
- @Override
- public int clampViewPositionHorizontal(View child, int left, int dx) {
- return child.getLeft();
- }
-
- @Override
- public int getViewVerticalDragRange(View child) {
- if (mHideable) {
- return mParentHeight - mMinOffset;
- } else {
- return mMaxOffset - mMinOffset;
- }
- }
- };
-
- void dispatchOnSlide(int top) {
- View bottomSheet = mViewRef.get();
- if (bottomSheet != null && mCallback != null) {
- if (top > mMaxOffset) {
- mCallback.onSlide(bottomSheet, (float) (mMaxOffset - top) /
- (mParentHeight - mMaxOffset));
- } else {
- mCallback.onSlide(bottomSheet,
- (float) (mMaxOffset - top) / ((mMaxOffset - mMinOffset)));
- }
- }
- }
-
- @VisibleForTesting
- int getPeekHeightMin() {
- return mPeekHeightMin;
- }
-
- private class SettleRunnable implements Runnable {
-
- private final View mView;
-
- @State
- private final int mTargetState;
-
- SettleRunnable(View view, @State int targetState) {
- mView = view;
- mTargetState = targetState;
- }
-
- @Override
- public void run() {
- if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
- ViewCompat.postOnAnimation(mView, this);
- } else {
- setStateInternal(mTargetState);
- }
- }
- }
-
- protected static class SavedState extends AbsSavedState {
- @State
- final int state;
-
- public SavedState(Parcel source) {
- this(source, null);
- }
-
- public SavedState(Parcel source, ClassLoader loader) {
- super(source, loader);
- //noinspection ResourceType
- state = source.readInt();
- }
-
- public SavedState(Parcelable superState, @State int state) {
- super(superState);
- this.state = state;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- super.writeToParcel(out, flags);
- out.writeInt(state);
- }
-
- public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
- @Override
- public SavedState createFromParcel(Parcel in, ClassLoader loader) {
- return new SavedState(in, loader);
- }
-
- @Override
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in, null);
- }
-
- @Override
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- /**
- * A utility function to get the {@link BottomSheetBehavior} associated with the {@code view}.
- *
- * @param view The {@link View} with {@link BottomSheetBehavior}.
- * @return The {@link BottomSheetBehavior} associated with the {@code view}.
- */
- @SuppressWarnings("unchecked")
- public static <V extends View> BottomSheetBehavior<V> from(V view) {
- ViewGroup.LayoutParams params = view.getLayoutParams();
- if (!(params instanceof CoordinatorLayout.LayoutParams)) {
- throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
- }
- CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params)
- .getBehavior();
- if (!(behavior instanceof BottomSheetBehavior)) {
- throw new IllegalArgumentException(
- "The view is not associated with BottomSheetBehavior");
- }
- return (BottomSheetBehavior<V>) behavior;
- }
-
-}
diff --git a/android/support/design/widget/BottomSheetDialog.java b/android/support/design/widget/BottomSheetDialog.java
deleted file mode 100644
index 19b5782d..00000000
--- a/android/support/design/widget/BottomSheetDialog.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.StyleRes;
-import android.support.design.R;
-import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v7.app.AppCompatDialog;
-import android.util.TypedValue;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-
-/**
- * Base class for {@link android.app.Dialog}s styled as a bottom sheet.
- */
-public class BottomSheetDialog extends AppCompatDialog {
-
- private BottomSheetBehavior<FrameLayout> mBehavior;
-
- boolean mCancelable = true;
- private boolean mCanceledOnTouchOutside = true;
- private boolean mCanceledOnTouchOutsideSet;
-
- public BottomSheetDialog(@NonNull Context context) {
- this(context, 0);
- }
-
- public BottomSheetDialog(@NonNull Context context, @StyleRes int theme) {
- super(context, getThemeResId(context, theme));
- // We hide the title bar for any style configuration. Otherwise, there will be a gap
- // above the bottom sheet when it is expanded.
- supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
- }
-
- protected BottomSheetDialog(@NonNull Context context, boolean cancelable,
- OnCancelListener cancelListener) {
- super(context, cancelable, cancelListener);
- supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
- mCancelable = cancelable;
- }
-
- @Override
- public void setContentView(@LayoutRes int layoutResId) {
- super.setContentView(wrapInBottomSheet(layoutResId, null, null));
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Window window = getWindow();
- if (window != null) {
- if (Build.VERSION.SDK_INT >= 21) {
- window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- }
- window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
- }
- }
-
- @Override
- public void setContentView(View view) {
- super.setContentView(wrapInBottomSheet(0, view, null));
- }
-
- @Override
- public void setContentView(View view, ViewGroup.LayoutParams params) {
- super.setContentView(wrapInBottomSheet(0, view, params));
- }
-
- @Override
- public void setCancelable(boolean cancelable) {
- super.setCancelable(cancelable);
- if (mCancelable != cancelable) {
- mCancelable = cancelable;
- if (mBehavior != null) {
- mBehavior.setHideable(cancelable);
- }
- }
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- if (mBehavior != null) {
- mBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
- }
- }
-
- @Override
- public void setCanceledOnTouchOutside(boolean cancel) {
- super.setCanceledOnTouchOutside(cancel);
- if (cancel && !mCancelable) {
- mCancelable = true;
- }
- mCanceledOnTouchOutside = cancel;
- mCanceledOnTouchOutsideSet = true;
- }
-
- private View wrapInBottomSheet(int layoutResId, View view, ViewGroup.LayoutParams params) {
- final FrameLayout container = (FrameLayout) View.inflate(getContext(),
- R.layout.design_bottom_sheet_dialog, null);
- final CoordinatorLayout coordinator =
- (CoordinatorLayout) container.findViewById(R.id.coordinator);
- if (layoutResId != 0 && view == null) {
- view = getLayoutInflater().inflate(layoutResId, coordinator, false);
- }
- FrameLayout bottomSheet = (FrameLayout) coordinator.findViewById(R.id.design_bottom_sheet);
- mBehavior = BottomSheetBehavior.from(bottomSheet);
- mBehavior.setBottomSheetCallback(mBottomSheetCallback);
- mBehavior.setHideable(mCancelable);
- if (params == null) {
- bottomSheet.addView(view);
- } else {
- bottomSheet.addView(view, params);
- }
- // We treat the CoordinatorLayout as outside the dialog though it is technically inside
- coordinator.findViewById(R.id.touch_outside).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mCancelable && isShowing() && shouldWindowCloseOnTouchOutside()) {
- cancel();
- }
- }
- });
- // Handle accessibility events
- ViewCompat.setAccessibilityDelegate(bottomSheet, new AccessibilityDelegateCompat() {
- @Override
- public void onInitializeAccessibilityNodeInfo(View host,
- AccessibilityNodeInfoCompat info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- if (mCancelable) {
- info.addAction(AccessibilityNodeInfoCompat.ACTION_DISMISS);
- info.setDismissable(true);
- } else {
- info.setDismissable(false);
- }
- }
-
- @Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
- if (action == AccessibilityNodeInfoCompat.ACTION_DISMISS && mCancelable) {
- cancel();
- return true;
- }
- return super.performAccessibilityAction(host, action, args);
- }
- });
- bottomSheet.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- // Consume the event and prevent it from falling through
- return true;
- }
- });
- return container;
- }
-
- boolean shouldWindowCloseOnTouchOutside() {
- if (!mCanceledOnTouchOutsideSet) {
- if (Build.VERSION.SDK_INT < 11) {
- mCanceledOnTouchOutside = true;
- } else {
- TypedArray a = getContext().obtainStyledAttributes(
- new int[]{android.R.attr.windowCloseOnTouchOutside});
- mCanceledOnTouchOutside = a.getBoolean(0, true);
- a.recycle();
- }
- mCanceledOnTouchOutsideSet = true;
- }
- return mCanceledOnTouchOutside;
- }
-
- 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();
- if (context.getTheme().resolveAttribute(
- R.attr.bottomSheetDialogTheme, outValue, true)) {
- themeId = outValue.resourceId;
- } else {
- // bottomSheetDialogTheme is not provided; we default to our light theme
- themeId = R.style.Theme_Design_Light_BottomSheetDialog;
- }
- }
- return themeId;
- }
-
- private BottomSheetBehavior.BottomSheetCallback mBottomSheetCallback
- = new BottomSheetBehavior.BottomSheetCallback() {
- @Override
- public void onStateChanged(@NonNull View bottomSheet,
- @BottomSheetBehavior.State int newState) {
- if (newState == BottomSheetBehavior.STATE_HIDDEN) {
- cancel();
- }
- }
-
- @Override
- public void onSlide(@NonNull View bottomSheet, float slideOffset) {
- }
- };
-
-}
diff --git a/android/support/design/widget/BottomSheetDialogFragment.java b/android/support/design/widget/BottomSheetDialogFragment.java
deleted file mode 100644
index 88429880..00000000
--- a/android/support/design/widget/BottomSheetDialogFragment.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.app.Dialog;
-import android.os.Bundle;
-import android.support.v4.app.DialogFragment;
-import android.support.v7.app.AppCompatDialogFragment;
-
-/**
- * Modal bottom sheet. This is a version of {@link DialogFragment} that shows a bottom sheet
- * using {@link BottomSheetDialog} instead of a floating dialog.
- */
-public class BottomSheetDialogFragment extends AppCompatDialogFragment {
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- return new BottomSheetDialog(getContext(), getTheme());
- }
-
-}
diff --git a/android/support/design/widget/CheckableImageButton.java b/android/support/design/widget/CheckableImageButton.java
deleted file mode 100644
index f2745817..00000000
--- a/android/support/design/widget/CheckableImageButton.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2016 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.design.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v7.widget.AppCompatImageButton;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.Checkable;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class CheckableImageButton extends AppCompatImageButton implements Checkable {
-
- private static final int[] DRAWABLE_STATE_CHECKED = new int[]{android.R.attr.state_checked};
-
- private boolean mChecked;
-
- public CheckableImageButton(Context context) {
- this(context, null);
- }
-
- public CheckableImageButton(Context context, AttributeSet attrs) {
- this(context, attrs, android.support.v7.appcompat.R.attr.imageButtonStyle);
- }
-
- public CheckableImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegateCompat() {
- @Override
- public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(host, event);
- event.setChecked(isChecked());
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(View host,
- AccessibilityNodeInfoCompat info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- info.setCheckable(true);
- info.setChecked(isChecked());
- }
- });
- }
-
- @Override
- public void setChecked(boolean checked) {
- if (mChecked != checked) {
- mChecked = checked;
- refreshDrawableState();
- sendAccessibilityEvent(
- AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
- }
- }
-
- @Override
- public boolean isChecked() {
- return mChecked;
- }
-
- @Override
- public void toggle() {
- setChecked(!mChecked);
- }
-
- @Override
- public int[] onCreateDrawableState(int extraSpace) {
- if (mChecked) {
- return mergeDrawableStates(
- super.onCreateDrawableState(extraSpace + DRAWABLE_STATE_CHECKED.length),
- DRAWABLE_STATE_CHECKED);
- } else {
- return super.onCreateDrawableState(extraSpace);
- }
- }
-}
diff --git a/android/support/design/widget/CircularBorderDrawable.java b/android/support/design/widget/CircularBorderDrawable.java
deleted file mode 100644
index 617a5010..00000000
--- a/android/support/design/widget/CircularBorderDrawable.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.LinearGradient;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Shader;
-import android.graphics.drawable.Drawable;
-import android.support.v4.graphics.ColorUtils;
-
-/**
- * A drawable which draws an oval 'border'.
- */
-class CircularBorderDrawable extends Drawable {
-
- /**
- * We actually draw the stroke wider than the border size given. This is to reduce any
- * potential transparent space caused by anti-aliasing and padding rounding.
- * This value defines the multiplier used to determine to draw stroke width.
- */
- private static final float DRAW_STROKE_WIDTH_MULTIPLE = 1.3333f;
-
- final Paint mPaint;
- final Rect mRect = new Rect();
- final RectF mRectF = new RectF();
-
- float mBorderWidth;
-
- private int mTopOuterStrokeColor;
- private int mTopInnerStrokeColor;
- private int mBottomOuterStrokeColor;
- private int mBottomInnerStrokeColor;
-
- private ColorStateList mBorderTint;
- private int mCurrentBorderTintColor;
-
- private boolean mInvalidateShader = true;
-
- private float mRotation;
-
- public CircularBorderDrawable() {
- mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mPaint.setStyle(Paint.Style.STROKE);
- }
-
- void setGradientColors(int topOuterStrokeColor, int topInnerStrokeColor,
- int bottomOuterStrokeColor, int bottomInnerStrokeColor) {
- mTopOuterStrokeColor = topOuterStrokeColor;
- mTopInnerStrokeColor = topInnerStrokeColor;
- mBottomOuterStrokeColor = bottomOuterStrokeColor;
- mBottomInnerStrokeColor = bottomInnerStrokeColor;
- }
-
- /**
- * Set the border width
- */
- void setBorderWidth(float width) {
- if (mBorderWidth != width) {
- mBorderWidth = width;
- mPaint.setStrokeWidth(width * DRAW_STROKE_WIDTH_MULTIPLE);
- mInvalidateShader = true;
- invalidateSelf();
- }
- }
-
- @Override
- public void draw(Canvas canvas) {
- if (mInvalidateShader) {
- mPaint.setShader(createGradientShader());
- mInvalidateShader = false;
- }
-
- final float halfBorderWidth = mPaint.getStrokeWidth() / 2f;
- final RectF rectF = mRectF;
-
- // We need to inset the oval bounds by half the border width. This is because stroke draws
- // the center of the border on the dimension. Whereas we want the stroke on the inside.
- copyBounds(mRect);
- rectF.set(mRect);
- rectF.left += halfBorderWidth;
- rectF.top += halfBorderWidth;
- rectF.right -= halfBorderWidth;
- rectF.bottom -= halfBorderWidth;
-
- canvas.save();
- canvas.rotate(mRotation, rectF.centerX(), rectF.centerY());
- // Draw the oval
- canvas.drawOval(rectF, mPaint);
- canvas.restore();
- }
-
- @Override
- public boolean getPadding(Rect padding) {
- final int borderWidth = Math.round(mBorderWidth);
- padding.set(borderWidth, borderWidth, borderWidth, borderWidth);
- return true;
- }
-
- @Override
- public void setAlpha(int alpha) {
- mPaint.setAlpha(alpha);
- invalidateSelf();
- }
-
- void setBorderTint(ColorStateList tint) {
- if (tint != null) {
- mCurrentBorderTintColor = tint.getColorForState(getState(), mCurrentBorderTintColor);
- }
- mBorderTint = tint;
- mInvalidateShader = true;
- invalidateSelf();
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- mPaint.setColorFilter(colorFilter);
- invalidateSelf();
- }
-
- @Override
- public int getOpacity() {
- return mBorderWidth > 0 ? PixelFormat.TRANSLUCENT : PixelFormat.TRANSPARENT;
- }
-
- final void setRotation(float rotation) {
- if (rotation != mRotation) {
- mRotation = rotation;
- invalidateSelf();
- }
- }
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- mInvalidateShader = true;
- }
-
- @Override
- public boolean isStateful() {
- return (mBorderTint != null && mBorderTint.isStateful()) || super.isStateful();
- }
-
- @Override
- protected boolean onStateChange(int[] state) {
- if (mBorderTint != null) {
- final int newColor = mBorderTint.getColorForState(state, mCurrentBorderTintColor);
- if (newColor != mCurrentBorderTintColor) {
- mInvalidateShader = true;
- mCurrentBorderTintColor = newColor;
- }
- }
- if (mInvalidateShader) {
- invalidateSelf();
- }
- return mInvalidateShader;
- }
-
- /**
- * Creates a vertical {@link LinearGradient}
- * @return
- */
- private Shader createGradientShader() {
- final Rect rect = mRect;
- copyBounds(rect);
-
- final float borderRatio = mBorderWidth / rect.height();
-
- final int[] colors = new int[6];
- colors[0] = ColorUtils.compositeColors(mTopOuterStrokeColor, mCurrentBorderTintColor);
- colors[1] = ColorUtils.compositeColors(mTopInnerStrokeColor, mCurrentBorderTintColor);
- colors[2] = ColorUtils.compositeColors(
- ColorUtils.setAlphaComponent(mTopInnerStrokeColor, 0), mCurrentBorderTintColor);
- colors[3] = ColorUtils.compositeColors(
- ColorUtils.setAlphaComponent(mBottomInnerStrokeColor, 0), mCurrentBorderTintColor);
- colors[4] = ColorUtils.compositeColors(mBottomInnerStrokeColor, mCurrentBorderTintColor);
- colors[5] = ColorUtils.compositeColors(mBottomOuterStrokeColor, mCurrentBorderTintColor);
-
- final float[] positions = new float[6];
- positions[0] = 0f;
- positions[1] = borderRatio;
- positions[2] = 0.5f;
- positions[3] = 0.5f;
- positions[4] = 1f - borderRatio;
- positions[5] = 1f;
-
- return new LinearGradient(
- 0, rect.top,
- 0, rect.bottom,
- colors, positions,
- Shader.TileMode.CLAMP);
- }
-}
diff --git a/android/support/design/widget/CircularBorderDrawableLollipop.java b/android/support/design/widget/CircularBorderDrawableLollipop.java
deleted file mode 100644
index 80084048..00000000
--- a/android/support/design/widget/CircularBorderDrawableLollipop.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.graphics.Outline;
-import android.support.annotation.RequiresApi;
-
-/**
- * Lollipop version of {@link CircularBorderDrawable}.
- */
-@RequiresApi(21)
-class CircularBorderDrawableLollipop extends CircularBorderDrawable {
-
- @Override
- public void getOutline(Outline outline) {
- copyBounds(mRect);
- outline.setOval(mRect);
- }
-
-}
diff --git a/android/support/design/widget/CollapsingTextHelper.java b/android/support/design/widget/CollapsingTextHelper.java
deleted file mode 100644
index a33cabc3..00000000
--- a/android/support/design/widget/CollapsingTextHelper.java
+++ /dev/null
@@ -1,723 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Typeface;
-import android.os.Build;
-import android.support.annotation.ColorInt;
-import android.support.v4.math.MathUtils;
-import android.support.v4.text.TextDirectionHeuristicsCompat;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.widget.TintTypedArray;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.view.Gravity;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-final class CollapsingTextHelper {
-
- // Pre-JB-MR2 doesn't support HW accelerated canvas scaled text so we will workaround it
- // by using our own texture
- private static final boolean USE_SCALING_TEXTURE = Build.VERSION.SDK_INT < 18;
-
- private static final boolean DEBUG_DRAW = false;
- private static final Paint DEBUG_DRAW_PAINT;
- static {
- DEBUG_DRAW_PAINT = DEBUG_DRAW ? new Paint() : null;
- if (DEBUG_DRAW_PAINT != null) {
- DEBUG_DRAW_PAINT.setAntiAlias(true);
- DEBUG_DRAW_PAINT.setColor(Color.MAGENTA);
- }
- }
-
- private final View mView;
-
- private boolean mDrawTitle;
- private float mExpandedFraction;
-
- private final Rect mExpandedBounds;
- private final Rect mCollapsedBounds;
- private final RectF mCurrentBounds;
- private int mExpandedTextGravity = Gravity.CENTER_VERTICAL;
- private int mCollapsedTextGravity = Gravity.CENTER_VERTICAL;
- private float mExpandedTextSize = 15;
- private float mCollapsedTextSize = 15;
- private ColorStateList mExpandedTextColor;
- private ColorStateList mCollapsedTextColor;
-
- private float mExpandedDrawY;
- private float mCollapsedDrawY;
- private float mExpandedDrawX;
- private float mCollapsedDrawX;
- private float mCurrentDrawX;
- private float mCurrentDrawY;
- private Typeface mCollapsedTypeface;
- private Typeface mExpandedTypeface;
- private Typeface mCurrentTypeface;
-
- private CharSequence mText;
- private CharSequence mTextToDraw;
- private boolean mIsRtl;
-
- private boolean mUseTexture;
- private Bitmap mExpandedTitleTexture;
- private Paint mTexturePaint;
- private float mTextureAscent;
- private float mTextureDescent;
-
- private float mScale;
- private float mCurrentTextSize;
-
- private int[] mState;
-
- private boolean mBoundsChanged;
-
- private final TextPaint mTextPaint;
-
- private Interpolator mPositionInterpolator;
- private Interpolator mTextSizeInterpolator;
-
- private float mCollapsedShadowRadius, mCollapsedShadowDx, mCollapsedShadowDy;
- private int mCollapsedShadowColor;
-
- private float mExpandedShadowRadius, mExpandedShadowDx, mExpandedShadowDy;
- private int mExpandedShadowColor;
-
- public CollapsingTextHelper(View view) {
- mView = view;
-
- mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
-
- mCollapsedBounds = new Rect();
- mExpandedBounds = new Rect();
- mCurrentBounds = new RectF();
- }
-
- void setTextSizeInterpolator(Interpolator interpolator) {
- mTextSizeInterpolator = interpolator;
- recalculate();
- }
-
- void setPositionInterpolator(Interpolator interpolator) {
- mPositionInterpolator = interpolator;
- recalculate();
- }
-
- void setExpandedTextSize(float textSize) {
- if (mExpandedTextSize != textSize) {
- mExpandedTextSize = textSize;
- recalculate();
- }
- }
-
- void setCollapsedTextSize(float textSize) {
- if (mCollapsedTextSize != textSize) {
- mCollapsedTextSize = textSize;
- recalculate();
- }
- }
-
- void setCollapsedTextColor(ColorStateList textColor) {
- if (mCollapsedTextColor != textColor) {
- mCollapsedTextColor = textColor;
- recalculate();
- }
- }
-
- void setExpandedTextColor(ColorStateList textColor) {
- if (mExpandedTextColor != textColor) {
- mExpandedTextColor = textColor;
- recalculate();
- }
- }
-
- void setExpandedBounds(int left, int top, int right, int bottom) {
- if (!rectEquals(mExpandedBounds, left, top, right, bottom)) {
- mExpandedBounds.set(left, top, right, bottom);
- mBoundsChanged = true;
- onBoundsChanged();
- }
- }
-
- void setCollapsedBounds(int left, int top, int right, int bottom) {
- if (!rectEquals(mCollapsedBounds, left, top, right, bottom)) {
- mCollapsedBounds.set(left, top, right, bottom);
- mBoundsChanged = true;
- onBoundsChanged();
- }
- }
-
- void onBoundsChanged() {
- mDrawTitle = mCollapsedBounds.width() > 0 && mCollapsedBounds.height() > 0
- && mExpandedBounds.width() > 0 && mExpandedBounds.height() > 0;
- }
-
- void setExpandedTextGravity(int gravity) {
- if (mExpandedTextGravity != gravity) {
- mExpandedTextGravity = gravity;
- recalculate();
- }
- }
-
- int getExpandedTextGravity() {
- return mExpandedTextGravity;
- }
-
- void setCollapsedTextGravity(int gravity) {
- if (mCollapsedTextGravity != gravity) {
- mCollapsedTextGravity = gravity;
- recalculate();
- }
- }
-
- int getCollapsedTextGravity() {
- return mCollapsedTextGravity;
- }
-
- void setCollapsedTextAppearance(int resId) {
- TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), resId,
- android.support.v7.appcompat.R.styleable.TextAppearance);
- if (a.hasValue(android.support.v7.appcompat.R.styleable.TextAppearance_android_textColor)) {
- mCollapsedTextColor = a.getColorStateList(
- android.support.v7.appcompat.R.styleable.TextAppearance_android_textColor);
- }
- if (a.hasValue(android.support.v7.appcompat.R.styleable.TextAppearance_android_textSize)) {
- mCollapsedTextSize = a.getDimensionPixelSize(
- android.support.v7.appcompat.R.styleable.TextAppearance_android_textSize,
- (int) mCollapsedTextSize);
- }
- mCollapsedShadowColor = a.getInt(
- android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowColor, 0);
- mCollapsedShadowDx = a.getFloat(
- android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowDx, 0);
- mCollapsedShadowDy = a.getFloat(
- android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowDy, 0);
- mCollapsedShadowRadius = a.getFloat(
- android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowRadius, 0);
- a.recycle();
-
- if (Build.VERSION.SDK_INT >= 16) {
- mCollapsedTypeface = readFontFamilyTypeface(resId);
- }
-
- recalculate();
- }
-
- void setExpandedTextAppearance(int resId) {
- TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), resId,
- android.support.v7.appcompat.R.styleable.TextAppearance);
- if (a.hasValue(android.support.v7.appcompat.R.styleable.TextAppearance_android_textColor)) {
- mExpandedTextColor = a.getColorStateList(
- android.support.v7.appcompat.R.styleable.TextAppearance_android_textColor);
- }
- if (a.hasValue(android.support.v7.appcompat.R.styleable.TextAppearance_android_textSize)) {
- mExpandedTextSize = a.getDimensionPixelSize(
- android.support.v7.appcompat.R.styleable.TextAppearance_android_textSize,
- (int) mExpandedTextSize);
- }
- mExpandedShadowColor = a.getInt(
- android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowColor, 0);
- mExpandedShadowDx = a.getFloat(
- android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowDx, 0);
- mExpandedShadowDy = a.getFloat(
- android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowDy, 0);
- mExpandedShadowRadius = a.getFloat(
- android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowRadius, 0);
- a.recycle();
-
- if (Build.VERSION.SDK_INT >= 16) {
- mExpandedTypeface = readFontFamilyTypeface(resId);
- }
-
- recalculate();
- }
-
- private Typeface readFontFamilyTypeface(int resId) {
- final TypedArray a = mView.getContext().obtainStyledAttributes(resId,
- new int[]{android.R.attr.fontFamily});
- try {
- final String family = a.getString(0);
- if (family != null) {
- return Typeface.create(family, Typeface.NORMAL);
- }
- } finally {
- a.recycle();
- }
- return null;
- }
-
- void setCollapsedTypeface(Typeface typeface) {
- if (areTypefacesDifferent(mCollapsedTypeface, typeface)) {
- mCollapsedTypeface = typeface;
- recalculate();
- }
- }
-
- void setExpandedTypeface(Typeface typeface) {
- if (areTypefacesDifferent(mExpandedTypeface, typeface)) {
- mExpandedTypeface = typeface;
- recalculate();
- }
- }
-
- void setTypefaces(Typeface typeface) {
- mCollapsedTypeface = mExpandedTypeface = typeface;
- recalculate();
- }
-
- Typeface getCollapsedTypeface() {
- return mCollapsedTypeface != null ? mCollapsedTypeface : Typeface.DEFAULT;
- }
-
- Typeface getExpandedTypeface() {
- return mExpandedTypeface != null ? mExpandedTypeface : Typeface.DEFAULT;
- }
-
- /**
- * Set the value indicating the current scroll value. This decides how much of the
- * background will be displayed, as well as the title metrics/positioning.
- *
- * A value of {@code 0.0} indicates that the layout is fully expanded.
- * A value of {@code 1.0} indicates that the layout is fully collapsed.
- */
- void setExpansionFraction(float fraction) {
- fraction = MathUtils.clamp(fraction, 0f, 1f);
-
- if (fraction != mExpandedFraction) {
- mExpandedFraction = fraction;
- calculateCurrentOffsets();
- }
- }
-
- final boolean setState(final int[] state) {
- mState = state;
-
- if (isStateful()) {
- recalculate();
- return true;
- }
-
- return false;
- }
-
- final boolean isStateful() {
- return (mCollapsedTextColor != null && mCollapsedTextColor.isStateful())
- || (mExpandedTextColor != null && mExpandedTextColor.isStateful());
- }
-
- float getExpansionFraction() {
- return mExpandedFraction;
- }
-
- float getCollapsedTextSize() {
- return mCollapsedTextSize;
- }
-
- float getExpandedTextSize() {
- return mExpandedTextSize;
- }
-
- private void calculateCurrentOffsets() {
- calculateOffsets(mExpandedFraction);
- }
-
- private void calculateOffsets(final float fraction) {
- interpolateBounds(fraction);
- mCurrentDrawX = lerp(mExpandedDrawX, mCollapsedDrawX, fraction,
- mPositionInterpolator);
- mCurrentDrawY = lerp(mExpandedDrawY, mCollapsedDrawY, fraction,
- mPositionInterpolator);
-
- setInterpolatedTextSize(lerp(mExpandedTextSize, mCollapsedTextSize,
- fraction, mTextSizeInterpolator));
-
- if (mCollapsedTextColor != mExpandedTextColor) {
- // If the collapsed and expanded text colors are different, blend them based on the
- // fraction
- mTextPaint.setColor(blendColors(
- getCurrentExpandedTextColor(), getCurrentCollapsedTextColor(), fraction));
- } else {
- mTextPaint.setColor(getCurrentCollapsedTextColor());
- }
-
- mTextPaint.setShadowLayer(
- lerp(mExpandedShadowRadius, mCollapsedShadowRadius, fraction, null),
- lerp(mExpandedShadowDx, mCollapsedShadowDx, fraction, null),
- lerp(mExpandedShadowDy, mCollapsedShadowDy, fraction, null),
- blendColors(mExpandedShadowColor, mCollapsedShadowColor, fraction));
-
- ViewCompat.postInvalidateOnAnimation(mView);
- }
-
- @ColorInt
- private int getCurrentExpandedTextColor() {
- if (mState != null) {
- return mExpandedTextColor.getColorForState(mState, 0);
- } else {
- return mExpandedTextColor.getDefaultColor();
- }
- }
-
- @ColorInt
- private int getCurrentCollapsedTextColor() {
- if (mState != null) {
- return mCollapsedTextColor.getColorForState(mState, 0);
- } else {
- return mCollapsedTextColor.getDefaultColor();
- }
- }
-
- private void calculateBaseOffsets() {
- final float currentTextSize = mCurrentTextSize;
-
- // We then calculate the collapsed text size, using the same logic
- calculateUsingTextSize(mCollapsedTextSize);
- float width = mTextToDraw != null ?
- mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length()) : 0;
- final int collapsedAbsGravity = GravityCompat.getAbsoluteGravity(mCollapsedTextGravity,
- mIsRtl ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR);
- switch (collapsedAbsGravity & Gravity.VERTICAL_GRAVITY_MASK) {
- case Gravity.BOTTOM:
- mCollapsedDrawY = mCollapsedBounds.bottom;
- break;
- case Gravity.TOP:
- mCollapsedDrawY = mCollapsedBounds.top - mTextPaint.ascent();
- break;
- case Gravity.CENTER_VERTICAL:
- default:
- float textHeight = mTextPaint.descent() - mTextPaint.ascent();
- float textOffset = (textHeight / 2) - mTextPaint.descent();
- mCollapsedDrawY = mCollapsedBounds.centerY() + textOffset;
- break;
- }
- switch (collapsedAbsGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
- case Gravity.CENTER_HORIZONTAL:
- mCollapsedDrawX = mCollapsedBounds.centerX() - (width / 2);
- break;
- case Gravity.RIGHT:
- mCollapsedDrawX = mCollapsedBounds.right - width;
- break;
- case Gravity.LEFT:
- default:
- mCollapsedDrawX = mCollapsedBounds.left;
- break;
- }
-
- calculateUsingTextSize(mExpandedTextSize);
- width = mTextToDraw != null
- ? mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length()) : 0;
- final int expandedAbsGravity = GravityCompat.getAbsoluteGravity(mExpandedTextGravity,
- mIsRtl ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR);
- switch (expandedAbsGravity & Gravity.VERTICAL_GRAVITY_MASK) {
- case Gravity.BOTTOM:
- mExpandedDrawY = mExpandedBounds.bottom;
- break;
- case Gravity.TOP:
- mExpandedDrawY = mExpandedBounds.top - mTextPaint.ascent();
- break;
- case Gravity.CENTER_VERTICAL:
- default:
- float textHeight = mTextPaint.descent() - mTextPaint.ascent();
- float textOffset = (textHeight / 2) - mTextPaint.descent();
- mExpandedDrawY = mExpandedBounds.centerY() + textOffset;
- break;
- }
- switch (expandedAbsGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
- case Gravity.CENTER_HORIZONTAL:
- mExpandedDrawX = mExpandedBounds.centerX() - (width / 2);
- break;
- case Gravity.RIGHT:
- mExpandedDrawX = mExpandedBounds.right - width;
- break;
- case Gravity.LEFT:
- default:
- mExpandedDrawX = mExpandedBounds.left;
- break;
- }
-
- // The bounds have changed so we need to clear the texture
- clearTexture();
- // Now reset the text size back to the original
- setInterpolatedTextSize(currentTextSize);
- }
-
- private void interpolateBounds(float fraction) {
- mCurrentBounds.left = lerp(mExpandedBounds.left, mCollapsedBounds.left,
- fraction, mPositionInterpolator);
- mCurrentBounds.top = lerp(mExpandedDrawY, mCollapsedDrawY,
- fraction, mPositionInterpolator);
- mCurrentBounds.right = lerp(mExpandedBounds.right, mCollapsedBounds.right,
- fraction, mPositionInterpolator);
- mCurrentBounds.bottom = lerp(mExpandedBounds.bottom, mCollapsedBounds.bottom,
- fraction, mPositionInterpolator);
- }
-
- public void draw(Canvas canvas) {
- final int saveCount = canvas.save();
-
- if (mTextToDraw != null && mDrawTitle) {
- float x = mCurrentDrawX;
- float y = mCurrentDrawY;
-
- final boolean drawTexture = mUseTexture && mExpandedTitleTexture != null;
-
- final float ascent;
- final float descent;
- if (drawTexture) {
- ascent = mTextureAscent * mScale;
- descent = mTextureDescent * mScale;
- } else {
- ascent = mTextPaint.ascent() * mScale;
- descent = mTextPaint.descent() * mScale;
- }
-
- if (DEBUG_DRAW) {
- // Just a debug tool, which drawn a magenta rect in the text bounds
- canvas.drawRect(mCurrentBounds.left, y + ascent, mCurrentBounds.right, y + descent,
- DEBUG_DRAW_PAINT);
- }
-
- if (drawTexture) {
- y += ascent;
- }
-
- if (mScale != 1f) {
- canvas.scale(mScale, mScale, x, y);
- }
-
- if (drawTexture) {
- // If we should use a texture, draw it instead of text
- canvas.drawBitmap(mExpandedTitleTexture, x, y, mTexturePaint);
- } else {
- canvas.drawText(mTextToDraw, 0, mTextToDraw.length(), x, y, mTextPaint);
- }
- }
-
- canvas.restoreToCount(saveCount);
- }
-
- private boolean calculateIsRtl(CharSequence text) {
- final boolean defaultIsRtl = ViewCompat.getLayoutDirection(mView)
- == ViewCompat.LAYOUT_DIRECTION_RTL;
- return (defaultIsRtl
- ? TextDirectionHeuristicsCompat.FIRSTSTRONG_RTL
- : TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR).isRtl(text, 0, text.length());
- }
-
- private void setInterpolatedTextSize(float textSize) {
- calculateUsingTextSize(textSize);
-
- // Use our texture if the scale isn't 1.0
- mUseTexture = USE_SCALING_TEXTURE && mScale != 1f;
-
- if (mUseTexture) {
- // Make sure we have an expanded texture if needed
- ensureExpandedTexture();
- }
-
- ViewCompat.postInvalidateOnAnimation(mView);
- }
-
- private boolean areTypefacesDifferent(Typeface first, Typeface second) {
- return (first != null && !first.equals(second)) || (first == null && second != null);
- }
-
- private void calculateUsingTextSize(final float textSize) {
- if (mText == null) return;
-
- final float collapsedWidth = mCollapsedBounds.width();
- final float expandedWidth = mExpandedBounds.width();
-
- final float availableWidth;
- final float newTextSize;
- boolean updateDrawText = false;
-
- if (isClose(textSize, mCollapsedTextSize)) {
- newTextSize = mCollapsedTextSize;
- mScale = 1f;
- if (areTypefacesDifferent(mCurrentTypeface, mCollapsedTypeface)) {
- mCurrentTypeface = mCollapsedTypeface;
- updateDrawText = true;
- }
- availableWidth = collapsedWidth;
- } else {
- newTextSize = mExpandedTextSize;
- if (areTypefacesDifferent(mCurrentTypeface, mExpandedTypeface)) {
- mCurrentTypeface = mExpandedTypeface;
- updateDrawText = true;
- }
- if (isClose(textSize, mExpandedTextSize)) {
- // If we're close to the expanded text size, snap to it and use a scale of 1
- mScale = 1f;
- } else {
- // Else, we'll scale down from the expanded text size
- mScale = textSize / mExpandedTextSize;
- }
-
- final float textSizeRatio = mCollapsedTextSize / mExpandedTextSize;
- // This is the size of the expanded bounds when it is scaled to match the
- // collapsed text size
- final float scaledDownWidth = expandedWidth * textSizeRatio;
-
- if (scaledDownWidth > collapsedWidth) {
- // If the scaled down size is larger than the actual collapsed width, we need to
- // cap the available width so that when the expanded text scales down, it matches
- // the collapsed width
- availableWidth = Math.min(collapsedWidth / textSizeRatio, expandedWidth);
- } else {
- // Otherwise we'll just use the expanded width
- availableWidth = expandedWidth;
- }
- }
-
- if (availableWidth > 0) {
- updateDrawText = (mCurrentTextSize != newTextSize) || mBoundsChanged || updateDrawText;
- mCurrentTextSize = newTextSize;
- mBoundsChanged = false;
- }
-
- if (mTextToDraw == null || updateDrawText) {
- mTextPaint.setTextSize(mCurrentTextSize);
- mTextPaint.setTypeface(mCurrentTypeface);
- // Use linear text scaling if we're scaling the canvas
- mTextPaint.setLinearText(mScale != 1f);
-
- // If we don't currently have text to draw, or the text size has changed, ellipsize...
- final CharSequence title = TextUtils.ellipsize(mText, mTextPaint,
- availableWidth, TextUtils.TruncateAt.END);
- if (!TextUtils.equals(title, mTextToDraw)) {
- mTextToDraw = title;
- mIsRtl = calculateIsRtl(mTextToDraw);
- }
- }
- }
-
- private void ensureExpandedTexture() {
- if (mExpandedTitleTexture != null || mExpandedBounds.isEmpty()
- || TextUtils.isEmpty(mTextToDraw)) {
- return;
- }
-
- calculateOffsets(0f);
- mTextureAscent = mTextPaint.ascent();
- mTextureDescent = mTextPaint.descent();
-
- final int w = Math.round(mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length()));
- final int h = Math.round(mTextureDescent - mTextureAscent);
-
- if (w <= 0 || h <= 0) {
- return; // If the width or height are 0, return
- }
-
- mExpandedTitleTexture = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
-
- Canvas c = new Canvas(mExpandedTitleTexture);
- c.drawText(mTextToDraw, 0, mTextToDraw.length(), 0, h - mTextPaint.descent(), mTextPaint);
-
- if (mTexturePaint == null) {
- // Make sure we have a paint
- mTexturePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
- }
- }
-
- public void recalculate() {
- if (mView.getHeight() > 0 && mView.getWidth() > 0) {
- // If we've already been laid out, calculate everything now otherwise we'll wait
- // until a layout
- calculateBaseOffsets();
- calculateCurrentOffsets();
- }
- }
-
- /**
- * Set the title to display
- *
- * @param text
- */
- void setText(CharSequence text) {
- if (text == null || !text.equals(mText)) {
- mText = text;
- mTextToDraw = null;
- clearTexture();
- recalculate();
- }
- }
-
- CharSequence getText() {
- return mText;
- }
-
- private void clearTexture() {
- if (mExpandedTitleTexture != null) {
- mExpandedTitleTexture.recycle();
- mExpandedTitleTexture = null;
- }
- }
-
- /**
- * Returns true if {@code value} is 'close' to it's closest decimal value. Close is currently
- * defined as it's difference being < 0.001.
- */
- private static boolean isClose(float value, float targetValue) {
- return Math.abs(value - targetValue) < 0.001f;
- }
-
- ColorStateList getExpandedTextColor() {
- return mExpandedTextColor;
- }
-
- ColorStateList getCollapsedTextColor() {
- return mCollapsedTextColor;
- }
-
- /**
- * Blend {@code color1} and {@code color2} using the given ratio.
- *
- * @param ratio of which to blend. 0.0 will return {@code color1}, 0.5 will give an even blend,
- * 1.0 will return {@code color2}.
- */
- private static int blendColors(int color1, int color2, float ratio) {
- final float inverseRatio = 1f - ratio;
- float a = (Color.alpha(color1) * inverseRatio) + (Color.alpha(color2) * ratio);
- float r = (Color.red(color1) * inverseRatio) + (Color.red(color2) * ratio);
- float g = (Color.green(color1) * inverseRatio) + (Color.green(color2) * ratio);
- float b = (Color.blue(color1) * inverseRatio) + (Color.blue(color2) * ratio);
- return Color.argb((int) a, (int) r, (int) g, (int) b);
- }
-
- private static float lerp(float startValue, float endValue, float fraction,
- Interpolator interpolator) {
- if (interpolator != null) {
- fraction = interpolator.getInterpolation(fraction);
- }
- return AnimationUtils.lerp(startValue, endValue, fraction);
- }
-
- private static boolean rectEquals(Rect r, int left, int top, int right, int bottom) {
- return !(r.left != left || r.top != top || r.right != right || r.bottom != bottom);
- }
-}
diff --git a/android/support/design/widget/CollapsingToolbarLayout.java b/android/support/design/widget/CollapsingToolbarLayout.java
deleted file mode 100644
index 8c9b7d49..00000000
--- a/android/support/design/widget/CollapsingToolbarLayout.java
+++ /dev/null
@@ -1,1308 +0,0 @@
-/*
- * 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.design.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.ColorInt;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.IntDef;
-import android.support.annotation.IntRange;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StyleRes;
-import android.support.design.R;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.math.MathUtils;
-import android.support.v4.util.ObjectsCompat;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.WindowInsetsCompat;
-import android.support.v4.widget.ViewGroupUtils;
-import android.support.v7.widget.Toolbar;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.widget.FrameLayout;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * CollapsingToolbarLayout is a wrapper for {@link Toolbar} which implements a collapsing app bar.
- * It is designed to be used as a direct child of a {@link AppBarLayout}.
- * CollapsingToolbarLayout contains the following features:
- *
- * <h4>Collapsing title</h4>
- * A title which is larger when the layout is fully visible but collapses and becomes smaller as
- * the layout is scrolled off screen. You can set the title to display via
- * {@link #setTitle(CharSequence)}. The title appearance can be tweaked via the
- * {@code collapsedTextAppearance} and {@code expandedTextAppearance} attributes.
- *
- * <h4>Content scrim</h4>
- * A full-bleed scrim which is show or hidden when the scroll position has hit a certain threshold.
- * You can change this via {@link #setContentScrim(Drawable)}.
- *
- * <h4>Status bar scrim</h4>
- * A scrim which is show or hidden behind the status bar when the scroll position has hit a certain
- * threshold. You can change this via {@link #setStatusBarScrim(Drawable)}. This only works
- * on {@link android.os.Build.VERSION_CODES#LOLLIPOP LOLLIPOP} devices when we set to fit system
- * windows.
- *
- * <h4>Parallax scrolling children</h4>
- * Child views can opt to be scrolled within this layout in a parallax fashion.
- * See {@link LayoutParams#COLLAPSE_MODE_PARALLAX} and
- * {@link LayoutParams#setParallaxMultiplier(float)}.
- *
- * <h4>Pinned position children</h4>
- * Child views can opt to be pinned in space globally. This is useful when implementing a
- * collapsing as it allows the {@link Toolbar} to be fixed in place even though this layout is
- * moving. See {@link LayoutParams#COLLAPSE_MODE_PIN}.
- *
- * <p><strong>Do not manually add views to the Toolbar at run time</strong>.
- * We will add a 'dummy view' to the Toolbar which allows us to work out the available space
- * for the title. This can interfere with any views which you add.</p>
- *
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_collapsedTitleTextAppearance
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleTextAppearance
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_contentScrim
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMargin
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_statusBarScrim
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_toolbarId
- */
-public class CollapsingToolbarLayout extends FrameLayout {
-
- private static final int DEFAULT_SCRIM_ANIMATION_DURATION = 600;
-
- private boolean mRefreshToolbar = true;
- private int mToolbarId;
- private Toolbar mToolbar;
- private View mToolbarDirectChild;
- private View mDummyView;
-
- private int mExpandedMarginStart;
- private int mExpandedMarginTop;
- private int mExpandedMarginEnd;
- private int mExpandedMarginBottom;
-
- private final Rect mTmpRect = new Rect();
- final CollapsingTextHelper mCollapsingTextHelper;
- private boolean mCollapsingTitleEnabled;
- private boolean mDrawCollapsingTitle;
-
- private Drawable mContentScrim;
- Drawable mStatusBarScrim;
- private int mScrimAlpha;
- private boolean mScrimsAreShown;
- private ValueAnimator mScrimAnimator;
- private long mScrimAnimationDuration;
- private int mScrimVisibleHeightTrigger = -1;
-
- private AppBarLayout.OnOffsetChangedListener mOnOffsetChangedListener;
-
- int mCurrentOffset;
-
- WindowInsetsCompat mLastInsets;
-
- public CollapsingToolbarLayout(Context context) {
- this(context, null);
- }
-
- public CollapsingToolbarLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public CollapsingToolbarLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- ThemeUtils.checkAppCompatTheme(context);
-
- mCollapsingTextHelper = new CollapsingTextHelper(this);
- mCollapsingTextHelper.setTextSizeInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
-
- TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.CollapsingToolbarLayout, defStyleAttr,
- R.style.Widget_Design_CollapsingToolbar);
-
- mCollapsingTextHelper.setExpandedTextGravity(
- a.getInt(R.styleable.CollapsingToolbarLayout_expandedTitleGravity,
- GravityCompat.START | Gravity.BOTTOM));
- mCollapsingTextHelper.setCollapsedTextGravity(
- a.getInt(R.styleable.CollapsingToolbarLayout_collapsedTitleGravity,
- GravityCompat.START | Gravity.CENTER_VERTICAL));
-
- mExpandedMarginStart = mExpandedMarginTop = mExpandedMarginEnd = mExpandedMarginBottom =
- a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_expandedTitleMargin, 0);
-
- if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginStart)) {
- mExpandedMarginStart = a.getDimensionPixelSize(
- R.styleable.CollapsingToolbarLayout_expandedTitleMarginStart, 0);
- }
- if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginEnd)) {
- mExpandedMarginEnd = a.getDimensionPixelSize(
- R.styleable.CollapsingToolbarLayout_expandedTitleMarginEnd, 0);
- }
- if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginTop)) {
- mExpandedMarginTop = a.getDimensionPixelSize(
- R.styleable.CollapsingToolbarLayout_expandedTitleMarginTop, 0);
- }
- if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginBottom)) {
- mExpandedMarginBottom = a.getDimensionPixelSize(
- R.styleable.CollapsingToolbarLayout_expandedTitleMarginBottom, 0);
- }
-
- mCollapsingTitleEnabled = a.getBoolean(
- R.styleable.CollapsingToolbarLayout_titleEnabled, true);
- setTitle(a.getText(R.styleable.CollapsingToolbarLayout_title));
-
- // First load the default text appearances
- mCollapsingTextHelper.setExpandedTextAppearance(
- R.style.TextAppearance_Design_CollapsingToolbar_Expanded);
- mCollapsingTextHelper.setCollapsedTextAppearance(
- android.support.v7.appcompat.R.style.TextAppearance_AppCompat_Widget_ActionBar_Title);
-
- // Now overlay any custom text appearances
- if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleTextAppearance)) {
- mCollapsingTextHelper.setExpandedTextAppearance(
- a.getResourceId(
- R.styleable.CollapsingToolbarLayout_expandedTitleTextAppearance, 0));
- }
- if (a.hasValue(R.styleable.CollapsingToolbarLayout_collapsedTitleTextAppearance)) {
- mCollapsingTextHelper.setCollapsedTextAppearance(
- a.getResourceId(
- R.styleable.CollapsingToolbarLayout_collapsedTitleTextAppearance, 0));
- }
-
- mScrimVisibleHeightTrigger = a.getDimensionPixelSize(
- R.styleable.CollapsingToolbarLayout_scrimVisibleHeightTrigger, -1);
-
- mScrimAnimationDuration = a.getInt(
- R.styleable.CollapsingToolbarLayout_scrimAnimationDuration,
- DEFAULT_SCRIM_ANIMATION_DURATION);
-
- setContentScrim(a.getDrawable(R.styleable.CollapsingToolbarLayout_contentScrim));
- setStatusBarScrim(a.getDrawable(R.styleable.CollapsingToolbarLayout_statusBarScrim));
-
- mToolbarId = a.getResourceId(R.styleable.CollapsingToolbarLayout_toolbarId, -1);
-
- a.recycle();
-
- setWillNotDraw(false);
-
- ViewCompat.setOnApplyWindowInsetsListener(this,
- new android.support.v4.view.OnApplyWindowInsetsListener() {
- @Override
- public WindowInsetsCompat onApplyWindowInsets(View v,
- WindowInsetsCompat insets) {
- return onWindowInsetChanged(insets);
- }
- });
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- // Add an OnOffsetChangedListener if possible
- final ViewParent parent = getParent();
- if (parent instanceof AppBarLayout) {
- // Copy over from the ABL whether we should fit system windows
- ViewCompat.setFitsSystemWindows(this, ViewCompat.getFitsSystemWindows((View) parent));
-
- if (mOnOffsetChangedListener == null) {
- mOnOffsetChangedListener = new OffsetUpdateListener();
- }
- ((AppBarLayout) parent).addOnOffsetChangedListener(mOnOffsetChangedListener);
-
- // We're attached, so lets request an inset dispatch
- ViewCompat.requestApplyInsets(this);
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- // Remove our OnOffsetChangedListener if possible and it exists
- final ViewParent parent = getParent();
- if (mOnOffsetChangedListener != null && parent instanceof AppBarLayout) {
- ((AppBarLayout) parent).removeOnOffsetChangedListener(mOnOffsetChangedListener);
- }
-
- super.onDetachedFromWindow();
- }
-
- WindowInsetsCompat onWindowInsetChanged(final WindowInsetsCompat insets) {
- WindowInsetsCompat newInsets = null;
-
- if (ViewCompat.getFitsSystemWindows(this)) {
- // If we're set to fit system windows, keep the insets
- newInsets = insets;
- }
-
- // If our insets have changed, keep them and invalidate the scroll ranges...
- if (!ObjectsCompat.equals(mLastInsets, newInsets)) {
- mLastInsets = newInsets;
- requestLayout();
- }
-
- // Consume the insets. This is done so that child views with fitSystemWindows=true do not
- // get the default padding functionality from View
- return insets.consumeSystemWindowInsets();
- }
-
- @Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
-
- // If we don't have a toolbar, the scrim will be not be drawn in drawChild() below.
- // Instead, we draw it here, before our collapsing text.
- ensureToolbar();
- if (mToolbar == null && mContentScrim != null && mScrimAlpha > 0) {
- mContentScrim.mutate().setAlpha(mScrimAlpha);
- mContentScrim.draw(canvas);
- }
-
- // Let the collapsing text helper draw its text
- if (mCollapsingTitleEnabled && mDrawCollapsingTitle) {
- mCollapsingTextHelper.draw(canvas);
- }
-
- // Now draw the status bar scrim
- if (mStatusBarScrim != null && mScrimAlpha > 0) {
- final int topInset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
- if (topInset > 0) {
- mStatusBarScrim.setBounds(0, -mCurrentOffset, getWidth(),
- topInset - mCurrentOffset);
- mStatusBarScrim.mutate().setAlpha(mScrimAlpha);
- mStatusBarScrim.draw(canvas);
- }
- }
- }
-
- @Override
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- // This is a little weird. Our scrim needs to be behind the Toolbar (if it is present),
- // but in front of any other children which are behind it. To do this we intercept the
- // drawChild() call, and draw our scrim just before the Toolbar is drawn
- boolean invalidated = false;
- if (mContentScrim != null && mScrimAlpha > 0 && isToolbarChild(child)) {
- mContentScrim.mutate().setAlpha(mScrimAlpha);
- mContentScrim.draw(canvas);
- invalidated = true;
- }
- return super.drawChild(canvas, child, drawingTime) || invalidated;
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- if (mContentScrim != null) {
- mContentScrim.setBounds(0, 0, w, h);
- }
- }
-
- private void ensureToolbar() {
- if (!mRefreshToolbar) {
- return;
- }
-
- // First clear out the current Toolbar
- mToolbar = null;
- mToolbarDirectChild = null;
-
- if (mToolbarId != -1) {
- // If we have an ID set, try and find it and it's direct parent to us
- mToolbar = findViewById(mToolbarId);
- if (mToolbar != null) {
- mToolbarDirectChild = findDirectChild(mToolbar);
- }
- }
-
- if (mToolbar == null) {
- // If we don't have an ID, or couldn't find a Toolbar with the correct ID, try and find
- // one from our direct children
- Toolbar toolbar = null;
- for (int i = 0, count = getChildCount(); i < count; i++) {
- final View child = getChildAt(i);
- if (child instanceof Toolbar) {
- toolbar = (Toolbar) child;
- break;
- }
- }
- mToolbar = toolbar;
- }
-
- updateDummyView();
- mRefreshToolbar = false;
- }
-
- private boolean isToolbarChild(View child) {
- return (mToolbarDirectChild == null || mToolbarDirectChild == this)
- ? child == mToolbar
- : child == mToolbarDirectChild;
- }
-
- /**
- * Returns the direct child of this layout, which itself is the ancestor of the
- * given view.
- */
- private View findDirectChild(final View descendant) {
- View directChild = descendant;
- for (ViewParent p = descendant.getParent(); p != this && p != null; p = p.getParent()) {
- if (p instanceof View) {
- directChild = (View) p;
- }
- }
- return directChild;
- }
-
- private void updateDummyView() {
- if (!mCollapsingTitleEnabled && mDummyView != null) {
- // If we have a dummy view and we have our title disabled, remove it from its parent
- final ViewParent parent = mDummyView.getParent();
- if (parent instanceof ViewGroup) {
- ((ViewGroup) parent).removeView(mDummyView);
- }
- }
- if (mCollapsingTitleEnabled && mToolbar != null) {
- if (mDummyView == null) {
- mDummyView = new View(getContext());
- }
- if (mDummyView.getParent() == null) {
- mToolbar.addView(mDummyView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
- }
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- ensureToolbar();
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- final int mode = MeasureSpec.getMode(heightMeasureSpec);
- final int topInset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
- if (mode == MeasureSpec.UNSPECIFIED && topInset > 0) {
- // If we have a top inset and we're set to wrap_content height we need to make sure
- // we add the top inset to our height, therefore we re-measure
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(
- getMeasuredHeight() + topInset, MeasureSpec.EXACTLY);
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
-
- if (mLastInsets != null) {
- // Shift down any views which are not set to fit system windows
- final int insetTop = mLastInsets.getSystemWindowInsetTop();
- for (int i = 0, z = getChildCount(); i < z; i++) {
- final View child = getChildAt(i);
- if (!ViewCompat.getFitsSystemWindows(child)) {
- if (child.getTop() < insetTop) {
- // If the child isn't set to fit system windows but is drawing within
- // the inset offset it down
- ViewCompat.offsetTopAndBottom(child, insetTop);
- }
- }
- }
- }
-
- // Update the collapsed bounds by getting it's transformed bounds
- if (mCollapsingTitleEnabled && mDummyView != null) {
- // We only draw the title if the dummy view is being displayed (Toolbar removes
- // views if there is no space)
- mDrawCollapsingTitle = ViewCompat.isAttachedToWindow(mDummyView)
- && mDummyView.getVisibility() == VISIBLE;
-
- if (mDrawCollapsingTitle) {
- final boolean isRtl = ViewCompat.getLayoutDirection(this)
- == ViewCompat.LAYOUT_DIRECTION_RTL;
-
- // Update the collapsed bounds
- final int maxOffset = getMaxOffsetForPinChild(
- mToolbarDirectChild != null ? mToolbarDirectChild : mToolbar);
- ViewGroupUtils.getDescendantRect(this, mDummyView, mTmpRect);
- mCollapsingTextHelper.setCollapsedBounds(
- mTmpRect.left + (isRtl
- ? mToolbar.getTitleMarginEnd()
- : mToolbar.getTitleMarginStart()),
- mTmpRect.top + maxOffset + mToolbar.getTitleMarginTop(),
- mTmpRect.right + (isRtl
- ? mToolbar.getTitleMarginStart()
- : mToolbar.getTitleMarginEnd()),
- mTmpRect.bottom + maxOffset - mToolbar.getTitleMarginBottom());
-
- // Update the expanded bounds
- mCollapsingTextHelper.setExpandedBounds(
- isRtl ? mExpandedMarginEnd : mExpandedMarginStart,
- mTmpRect.top + mExpandedMarginTop,
- right - left - (isRtl ? mExpandedMarginStart : mExpandedMarginEnd),
- bottom - top - mExpandedMarginBottom);
- // Now recalculate using the new bounds
- mCollapsingTextHelper.recalculate();
- }
- }
-
- // Update our child view offset helpers. This needs to be done after the title has been
- // setup, so that any Toolbars are in their original position
- for (int i = 0, z = getChildCount(); i < z; i++) {
- getViewOffsetHelper(getChildAt(i)).onViewLayout();
- }
-
- // Finally, set our minimum height to enable proper AppBarLayout collapsing
- if (mToolbar != null) {
- if (mCollapsingTitleEnabled && TextUtils.isEmpty(mCollapsingTextHelper.getText())) {
- // If we do not currently have a title, try and grab it from the Toolbar
- mCollapsingTextHelper.setText(mToolbar.getTitle());
- }
- if (mToolbarDirectChild == null || mToolbarDirectChild == this) {
- setMinimumHeight(getHeightWithMargins(mToolbar));
- } else {
- setMinimumHeight(getHeightWithMargins(mToolbarDirectChild));
- }
- }
-
- updateScrimVisibility();
- }
-
- private static int getHeightWithMargins(@NonNull final View view) {
- final ViewGroup.LayoutParams lp = view.getLayoutParams();
- if (lp instanceof MarginLayoutParams) {
- final MarginLayoutParams mlp = (MarginLayoutParams) lp;
- return view.getHeight() + mlp.topMargin + mlp.bottomMargin;
- }
- return view.getHeight();
- }
-
- static ViewOffsetHelper getViewOffsetHelper(View view) {
- ViewOffsetHelper offsetHelper = (ViewOffsetHelper) view.getTag(R.id.view_offset_helper);
- if (offsetHelper == null) {
- offsetHelper = new ViewOffsetHelper(view);
- view.setTag(R.id.view_offset_helper, offsetHelper);
- }
- return offsetHelper;
- }
-
- /**
- * Sets the title to be displayed by this view, if enabled.
- *
- * @see #setTitleEnabled(boolean)
- * @see #getTitle()
- *
- * @attr ref R.styleable#CollapsingToolbarLayout_title
- */
- public void setTitle(@Nullable CharSequence title) {
- mCollapsingTextHelper.setText(title);
- }
-
- /**
- * Returns the title currently being displayed by this view. If the title is not enabled, then
- * this will return {@code null}.
- *
- * @attr ref R.styleable#CollapsingToolbarLayout_title
- */
- @Nullable
- public CharSequence getTitle() {
- return mCollapsingTitleEnabled ? mCollapsingTextHelper.getText() : null;
- }
-
- /**
- * Sets whether this view should display its own title.
- *
- * <p>The title displayed by this view will shrink and grow based on the scroll offset.</p>
- *
- * @see #setTitle(CharSequence)
- * @see #isTitleEnabled()
- *
- * @attr ref R.styleable#CollapsingToolbarLayout_titleEnabled
- */
- public void setTitleEnabled(boolean enabled) {
- if (enabled != mCollapsingTitleEnabled) {
- mCollapsingTitleEnabled = enabled;
- updateDummyView();
- requestLayout();
- }
- }
-
- /**
- * Returns whether this view is currently displaying its own title.
- *
- * @see #setTitleEnabled(boolean)
- *
- * @attr ref R.styleable#CollapsingToolbarLayout_titleEnabled
- */
- public boolean isTitleEnabled() {
- return mCollapsingTitleEnabled;
- }
-
- /**
- * Set whether the content scrim and/or status bar scrim should be shown or not. Any change
- * in the vertical scroll may overwrite this value. Any visibility change will be animated if
- * this view has already been laid out.
- *
- * @param shown whether the scrims should be shown
- *
- * @see #getStatusBarScrim()
- * @see #getContentScrim()
- */
- public void setScrimsShown(boolean shown) {
- setScrimsShown(shown, ViewCompat.isLaidOut(this) && !isInEditMode());
- }
-
- /**
- * Set whether the content scrim and/or status bar scrim should be shown or not. Any change
- * in the vertical scroll may overwrite this value.
- *
- * @param shown whether the scrims should be shown
- * @param animate whether to animate the visibility change
- *
- * @see #getStatusBarScrim()
- * @see #getContentScrim()
- */
- public void setScrimsShown(boolean shown, boolean animate) {
- if (mScrimsAreShown != shown) {
- if (animate) {
- animateScrim(shown ? 0xFF : 0x0);
- } else {
- setScrimAlpha(shown ? 0xFF : 0x0);
- }
- mScrimsAreShown = shown;
- }
- }
-
- private void animateScrim(int targetAlpha) {
- ensureToolbar();
- if (mScrimAnimator == null) {
- mScrimAnimator = new ValueAnimator();
- mScrimAnimator.setDuration(mScrimAnimationDuration);
- mScrimAnimator.setInterpolator(
- targetAlpha > mScrimAlpha
- ? AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR
- : AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
- mScrimAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- setScrimAlpha((int) animator.getAnimatedValue());
- }
- });
- } else if (mScrimAnimator.isRunning()) {
- mScrimAnimator.cancel();
- }
-
- mScrimAnimator.setIntValues(mScrimAlpha, targetAlpha);
- mScrimAnimator.start();
- }
-
- void setScrimAlpha(int alpha) {
- if (alpha != mScrimAlpha) {
- final Drawable contentScrim = mContentScrim;
- if (contentScrim != null && mToolbar != null) {
- ViewCompat.postInvalidateOnAnimation(mToolbar);
- }
- mScrimAlpha = alpha;
- ViewCompat.postInvalidateOnAnimation(CollapsingToolbarLayout.this);
- }
- }
-
- int getScrimAlpha() {
- return mScrimAlpha;
- }
-
- /**
- * Set the drawable to use for the content scrim from resources. Providing null will disable
- * the scrim functionality.
- *
- * @param drawable the drawable to display
- *
- * @attr ref R.styleable#CollapsingToolbarLayout_contentScrim
- * @see #getContentScrim()
- */
- public void setContentScrim(@Nullable Drawable drawable) {
- if (mContentScrim != drawable) {
- if (mContentScrim != null) {
- mContentScrim.setCallback(null);
- }
- mContentScrim = drawable != null ? drawable.mutate() : null;
- if (mContentScrim != null) {
- mContentScrim.setBounds(0, 0, getWidth(), getHeight());
- mContentScrim.setCallback(this);
- mContentScrim.setAlpha(mScrimAlpha);
- }
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
-
- /**
- * Set the color to use for the content scrim.
- *
- * @param color the color to display
- *
- * @attr ref R.styleable#CollapsingToolbarLayout_contentScrim
- * @see #getContentScrim()
- */
- public void setContentScrimColor(@ColorInt int color) {
- setContentScrim(new ColorDrawable(color));
- }
-
- /**
- * Set the drawable to use for the content scrim from resources.
- *
- * @param resId drawable resource id
- *
- * @attr ref R.styleable#CollapsingToolbarLayout_contentScrim
- * @see #getContentScrim()
- */
- public void setContentScrimResource(@DrawableRes int resId) {
- setContentScrim(ContextCompat.getDrawable(getContext(), resId));
-
- }
-
- /**
- * Returns the drawable which is used for the foreground scrim.
- *
- * @attr ref R.styleable#CollapsingToolbarLayout_contentScrim
- * @see #setContentScrim(Drawable)
- */
- @Nullable
- public Drawable getContentScrim() {
- return mContentScrim;
- }
-
- /**
- * Set the drawable to use for the status bar scrim from resources.
- * Providing null will disable the scrim functionality.
- *
- * <p>This scrim is only shown when we have been given a top system inset.</p>
- *
- * @param drawable the drawable to display
- *
- * @attr ref R.styleable#CollapsingToolbarLayout_statusBarScrim
- * @see #getStatusBarScrim()
- */
- public void setStatusBarScrim(@Nullable Drawable drawable) {
- if (mStatusBarScrim != drawable) {
- if (mStatusBarScrim != null) {
- mStatusBarScrim.setCallback(null);
- }
- mStatusBarScrim = drawable != null ? drawable.mutate() : null;
- if (mStatusBarScrim != null) {
- if (mStatusBarScrim.isStateful()) {
- mStatusBarScrim.setState(getDrawableState());
- }
- DrawableCompat.setLayoutDirection(mStatusBarScrim,
- ViewCompat.getLayoutDirection(this));
- mStatusBarScrim.setVisible(getVisibility() == VISIBLE, false);
- mStatusBarScrim.setCallback(this);
- mStatusBarScrim.setAlpha(mScrimAlpha);
- }
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
-
- @Override
- protected void drawableStateChanged() {
- super.drawableStateChanged();
-
- final int[] state = getDrawableState();
- boolean changed = false;
-
- Drawable d = mStatusBarScrim;
- if (d != null && d.isStateful()) {
- changed |= d.setState(state);
- }
- d = mContentScrim;
- if (d != null && d.isStateful()) {
- changed |= d.setState(state);
- }
- if (mCollapsingTextHelper != null) {
- changed |= mCollapsingTextHelper.setState(state);
- }
-
- if (changed) {
- invalidate();
- }
- }
-
- @Override
- protected boolean verifyDrawable(Drawable who) {
- return super.verifyDrawable(who) || who == mContentScrim || who == mStatusBarScrim;
- }
-
- @Override
- public void setVisibility(int visibility) {
- super.setVisibility(visibility);
-
- final boolean visible = visibility == VISIBLE;
- if (mStatusBarScrim != null && mStatusBarScrim.isVisible() != visible) {
- mStatusBarScrim.setVisible(visible, false);
- }
- if (mContentScrim != null && mContentScrim.isVisible() != visible) {
- mContentScrim.setVisible(visible, false);
- }
- }
-
- /**
- * Set the color to use for the status bar scrim.
- *
- * <p>This scrim is only shown when we have been given a top system inset.</p>
- *
- * @param color the color to display
- *
- * @attr ref R.styleable#CollapsingToolbarLayout_statusBarScrim
- * @see #getStatusBarScrim()
- */
- public void setStatusBarScrimColor(@ColorInt int color) {
- setStatusBarScrim(new ColorDrawable(color));
- }
-
- /**
- * Set the drawable to use for the content scrim from resources.
- *
- * @param resId drawable resource id
- *
- * @attr ref R.styleable#CollapsingToolbarLayout_statusBarScrim
- * @see #getStatusBarScrim()
- */
- public void setStatusBarScrimResource(@DrawableRes int resId) {
- setStatusBarScrim(ContextCompat.getDrawable(getContext(), resId));
- }
-
- /**
- * Returns the drawable which is used for the status bar scrim.
- *
- * @attr ref R.styleable#CollapsingToolbarLayout_statusBarScrim
- * @see #setStatusBarScrim(Drawable)
- */
- @Nullable
- public Drawable getStatusBarScrim() {
- return mStatusBarScrim;
- }
-
- /**
- * Sets the text color and size for the collapsed title from the specified
- * TextAppearance resource.
- *
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_collapsedTitleTextAppearance
- */
- public void setCollapsedTitleTextAppearance(@StyleRes int resId) {
- mCollapsingTextHelper.setCollapsedTextAppearance(resId);
- }
-
- /**
- * Sets the text color of the collapsed title.
- *
- * @param color The new text color in ARGB format
- */
- public void setCollapsedTitleTextColor(@ColorInt int color) {
- setCollapsedTitleTextColor(ColorStateList.valueOf(color));
- }
-
- /**
- * Sets the text colors of the collapsed title.
- *
- * @param colors ColorStateList containing the new text colors
- */
- public void setCollapsedTitleTextColor(@NonNull ColorStateList colors) {
- mCollapsingTextHelper.setCollapsedTextColor(colors);
- }
-
- /**
- * Sets the horizontal alignment of the collapsed title and the vertical gravity that will
- * be used when there is extra space in the collapsed bounds beyond what is required for
- * the title itself.
- *
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_collapsedTitleGravity
- */
- public void setCollapsedTitleGravity(int gravity) {
- mCollapsingTextHelper.setCollapsedTextGravity(gravity);
- }
-
- /**
- * Returns the horizontal and vertical alignment for title when collapsed.
- *
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_collapsedTitleGravity
- */
- public int getCollapsedTitleGravity() {
- return mCollapsingTextHelper.getCollapsedTextGravity();
- }
-
- /**
- * Sets the text color and size for the expanded title from the specified
- * TextAppearance resource.
- *
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleTextAppearance
- */
- public void setExpandedTitleTextAppearance(@StyleRes int resId) {
- mCollapsingTextHelper.setExpandedTextAppearance(resId);
- }
-
- /**
- * Sets the text color of the expanded title.
- *
- * @param color The new text color in ARGB format
- */
- public void setExpandedTitleColor(@ColorInt int color) {
- setExpandedTitleTextColor(ColorStateList.valueOf(color));
- }
-
- /**
- * Sets the text colors of the expanded title.
- *
- * @param colors ColorStateList containing the new text colors
- */
- public void setExpandedTitleTextColor(@NonNull ColorStateList colors) {
- mCollapsingTextHelper.setExpandedTextColor(colors);
- }
-
- /**
- * Sets the horizontal alignment of the expanded title and the vertical gravity that will
- * be used when there is extra space in the expanded bounds beyond what is required for
- * the title itself.
- *
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleGravity
- */
- public void setExpandedTitleGravity(int gravity) {
- mCollapsingTextHelper.setExpandedTextGravity(gravity);
- }
-
- /**
- * Returns the horizontal and vertical alignment for title when expanded.
- *
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleGravity
- */
- public int getExpandedTitleGravity() {
- return mCollapsingTextHelper.getExpandedTextGravity();
- }
-
- /**
- * Set the typeface to use for the collapsed title.
- *
- * @param typeface typeface to use, or {@code null} to use the default.
- */
- public void setCollapsedTitleTypeface(@Nullable Typeface typeface) {
- mCollapsingTextHelper.setCollapsedTypeface(typeface);
- }
-
- /**
- * Returns the typeface used for the collapsed title.
- */
- @NonNull
- public Typeface getCollapsedTitleTypeface() {
- return mCollapsingTextHelper.getCollapsedTypeface();
- }
-
- /**
- * Set the typeface to use for the expanded title.
- *
- * @param typeface typeface to use, or {@code null} to use the default.
- */
- public void setExpandedTitleTypeface(@Nullable Typeface typeface) {
- mCollapsingTextHelper.setExpandedTypeface(typeface);
- }
-
- /**
- * Returns the typeface used for the expanded title.
- */
- @NonNull
- public Typeface getExpandedTitleTypeface() {
- return mCollapsingTextHelper.getExpandedTypeface();
- }
-
- /**
- * Sets the expanded title margins.
- *
- * @param start the starting title margin in pixels
- * @param top the top title margin in pixels
- * @param end the ending title margin in pixels
- * @param bottom the bottom title margin in pixels
- *
- * @see #getExpandedTitleMarginStart()
- * @see #getExpandedTitleMarginTop()
- * @see #getExpandedTitleMarginEnd()
- * @see #getExpandedTitleMarginBottom()
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMargin
- */
- public void setExpandedTitleMargin(int start, int top, int end, int bottom) {
- mExpandedMarginStart = start;
- mExpandedMarginTop = top;
- mExpandedMarginEnd = end;
- mExpandedMarginBottom = bottom;
- requestLayout();
- }
-
- /**
- * @return the starting expanded title margin in pixels
- *
- * @see #setExpandedTitleMarginStart(int)
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart
- */
- public int getExpandedTitleMarginStart() {
- return mExpandedMarginStart;
- }
-
- /**
- * Sets the starting expanded title margin in pixels.
- *
- * @param margin the starting title margin in pixels
- * @see #getExpandedTitleMarginStart()
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart
- */
- public void setExpandedTitleMarginStart(int margin) {
- mExpandedMarginStart = margin;
- requestLayout();
- }
-
- /**
- * @return the top expanded title margin in pixels
- * @see #setExpandedTitleMarginTop(int)
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginTop
- */
- public int getExpandedTitleMarginTop() {
- return mExpandedMarginTop;
- }
-
- /**
- * Sets the top expanded title margin in pixels.
- *
- * @param margin the top title margin in pixels
- * @see #getExpandedTitleMarginTop()
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginTop
- */
- public void setExpandedTitleMarginTop(int margin) {
- mExpandedMarginTop = margin;
- requestLayout();
- }
-
- /**
- * @return the ending expanded title margin in pixels
- * @see #setExpandedTitleMarginEnd(int)
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd
- */
- public int getExpandedTitleMarginEnd() {
- return mExpandedMarginEnd;
- }
-
- /**
- * Sets the ending expanded title margin in pixels.
- *
- * @param margin the ending title margin in pixels
- * @see #getExpandedTitleMarginEnd()
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd
- */
- public void setExpandedTitleMarginEnd(int margin) {
- mExpandedMarginEnd = margin;
- requestLayout();
- }
-
- /**
- * @return the bottom expanded title margin in pixels
- * @see #setExpandedTitleMarginBottom(int)
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom
- */
- public int getExpandedTitleMarginBottom() {
- return mExpandedMarginBottom;
- }
-
- /**
- * Sets the bottom expanded title margin in pixels.
- *
- * @param margin the bottom title margin in pixels
- * @see #getExpandedTitleMarginBottom()
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom
- */
- public void setExpandedTitleMarginBottom(int margin) {
- mExpandedMarginBottom = margin;
- requestLayout();
- }
-
- /**
- * Set the amount of visible height in pixels used to define when to trigger a scrim
- * visibility change.
- *
- * <p>If the visible height of this view is less than the given value, the scrims will be
- * made visible, otherwise they are hidden.</p>
- *
- * @param height value in pixels used to define when to trigger a scrim visibility change
- *
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_scrimVisibleHeightTrigger
- */
- public void setScrimVisibleHeightTrigger(@IntRange(from = 0) final int height) {
- if (mScrimVisibleHeightTrigger != height) {
- mScrimVisibleHeightTrigger = height;
- // Update the scrim visibility
- updateScrimVisibility();
- }
- }
-
- /**
- * Returns the amount of visible height in pixels used to define when to trigger a scrim
- * visibility change.
- *
- * @see #setScrimVisibleHeightTrigger(int)
- */
- public int getScrimVisibleHeightTrigger() {
- if (mScrimVisibleHeightTrigger >= 0) {
- // If we have one explicitly set, return it
- return mScrimVisibleHeightTrigger;
- }
-
- // Otherwise we'll use the default computed value
- final int insetTop = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
-
- final int minHeight = ViewCompat.getMinimumHeight(this);
- if (minHeight > 0) {
- // If we have a minHeight set, lets use 2 * minHeight (capped at our height)
- return Math.min((minHeight * 2) + insetTop, getHeight());
- }
-
- // If we reach here then we don't have a min height set. Instead we'll take a
- // guess at 1/3 of our height being visible
- return getHeight() / 3;
- }
-
- /**
- * Set the duration used for scrim visibility animations.
- *
- * @param duration the duration to use in milliseconds
- *
- * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_scrimAnimationDuration
- */
- public void setScrimAnimationDuration(@IntRange(from = 0) final long duration) {
- mScrimAnimationDuration = duration;
- }
-
- /**
- * Returns the duration in milliseconds used for scrim visibility animations.
- */
- public long getScrimAnimationDuration() {
- return mScrimAnimationDuration;
- }
-
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams;
- }
-
- @Override
- protected LayoutParams generateDefaultLayoutParams() {
- return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
- }
-
- @Override
- public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new LayoutParams(getContext(), attrs);
- }
-
- @Override
- protected FrameLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return new LayoutParams(p);
- }
-
- public static class LayoutParams extends FrameLayout.LayoutParams {
-
- private static final float DEFAULT_PARALLAX_MULTIPLIER = 0.5f;
-
- /** @hide */
- @RestrictTo(LIBRARY_GROUP)
- @IntDef({
- COLLAPSE_MODE_OFF,
- COLLAPSE_MODE_PIN,
- COLLAPSE_MODE_PARALLAX
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface CollapseMode {}
-
- /**
- * The view will act as normal with no collapsing behavior.
- */
- public static final int COLLAPSE_MODE_OFF = 0;
-
- /**
- * The view will pin in place until it reaches the bottom of the
- * {@link CollapsingToolbarLayout}.
- */
- public static final int COLLAPSE_MODE_PIN = 1;
-
- /**
- * The view will scroll in a parallax fashion. See {@link #setParallaxMultiplier(float)}
- * to change the multiplier used.
- */
- public static final int COLLAPSE_MODE_PARALLAX = 2;
-
- int mCollapseMode = COLLAPSE_MODE_OFF;
- float mParallaxMult = DEFAULT_PARALLAX_MULTIPLIER;
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
-
- TypedArray a = c.obtainStyledAttributes(attrs,
- R.styleable.CollapsingToolbarLayout_Layout);
- mCollapseMode = a.getInt(
- R.styleable.CollapsingToolbarLayout_Layout_layout_collapseMode,
- COLLAPSE_MODE_OFF);
- setParallaxMultiplier(a.getFloat(
- R.styleable.CollapsingToolbarLayout_Layout_layout_collapseParallaxMultiplier,
- DEFAULT_PARALLAX_MULTIPLIER));
- a.recycle();
- }
-
- public LayoutParams(int width, int height) {
- super(width, height);
- }
-
- public LayoutParams(int width, int height, int gravity) {
- super(width, height, gravity);
- }
-
- public LayoutParams(ViewGroup.LayoutParams p) {
- super(p);
- }
-
- public LayoutParams(MarginLayoutParams source) {
- super(source);
- }
-
- @RequiresApi(19)
- public LayoutParams(FrameLayout.LayoutParams source) {
- // The copy constructor called here only exists on API 19+.
- super(source);
- }
-
- /**
- * Set the collapse mode.
- *
- * @param collapseMode one of {@link #COLLAPSE_MODE_OFF}, {@link #COLLAPSE_MODE_PIN}
- * or {@link #COLLAPSE_MODE_PARALLAX}.
- */
- public void setCollapseMode(@CollapseMode int collapseMode) {
- mCollapseMode = collapseMode;
- }
-
- /**
- * Returns the requested collapse mode.
- *
- * @return the current mode. One of {@link #COLLAPSE_MODE_OFF}, {@link #COLLAPSE_MODE_PIN}
- * or {@link #COLLAPSE_MODE_PARALLAX}.
- */
- @CollapseMode
- public int getCollapseMode() {
- return mCollapseMode;
- }
-
- /**
- * Set the parallax scroll multiplier used in conjunction with
- * {@link #COLLAPSE_MODE_PARALLAX}. A value of {@code 0.0} indicates no movement at all,
- * {@code 1.0f} indicates normal scroll movement.
- *
- * @param multiplier the multiplier.
- *
- * @see #getParallaxMultiplier()
- */
- public void setParallaxMultiplier(float multiplier) {
- mParallaxMult = multiplier;
- }
-
- /**
- * Returns the parallax scroll multiplier used in conjunction with
- * {@link #COLLAPSE_MODE_PARALLAX}.
- *
- * @see #setParallaxMultiplier(float)
- */
- public float getParallaxMultiplier() {
- return mParallaxMult;
- }
- }
-
- /**
- * Show or hide the scrims if needed
- */
- final void updateScrimVisibility() {
- if (mContentScrim != null || mStatusBarScrim != null) {
- setScrimsShown(getHeight() + mCurrentOffset < getScrimVisibleHeightTrigger());
- }
- }
-
- final int getMaxOffsetForPinChild(View child) {
- final ViewOffsetHelper offsetHelper = getViewOffsetHelper(child);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- return getHeight()
- - offsetHelper.getLayoutTop()
- - child.getHeight()
- - lp.bottomMargin;
- }
-
- private class OffsetUpdateListener implements AppBarLayout.OnOffsetChangedListener {
- OffsetUpdateListener() {
- }
-
- @Override
- public void onOffsetChanged(AppBarLayout layout, int verticalOffset) {
- mCurrentOffset = verticalOffset;
-
- final int insetTop = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
-
- for (int i = 0, z = getChildCount(); i < z; i++) {
- final View child = getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- final ViewOffsetHelper offsetHelper = getViewOffsetHelper(child);
-
- switch (lp.mCollapseMode) {
- case LayoutParams.COLLAPSE_MODE_PIN:
- offsetHelper.setTopAndBottomOffset(MathUtils.clamp(
- -verticalOffset, 0, getMaxOffsetForPinChild(child)));
- break;
- case LayoutParams.COLLAPSE_MODE_PARALLAX:
- offsetHelper.setTopAndBottomOffset(
- Math.round(-verticalOffset * lp.mParallaxMult));
- break;
- }
- }
-
- // Show or hide the scrims if needed
- updateScrimVisibility();
-
- if (mStatusBarScrim != null && insetTop > 0) {
- ViewCompat.postInvalidateOnAnimation(CollapsingToolbarLayout.this);
- }
-
- // Update the collapsing text's fraction
- final int expandRange = getHeight() - ViewCompat.getMinimumHeight(
- CollapsingToolbarLayout.this) - insetTop;
- mCollapsingTextHelper.setExpansionFraction(
- Math.abs(verticalOffset) / (float) expandRange);
- }
- }
-}
diff --git a/android/support/design/widget/CoordinatorLayout.java b/android/support/design/widget/CoordinatorLayout.java
index 03cce024..b7f47f40 100644
--- a/android/support/design/widget/CoordinatorLayout.java
+++ b/android/support/design/widget/CoordinatorLayout.java
@@ -366,7 +366,11 @@ public class CoordinatorLayout extends ViewGroup implements NestedScrollingParen
return insets;
}
- final WindowInsetsCompat getLastWindowInsets() {
+ /**
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ public final WindowInsetsCompat getLastWindowInsets() {
return mLastInsets;
}
diff --git a/android/support/design/widget/DrawableUtils.java b/android/support/design/widget/DrawableUtils.java
deleted file mode 100644
index df1c04b0..00000000
--- a/android/support/design/widget/DrawableUtils.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.DrawableContainer;
-import android.util.Log;
-
-import java.lang.reflect.Method;
-
-/**
- * Caution. Gross hacks ahead.
- */
-class DrawableUtils {
-
- private static final String LOG_TAG = "DrawableUtils";
-
- private static Method sSetConstantStateMethod;
- private static boolean sSetConstantStateMethodFetched;
-
- private DrawableUtils() {}
-
- static boolean setContainerConstantState(DrawableContainer drawable,
- Drawable.ConstantState constantState) {
- // We can use getDeclaredMethod() on v9+
- return setContainerConstantStateV9(drawable, constantState);
- }
-
- private static boolean setContainerConstantStateV9(DrawableContainer drawable,
- Drawable.ConstantState constantState) {
- if (!sSetConstantStateMethodFetched) {
- try {
- sSetConstantStateMethod = DrawableContainer.class.getDeclaredMethod(
- "setConstantState", DrawableContainer.DrawableContainerState.class);
- sSetConstantStateMethod.setAccessible(true);
- } catch (NoSuchMethodException e) {
- Log.e(LOG_TAG, "Could not fetch setConstantState(). Oh well.");
- }
- sSetConstantStateMethodFetched = true;
- }
- if (sSetConstantStateMethod != null) {
- try {
- sSetConstantStateMethod.invoke(drawable, constantState);
- return true;
- } catch (Exception e) {
- Log.e(LOG_TAG, "Could not invoke setConstantState(). Oh well.");
- }
- }
- return false;
- }
-}
diff --git a/android/support/design/widget/FloatingActionButton.java b/android/support/design/widget/FloatingActionButton.java
deleted file mode 100644
index f37b3798..00000000
--- a/android/support/design/widget/FloatingActionButton.java
+++ /dev/null
@@ -1,870 +0,0 @@
-/*
- * 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.design.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.ColorInt;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.VisibleForTesting;
-import android.support.design.R;
-import android.support.design.widget.FloatingActionButtonImpl.InternalVisibilityChangedListener;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.widget.ViewGroupUtils;
-import android.support.v7.widget.AppCompatImageHelper;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-
-/**
- * Floating action buttons are used for a special type of promoted action. They are distinguished
- * by a circled icon floating above the UI and have special motion behaviors related to morphing,
- * launching, and the transferring anchor point.
- *
- * <p>Floating action buttons come in two sizes: the default and the mini. The size can be
- * controlled with the {@code fabSize} attribute.</p>
- *
- * <p>As this class descends from {@link ImageView}, you can control the icon which is displayed
- * via {@link #setImageDrawable(Drawable)}.</p>
- *
- * <p>The background color of this view defaults to the your theme's {@code colorAccent}. If you
- * wish to change this at runtime then you can do so via
- * {@link #setBackgroundTintList(ColorStateList)}.</p>
- */
-@CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class)
-public class FloatingActionButton extends VisibilityAwareImageButton {
-
- private static final String LOG_TAG = "FloatingActionButton";
-
- /**
- * Callback to be invoked when the visibility of a FloatingActionButton changes.
- */
- public abstract static class OnVisibilityChangedListener {
- /**
- * Called when a FloatingActionButton has been
- * {@link #show(OnVisibilityChangedListener) shown}.
- *
- * @param fab the FloatingActionButton that was shown.
- */
- public void onShown(FloatingActionButton fab) {}
-
- /**
- * Called when a FloatingActionButton has been
- * {@link #hide(OnVisibilityChangedListener) hidden}.
- *
- * @param fab the FloatingActionButton that was hidden.
- */
- public void onHidden(FloatingActionButton fab) {}
- }
-
- // These values must match those in the attrs declaration
-
- /**
- * The mini sized button. Will always been smaller than {@link #SIZE_NORMAL}.
- *
- * @see #setSize(int)
- */
- public static final int SIZE_MINI = 1;
-
- /**
- * The normal sized button. Will always been larger than {@link #SIZE_MINI}.
- *
- * @see #setSize(int)
- */
- public static final int SIZE_NORMAL = 0;
-
- /**
- * Size which will change based on the window size. For small sized windows
- * (largest screen dimension < 470dp) this will select a small sized button, and for
- * larger sized windows it will select a larger size.
- *
- * @see #setSize(int)
- */
- public static final int SIZE_AUTO = -1;
-
- /**
- * Indicates that FloatingActionButton should not have a custom size.
- */
- public static final int NO_CUSTOM_SIZE = 0;
-
- /**
- * The switch point for the largest screen edge where SIZE_AUTO switches from mini to normal.
- */
- private static final int AUTO_MINI_LARGEST_SCREEN_WIDTH = 470;
-
- /** @hide */
- @RestrictTo(LIBRARY_GROUP)
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({SIZE_MINI, SIZE_NORMAL, SIZE_AUTO})
- public @interface Size {}
-
- private ColorStateList mBackgroundTint;
- private PorterDuff.Mode mBackgroundTintMode;
-
- private int mBorderWidth;
- private int mRippleColor;
- private int mSize;
- private int mCustomSize;
- int mImagePadding;
- private int mMaxImageSize;
-
- boolean mCompatPadding;
- final Rect mShadowPadding = new Rect();
- private final Rect mTouchArea = new Rect();
-
- private AppCompatImageHelper mImageHelper;
-
- private FloatingActionButtonImpl mImpl;
-
- public FloatingActionButton(Context context) {
- this(context, null);
- }
-
- public FloatingActionButton(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- ThemeUtils.checkAppCompatTheme(context);
-
- TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.FloatingActionButton, defStyleAttr,
- R.style.Widget_Design_FloatingActionButton);
- mBackgroundTint = a.getColorStateList(R.styleable.FloatingActionButton_backgroundTint);
- mBackgroundTintMode = ViewUtils.parseTintMode(a.getInt(
- R.styleable.FloatingActionButton_backgroundTintMode, -1), null);
- mRippleColor = a.getColor(R.styleable.FloatingActionButton_rippleColor, 0);
- mSize = a.getInt(R.styleable.FloatingActionButton_fabSize, SIZE_AUTO);
- mCustomSize = a.getDimensionPixelSize(R.styleable.FloatingActionButton_fabCustomSize,
- 0);
- mBorderWidth = a.getDimensionPixelSize(R.styleable.FloatingActionButton_borderWidth, 0);
- final float elevation = a.getDimension(R.styleable.FloatingActionButton_elevation, 0f);
- final float pressedTranslationZ = a.getDimension(
- R.styleable.FloatingActionButton_pressedTranslationZ, 0f);
- mCompatPadding = a.getBoolean(R.styleable.FloatingActionButton_useCompatPadding, false);
- a.recycle();
-
- mImageHelper = new AppCompatImageHelper(this);
- mImageHelper.loadFromAttributes(attrs, defStyleAttr);
-
- mMaxImageSize = (int) getResources().getDimension(R.dimen.design_fab_image_size);
-
- getImpl().setBackgroundDrawable(mBackgroundTint, mBackgroundTintMode,
- mRippleColor, mBorderWidth);
- getImpl().setElevation(elevation);
- getImpl().setPressedTranslationZ(pressedTranslationZ);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int preferredSize = getSizeDimension();
-
- mImagePadding = (preferredSize - mMaxImageSize) / 2;
- getImpl().updatePadding();
-
- final int w = resolveAdjustedSize(preferredSize, widthMeasureSpec);
- final int h = resolveAdjustedSize(preferredSize, heightMeasureSpec);
-
- // As we want to stay circular, we set both dimensions to be the
- // smallest resolved dimension
- final int d = Math.min(w, h);
-
- // We add the shadow's padding to the measured dimension
- setMeasuredDimension(
- d + mShadowPadding.left + mShadowPadding.right,
- d + mShadowPadding.top + mShadowPadding.bottom);
- }
-
- /**
- * Returns the ripple color for this button.
- *
- * @return the ARGB color used for the ripple
- * @see #setRippleColor(int)
- */
- @ColorInt
- public int getRippleColor() {
- return mRippleColor;
- }
-
- /**
- * Sets the ripple color for this button.
- *
- * <p>When running on devices with KitKat or below, we draw this color as a filled circle
- * rather than a ripple.</p>
- *
- * @param color ARGB color to use for the ripple
- * @attr ref android.support.design.R.styleable#FloatingActionButton_rippleColor
- * @see #getRippleColor()
- */
- public void setRippleColor(@ColorInt int color) {
- if (mRippleColor != color) {
- mRippleColor = color;
- getImpl().setRippleColor(color);
- }
- }
-
- /**
- * Returns the tint applied to the background drawable, if specified.
- *
- * @return the tint applied to the background drawable
- * @see #setBackgroundTintList(ColorStateList)
- */
- @Nullable
- @Override
- public ColorStateList getBackgroundTintList() {
- return mBackgroundTint;
- }
-
- /**
- * Applies a tint to the background drawable. Does not modify the current tint
- * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
- *
- * @param tint the tint to apply, may be {@code null} to clear tint
- */
- @Override
- public void setBackgroundTintList(@Nullable ColorStateList tint) {
- if (mBackgroundTint != tint) {
- mBackgroundTint = tint;
- getImpl().setBackgroundTintList(tint);
- }
- }
-
- /**
- * Returns the blending mode used to apply the tint to the background
- * drawable, if specified.
- *
- * @return the blending mode used to apply the tint to the background
- * drawable
- * @see #setBackgroundTintMode(PorterDuff.Mode)
- */
- @Nullable
- @Override
- public PorterDuff.Mode getBackgroundTintMode() {
- return mBackgroundTintMode;
- }
-
- /**
- * Specifies the blending mode used to apply the tint specified by
- * {@link #setBackgroundTintList(ColorStateList)}} to the background
- * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
- *
- * @param tintMode the blending mode used to apply the tint, may be
- * {@code null} to clear tint
- */
- @Override
- public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
- if (mBackgroundTintMode != tintMode) {
- mBackgroundTintMode = tintMode;
- getImpl().setBackgroundTintMode(tintMode);
- }
- }
-
- @Override
- public void setBackgroundDrawable(Drawable background) {
- Log.i(LOG_TAG, "Setting a custom background is not supported.");
- }
-
- @Override
- public void setBackgroundResource(int resid) {
- Log.i(LOG_TAG, "Setting a custom background is not supported.");
- }
-
- @Override
- public void setBackgroundColor(int color) {
- Log.i(LOG_TAG, "Setting a custom background is not supported.");
- }
-
- @Override
- public void setImageResource(@DrawableRes int resId) {
- // Intercept this call and instead retrieve the Drawable via the image helper
- mImageHelper.setImageResource(resId);
- }
-
- /**
- * Shows the button.
- * <p>This method will animate the button show if the view has already been laid out.</p>
- */
- public void show() {
- show(null);
- }
-
- /**
- * Shows the button.
- * <p>This method will animate the button show if the view has already been laid out.</p>
- *
- * @param listener the listener to notify when this view is shown
- */
- public void show(@Nullable final OnVisibilityChangedListener listener) {
- show(listener, true);
- }
-
- void show(OnVisibilityChangedListener listener, boolean fromUser) {
- getImpl().show(wrapOnVisibilityChangedListener(listener), fromUser);
- }
-
- /**
- * Hides the button.
- * <p>This method will animate the button hide if the view has already been laid out.</p>
- */
- public void hide() {
- hide(null);
- }
-
- /**
- * Hides the button.
- * <p>This method will animate the button hide if the view has already been laid out.</p>
- *
- * @param listener the listener to notify when this view is hidden
- */
- public void hide(@Nullable OnVisibilityChangedListener listener) {
- hide(listener, true);
- }
-
- void hide(@Nullable OnVisibilityChangedListener listener, boolean fromUser) {
- getImpl().hide(wrapOnVisibilityChangedListener(listener), fromUser);
- }
-
- /**
- * Set whether FloatingActionButton should add inner padding on platforms Lollipop and after,
- * to ensure consistent dimensions on all platforms.
- *
- * @param useCompatPadding true if FloatingActionButton is adding inner padding on platforms
- * Lollipop and after, to ensure consistent dimensions on all platforms.
- *
- * @attr ref android.support.design.R.styleable#FloatingActionButton_useCompatPadding
- * @see #getUseCompatPadding()
- */
- public void setUseCompatPadding(boolean useCompatPadding) {
- if (mCompatPadding != useCompatPadding) {
- mCompatPadding = useCompatPadding;
- getImpl().onCompatShadowChanged();
- }
- }
-
- /**
- * Returns whether FloatingActionButton will add inner padding on platforms Lollipop and after.
- *
- * @return true if FloatingActionButton is adding inner padding on platforms Lollipop and after,
- * to ensure consistent dimensions on all platforms.
- *
- * @attr ref android.support.design.R.styleable#FloatingActionButton_useCompatPadding
- * @see #setUseCompatPadding(boolean)
- */
- public boolean getUseCompatPadding() {
- return mCompatPadding;
- }
-
- /**
- * Sets the size of the button.
- *
- * <p>The options relate to the options available on the material design specification.
- * {@link #SIZE_NORMAL} is larger than {@link #SIZE_MINI}. {@link #SIZE_AUTO} will choose
- * an appropriate size based on the screen size.</p>
- *
- * @param size one of {@link #SIZE_NORMAL}, {@link #SIZE_MINI} or {@link #SIZE_AUTO}
- *
- * @attr ref android.support.design.R.styleable#FloatingActionButton_fabSize
- */
- public void setSize(@Size int size) {
- if (size != mSize) {
- mSize = size;
- requestLayout();
- }
- }
-
- /**
- * Returns the chosen size for this button.
- *
- * @return one of {@link #SIZE_NORMAL}, {@link #SIZE_MINI} or {@link #SIZE_AUTO}
- * @see #setSize(int)
- */
- @Size
- public int getSize() {
- return mSize;
- }
-
- @Nullable
- private InternalVisibilityChangedListener wrapOnVisibilityChangedListener(
- @Nullable final OnVisibilityChangedListener listener) {
- if (listener == null) {
- return null;
- }
-
- return new InternalVisibilityChangedListener() {
- @Override
- public void onShown() {
- listener.onShown(FloatingActionButton.this);
- }
-
- @Override
- public void onHidden() {
- listener.onHidden(FloatingActionButton.this);
- }
- };
- }
-
- /**
- * Sets the size of the button to be a custom value in pixels. If set to
- * {@link #NO_CUSTOM_SIZE}, custom size will not be used and size will be calculated according
- * to {@link #setSize(int)} method.
- *
- * @param size preferred size in pixels, or zero
- *
- * @attr ref android.support.design.R.styleable#FloatingActionButton_fabCustomSize
- */
- public void setCustomSize(int size) {
- if (size < 0) {
- throw new IllegalArgumentException("Custom size should be non-negative.");
- }
- mCustomSize = size;
- }
-
- /**
- * Returns the custom size for this button.
- *
- * @return size in pixels, or {@link #NO_CUSTOM_SIZE}
- */
- public int getCustomSize() {
- return mCustomSize;
- }
-
- int getSizeDimension() {
- return getSizeDimension(mSize);
- }
-
- private int getSizeDimension(@Size final int size) {
- final Resources res = getResources();
- // If custom size is set, return it
- if (mCustomSize != NO_CUSTOM_SIZE) {
- return mCustomSize;
- }
- switch (size) {
- case SIZE_AUTO:
- // If we're set to auto, grab the size from resources and refresh
- final int width = res.getConfiguration().screenWidthDp;
- final int height = res.getConfiguration().screenHeightDp;
- return Math.max(width, height) < AUTO_MINI_LARGEST_SCREEN_WIDTH
- ? getSizeDimension(SIZE_MINI)
- : getSizeDimension(SIZE_NORMAL);
- case SIZE_MINI:
- return res.getDimensionPixelSize(R.dimen.design_fab_size_mini);
- case SIZE_NORMAL:
- default:
- return res.getDimensionPixelSize(R.dimen.design_fab_size_normal);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- getImpl().onAttachedToWindow();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- getImpl().onDetachedFromWindow();
- }
-
- @Override
- protected void drawableStateChanged() {
- super.drawableStateChanged();
- getImpl().onDrawableStateChanged(getDrawableState());
- }
-
- @Override
- public void jumpDrawablesToCurrentState() {
- super.jumpDrawablesToCurrentState();
- getImpl().jumpDrawableToCurrentState();
- }
-
- /**
- * Return in {@code rect} the bounds of the actual floating action button content in view-local
- * coordinates. This is defined as anything within any visible shadow.
- *
- * @return true if this view actually has been laid out and has a content rect, else false.
- */
- public boolean getContentRect(@NonNull Rect rect) {
- if (ViewCompat.isLaidOut(this)) {
- rect.set(0, 0, getWidth(), getHeight());
- rect.left += mShadowPadding.left;
- rect.top += mShadowPadding.top;
- rect.right -= mShadowPadding.right;
- rect.bottom -= mShadowPadding.bottom;
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Returns the FloatingActionButton's background, minus any compatible shadow implementation.
- */
- @NonNull
- public Drawable getContentBackground() {
- return getImpl().getContentBackground();
- }
-
- private static int resolveAdjustedSize(int desiredSize, int measureSpec) {
- int result = desiredSize;
- int specMode = MeasureSpec.getMode(measureSpec);
- int specSize = MeasureSpec.getSize(measureSpec);
- switch (specMode) {
- case MeasureSpec.UNSPECIFIED:
- // Parent says we can be as big as we want. Just don't be larger
- // than max size imposed on ourselves.
- result = desiredSize;
- break;
- case MeasureSpec.AT_MOST:
- // Parent says we can be as big as we want, up to specSize.
- // Don't be larger than specSize, and don't be larger than
- // the max size imposed on ourselves.
- result = Math.min(desiredSize, specSize);
- break;
- case MeasureSpec.EXACTLY:
- // No choice. Do what we are told.
- result = specSize;
- break;
- }
- return result;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN:
- // Skipping the gesture if it doesn't start in in the FAB 'content' area
- if (getContentRect(mTouchArea)
- && !mTouchArea.contains((int) ev.getX(), (int) ev.getY())) {
- return false;
- }
- break;
- }
- return super.onTouchEvent(ev);
- }
-
- /**
- * Behavior designed for use with {@link FloatingActionButton} instances. Its main function
- * is to move {@link FloatingActionButton} views so that any displayed {@link Snackbar}s do
- * not cover them.
- */
- public static class Behavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
- private static final boolean AUTO_HIDE_DEFAULT = true;
-
- private Rect mTmpRect;
- private OnVisibilityChangedListener mInternalAutoHideListener;
- private boolean mAutoHideEnabled;
-
- public Behavior() {
- super();
- mAutoHideEnabled = AUTO_HIDE_DEFAULT;
- }
-
- public Behavior(Context context, AttributeSet attrs) {
- super(context, attrs);
- TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.FloatingActionButton_Behavior_Layout);
- mAutoHideEnabled = a.getBoolean(
- R.styleable.FloatingActionButton_Behavior_Layout_behavior_autoHide,
- AUTO_HIDE_DEFAULT);
- a.recycle();
- }
-
- /**
- * Sets whether the associated FloatingActionButton automatically hides when there is
- * not enough space to be displayed. This works with {@link AppBarLayout}
- * and {@link BottomSheetBehavior}.
- *
- * @attr ref android.support.design.R.styleable#FloatingActionButton_Behavior_Layout_behavior_autoHide
- * @param autoHide true to enable automatic hiding
- */
- public void setAutoHideEnabled(boolean autoHide) {
- mAutoHideEnabled = autoHide;
- }
-
- /**
- * Returns whether the associated FloatingActionButton automatically hides when there is
- * not enough space to be displayed.
- *
- * @attr ref android.support.design.R.styleable#FloatingActionButton_Behavior_Layout_behavior_autoHide
- * @return true if enabled
- */
- public boolean isAutoHideEnabled() {
- return mAutoHideEnabled;
- }
-
- @Override
- public void onAttachedToLayoutParams(@NonNull CoordinatorLayout.LayoutParams lp) {
- if (lp.dodgeInsetEdges == Gravity.NO_GRAVITY) {
- // If the developer hasn't set dodgeInsetEdges, lets set it to BOTTOM so that
- // we dodge any Snackbars
- lp.dodgeInsetEdges = Gravity.BOTTOM;
- }
- }
-
- @Override
- public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child,
- View dependency) {
- if (dependency instanceof AppBarLayout) {
- // If we're depending on an AppBarLayout we will show/hide it automatically
- // if the FAB is anchored to the AppBarLayout
- updateFabVisibilityForAppBarLayout(parent, (AppBarLayout) dependency, child);
- } else if (isBottomSheet(dependency)) {
- updateFabVisibilityForBottomSheet(dependency, child);
- }
- return false;
- }
-
- private static boolean isBottomSheet(@NonNull View view) {
- final ViewGroup.LayoutParams lp = view.getLayoutParams();
- if (lp instanceof CoordinatorLayout.LayoutParams) {
- return ((CoordinatorLayout.LayoutParams) lp)
- .getBehavior() instanceof BottomSheetBehavior;
- }
- return false;
- }
-
- @VisibleForTesting
- void setInternalAutoHideListener(OnVisibilityChangedListener listener) {
- mInternalAutoHideListener = listener;
- }
-
- private boolean shouldUpdateVisibility(View dependency, FloatingActionButton child) {
- final CoordinatorLayout.LayoutParams lp =
- (CoordinatorLayout.LayoutParams) child.getLayoutParams();
- if (!mAutoHideEnabled) {
- return false;
- }
-
- if (lp.getAnchorId() != dependency.getId()) {
- // The anchor ID doesn't match the dependency, so we won't automatically
- // show/hide the FAB
- return false;
- }
-
- //noinspection RedundantIfStatement
- if (child.getUserSetVisibility() != VISIBLE) {
- // The view isn't set to be visible so skip changing its visibility
- return false;
- }
-
- return true;
- }
-
- private boolean updateFabVisibilityForAppBarLayout(CoordinatorLayout parent,
- AppBarLayout appBarLayout, FloatingActionButton child) {
- if (!shouldUpdateVisibility(appBarLayout, child)) {
- return false;
- }
-
- if (mTmpRect == null) {
- mTmpRect = new Rect();
- }
-
- // First, let's get the visible rect of the dependency
- final Rect rect = mTmpRect;
- ViewGroupUtils.getDescendantRect(parent, appBarLayout, rect);
-
- if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) {
- // If the anchor's bottom is below the seam, we'll animate our FAB out
- child.hide(mInternalAutoHideListener, false);
- } else {
- // Else, we'll animate our FAB back in
- child.show(mInternalAutoHideListener, false);
- }
- return true;
- }
-
- private boolean updateFabVisibilityForBottomSheet(View bottomSheet,
- FloatingActionButton child) {
- if (!shouldUpdateVisibility(bottomSheet, child)) {
- return false;
- }
- CoordinatorLayout.LayoutParams lp =
- (CoordinatorLayout.LayoutParams) child.getLayoutParams();
- if (bottomSheet.getTop() < child.getHeight() / 2 + lp.topMargin) {
- child.hide(mInternalAutoHideListener, false);
- } else {
- child.show(mInternalAutoHideListener, false);
- }
- return true;
- }
-
- @Override
- public boolean onLayoutChild(CoordinatorLayout parent, FloatingActionButton child,
- int layoutDirection) {
- // First, let's make sure that the visibility of the FAB is consistent
- final List<View> dependencies = parent.getDependencies(child);
- for (int i = 0, count = dependencies.size(); i < count; i++) {
- final View dependency = dependencies.get(i);
- if (dependency instanceof AppBarLayout) {
- if (updateFabVisibilityForAppBarLayout(
- parent, (AppBarLayout) dependency, child)) {
- break;
- }
- } else if (isBottomSheet(dependency)) {
- if (updateFabVisibilityForBottomSheet(dependency, child)) {
- break;
- }
- }
- }
- // Now let the CoordinatorLayout lay out the FAB
- parent.onLayoutChild(child, layoutDirection);
- // Now offset it if needed
- offsetIfNeeded(parent, child);
- return true;
- }
-
- @Override
- public boolean getInsetDodgeRect(@NonNull CoordinatorLayout parent,
- @NonNull FloatingActionButton child, @NonNull Rect rect) {
- // Since we offset so that any internal shadow padding isn't shown, we need to make
- // sure that the shadow isn't used for any dodge inset calculations
- final Rect shadowPadding = child.mShadowPadding;
- rect.set(child.getLeft() + shadowPadding.left,
- child.getTop() + shadowPadding.top,
- child.getRight() - shadowPadding.right,
- child.getBottom() - shadowPadding.bottom);
- return true;
- }
-
- /**
- * Pre-Lollipop we use padding so that the shadow has enough space to be drawn. This method
- * offsets our layout position so that we're positioned correctly if we're on one of
- * our parent's edges.
- */
- private void offsetIfNeeded(CoordinatorLayout parent, FloatingActionButton fab) {
- final Rect padding = fab.mShadowPadding;
-
- if (padding != null && padding.centerX() > 0 && padding.centerY() > 0) {
- final CoordinatorLayout.LayoutParams lp =
- (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
-
- int offsetTB = 0, offsetLR = 0;
-
- if (fab.getRight() >= parent.getWidth() - lp.rightMargin) {
- // If we're on the right edge, shift it the right
- offsetLR = padding.right;
- } else if (fab.getLeft() <= lp.leftMargin) {
- // If we're on the left edge, shift it the left
- offsetLR = -padding.left;
- }
- if (fab.getBottom() >= parent.getHeight() - lp.bottomMargin) {
- // If we're on the bottom edge, shift it down
- offsetTB = padding.bottom;
- } else if (fab.getTop() <= lp.topMargin) {
- // If we're on the top edge, shift it up
- offsetTB = -padding.top;
- }
-
- if (offsetTB != 0) {
- ViewCompat.offsetTopAndBottom(fab, offsetTB);
- }
- if (offsetLR != 0) {
- ViewCompat.offsetLeftAndRight(fab, offsetLR);
- }
- }
- }
- }
-
- /**
- * Returns the backward compatible elevation of the FloatingActionButton.
- *
- * @return the backward compatible elevation in pixels.
- * @attr ref android.support.design.R.styleable#FloatingActionButton_elevation
- * @see #setCompatElevation(float)
- */
- public float getCompatElevation() {
- return getImpl().getElevation();
- }
-
- /**
- * Updates the backward compatible elevation of the FloatingActionButton.
- *
- * @param elevation The backward compatible elevation in pixels.
- * @attr ref android.support.design.R.styleable#FloatingActionButton_elevation
- * @see #getCompatElevation()
- * @see #setUseCompatPadding(boolean)
- */
- public void setCompatElevation(float elevation) {
- getImpl().setElevation(elevation);
- }
-
- private FloatingActionButtonImpl getImpl() {
- if (mImpl == null) {
- mImpl = createImpl();
- }
- return mImpl;
- }
-
- private FloatingActionButtonImpl createImpl() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- return new FloatingActionButtonLollipop(this, new ShadowDelegateImpl());
- } else {
- return new FloatingActionButtonImpl(this, new ShadowDelegateImpl());
- }
- }
-
- private class ShadowDelegateImpl implements ShadowViewDelegate {
- ShadowDelegateImpl() {
- }
-
- @Override
- public float getRadius() {
- return getSizeDimension() / 2f;
- }
-
- @Override
- public void setShadowPadding(int left, int top, int right, int bottom) {
- mShadowPadding.set(left, top, right, bottom);
- setPadding(left + mImagePadding, top + mImagePadding,
- right + mImagePadding, bottom + mImagePadding);
- }
-
- @Override
- public void setBackgroundDrawable(Drawable background) {
- FloatingActionButton.super.setBackgroundDrawable(background);
- }
-
- @Override
- public boolean isCompatPaddingEnabled() {
- return mCompatPadding;
- }
- }
-}
diff --git a/android/support/design/widget/FloatingActionButtonImpl.java b/android/support/design/widget/FloatingActionButtonImpl.java
deleted file mode 100644
index 132cd81b..00000000
--- a/android/support/design/widget/FloatingActionButtonImpl.java
+++ /dev/null
@@ -1,531 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.LayerDrawable;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.support.design.R;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.view.ViewCompat;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.animation.Interpolator;
-
-@RequiresApi(14)
-class FloatingActionButtonImpl {
- static final Interpolator ANIM_INTERPOLATOR = AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR;
- static final long PRESSED_ANIM_DURATION = 100;
- static final long PRESSED_ANIM_DELAY = 100;
-
- static final int ANIM_STATE_NONE = 0;
- static final int ANIM_STATE_HIDING = 1;
- static final int ANIM_STATE_SHOWING = 2;
-
- int mAnimState = ANIM_STATE_NONE;
-
- private final StateListAnimator mStateListAnimator;
-
- ShadowDrawableWrapper mShadowDrawable;
-
- private float mRotation;
-
- Drawable mShapeDrawable;
- Drawable mRippleDrawable;
- CircularBorderDrawable mBorderDrawable;
- Drawable mContentBackground;
-
- float mElevation;
- float mPressedTranslationZ;
-
- interface InternalVisibilityChangedListener {
- void onShown();
- void onHidden();
- }
-
- static final int SHOW_HIDE_ANIM_DURATION = 200;
-
- static final int[] PRESSED_ENABLED_STATE_SET = {android.R.attr.state_pressed,
- android.R.attr.state_enabled};
- static final int[] FOCUSED_ENABLED_STATE_SET = {android.R.attr.state_focused,
- android.R.attr.state_enabled};
- static final int[] ENABLED_STATE_SET = {android.R.attr.state_enabled};
- static final int[] EMPTY_STATE_SET = new int[0];
-
- final VisibilityAwareImageButton mView;
- final ShadowViewDelegate mShadowViewDelegate;
-
- private final Rect mTmpRect = new Rect();
- private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
-
- FloatingActionButtonImpl(VisibilityAwareImageButton view,
- ShadowViewDelegate shadowViewDelegate) {
- mView = view;
- mShadowViewDelegate = shadowViewDelegate;
-
- mStateListAnimator = new StateListAnimator();
-
- // Elevate with translationZ when pressed or focused
- mStateListAnimator.addState(PRESSED_ENABLED_STATE_SET,
- createAnimator(new ElevateToTranslationZAnimation()));
- mStateListAnimator.addState(FOCUSED_ENABLED_STATE_SET,
- createAnimator(new ElevateToTranslationZAnimation()));
- // Reset back to elevation by default
- mStateListAnimator.addState(ENABLED_STATE_SET,
- createAnimator(new ResetElevationAnimation()));
- // Set to 0 when disabled
- mStateListAnimator.addState(EMPTY_STATE_SET,
- createAnimator(new DisabledElevationAnimation()));
-
- mRotation = mView.getRotation();
- }
-
- void setBackgroundDrawable(ColorStateList backgroundTint,
- PorterDuff.Mode backgroundTintMode, int rippleColor, int borderWidth) {
- // Now we need to tint the original background with the tint, using
- // an InsetDrawable if we have a border width
- mShapeDrawable = DrawableCompat.wrap(createShapeDrawable());
- DrawableCompat.setTintList(mShapeDrawable, backgroundTint);
- if (backgroundTintMode != null) {
- DrawableCompat.setTintMode(mShapeDrawable, backgroundTintMode);
- }
-
- // Now we created a mask Drawable which will be used for touch feedback.
- GradientDrawable touchFeedbackShape = createShapeDrawable();
-
- // We'll now wrap that touch feedback mask drawable with a ColorStateList. We do not need
- // to inset for any border here as LayerDrawable will nest the padding for us
- mRippleDrawable = DrawableCompat.wrap(touchFeedbackShape);
- DrawableCompat.setTintList(mRippleDrawable, createColorStateList(rippleColor));
-
- final Drawable[] layers;
- if (borderWidth > 0) {
- mBorderDrawable = createBorderDrawable(borderWidth, backgroundTint);
- layers = new Drawable[] {mBorderDrawable, mShapeDrawable, mRippleDrawable};
- } else {
- mBorderDrawable = null;
- layers = new Drawable[] {mShapeDrawable, mRippleDrawable};
- }
-
- mContentBackground = new LayerDrawable(layers);
-
- mShadowDrawable = new ShadowDrawableWrapper(
- mView.getContext(),
- mContentBackground,
- mShadowViewDelegate.getRadius(),
- mElevation,
- mElevation + mPressedTranslationZ);
- mShadowDrawable.setAddPaddingForCorners(false);
- mShadowViewDelegate.setBackgroundDrawable(mShadowDrawable);
- }
-
- void setBackgroundTintList(ColorStateList tint) {
- if (mShapeDrawable != null) {
- DrawableCompat.setTintList(mShapeDrawable, tint);
- }
- if (mBorderDrawable != null) {
- mBorderDrawable.setBorderTint(tint);
- }
- }
-
- void setBackgroundTintMode(PorterDuff.Mode tintMode) {
- if (mShapeDrawable != null) {
- DrawableCompat.setTintMode(mShapeDrawable, tintMode);
- }
- }
-
-
- void setRippleColor(int rippleColor) {
- if (mRippleDrawable != null) {
- DrawableCompat.setTintList(mRippleDrawable, createColorStateList(rippleColor));
- }
- }
-
- final void setElevation(float elevation) {
- if (mElevation != elevation) {
- mElevation = elevation;
- onElevationsChanged(elevation, mPressedTranslationZ);
- }
- }
-
- float getElevation() {
- return mElevation;
- }
-
- final void setPressedTranslationZ(float translationZ) {
- if (mPressedTranslationZ != translationZ) {
- mPressedTranslationZ = translationZ;
- onElevationsChanged(mElevation, translationZ);
- }
- }
-
- void onElevationsChanged(float elevation, float pressedTranslationZ) {
- if (mShadowDrawable != null) {
- mShadowDrawable.setShadowSize(elevation, elevation + mPressedTranslationZ);
- updatePadding();
- }
- }
-
- void onDrawableStateChanged(int[] state) {
- mStateListAnimator.setState(state);
- }
-
- void jumpDrawableToCurrentState() {
- mStateListAnimator.jumpToCurrentState();
- }
-
- void hide(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
- if (isOrWillBeHidden()) {
- // We either are or will soon be hidden, skip the call
- return;
- }
-
- mView.animate().cancel();
-
- if (shouldAnimateVisibilityChange()) {
- mAnimState = ANIM_STATE_HIDING;
-
- mView.animate()
- .scaleX(0f)
- .scaleY(0f)
- .alpha(0f)
- .setDuration(SHOW_HIDE_ANIM_DURATION)
- .setInterpolator(AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR)
- .setListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationStart(Animator animation) {
- mView.internalSetVisibility(View.VISIBLE, fromUser);
- mCancelled = false;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimState = ANIM_STATE_NONE;
-
- if (!mCancelled) {
- mView.internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE,
- fromUser);
- if (listener != null) {
- listener.onHidden();
- }
- }
- }
- });
- } else {
- // If the view isn't laid out, or we're in the editor, don't run the animation
- mView.internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE, fromUser);
- if (listener != null) {
- listener.onHidden();
- }
- }
- }
-
- void show(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
- if (isOrWillBeShown()) {
- // We either are or will soon be visible, skip the call
- return;
- }
-
- mView.animate().cancel();
-
- if (shouldAnimateVisibilityChange()) {
- mAnimState = ANIM_STATE_SHOWING;
-
- if (mView.getVisibility() != View.VISIBLE) {
- // If the view isn't visible currently, we'll animate it from a single pixel
- mView.setAlpha(0f);
- mView.setScaleY(0f);
- mView.setScaleX(0f);
- }
-
- mView.animate()
- .scaleX(1f)
- .scaleY(1f)
- .alpha(1f)
- .setDuration(SHOW_HIDE_ANIM_DURATION)
- .setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mView.internalSetVisibility(View.VISIBLE, fromUser);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimState = ANIM_STATE_NONE;
- if (listener != null) {
- listener.onShown();
- }
- }
- });
- } else {
- mView.internalSetVisibility(View.VISIBLE, fromUser);
- mView.setAlpha(1f);
- mView.setScaleY(1f);
- mView.setScaleX(1f);
- if (listener != null) {
- listener.onShown();
- }
- }
- }
-
- final Drawable getContentBackground() {
- return mContentBackground;
- }
-
- void onCompatShadowChanged() {
- // Ignore pre-v21
- }
-
- final void updatePadding() {
- Rect rect = mTmpRect;
- getPadding(rect);
- onPaddingUpdated(rect);
- mShadowViewDelegate.setShadowPadding(rect.left, rect.top, rect.right, rect.bottom);
- }
-
- void getPadding(Rect rect) {
- mShadowDrawable.getPadding(rect);
- }
-
- void onPaddingUpdated(Rect padding) {}
-
- void onAttachedToWindow() {
- if (requirePreDrawListener()) {
- ensurePreDrawListener();
- mView.getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
- }
- }
-
- void onDetachedFromWindow() {
- if (mPreDrawListener != null) {
- mView.getViewTreeObserver().removeOnPreDrawListener(mPreDrawListener);
- mPreDrawListener = null;
- }
- }
-
- boolean requirePreDrawListener() {
- return true;
- }
-
- CircularBorderDrawable createBorderDrawable(int borderWidth, ColorStateList backgroundTint) {
- final Context context = mView.getContext();
- CircularBorderDrawable borderDrawable = newCircularDrawable();
- borderDrawable.setGradientColors(
- ContextCompat.getColor(context, R.color.design_fab_stroke_top_outer_color),
- ContextCompat.getColor(context, R.color.design_fab_stroke_top_inner_color),
- ContextCompat.getColor(context, R.color.design_fab_stroke_end_inner_color),
- ContextCompat.getColor(context, R.color.design_fab_stroke_end_outer_color));
- borderDrawable.setBorderWidth(borderWidth);
- borderDrawable.setBorderTint(backgroundTint);
- return borderDrawable;
- }
-
- CircularBorderDrawable newCircularDrawable() {
- return new CircularBorderDrawable();
- }
-
- void onPreDraw() {
- final float rotation = mView.getRotation();
- if (mRotation != rotation) {
- mRotation = rotation;
- updateFromViewRotation();
- }
- }
-
- private void ensurePreDrawListener() {
- if (mPreDrawListener == null) {
- mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- FloatingActionButtonImpl.this.onPreDraw();
- return true;
- }
- };
- }
- }
-
- GradientDrawable createShapeDrawable() {
- GradientDrawable d = newGradientDrawableForShape();
- d.setShape(GradientDrawable.OVAL);
- d.setColor(Color.WHITE);
- return d;
- }
-
- GradientDrawable newGradientDrawableForShape() {
- return new GradientDrawable();
- }
-
- boolean isOrWillBeShown() {
- if (mView.getVisibility() != View.VISIBLE) {
- // If we not currently visible, return true if we're animating to be shown
- return mAnimState == ANIM_STATE_SHOWING;
- } else {
- // Otherwise if we're visible, return true if we're not animating to be hidden
- return mAnimState != ANIM_STATE_HIDING;
- }
- }
-
- boolean isOrWillBeHidden() {
- if (mView.getVisibility() == View.VISIBLE) {
- // If we currently visible, return true if we're animating to be hidden
- return mAnimState == ANIM_STATE_HIDING;
- } else {
- // Otherwise if we're not visible, return true if we're not animating to be shown
- return mAnimState != ANIM_STATE_SHOWING;
- }
- }
-
- private ValueAnimator createAnimator(@NonNull ShadowAnimatorImpl impl) {
- final ValueAnimator animator = new ValueAnimator();
- animator.setInterpolator(ANIM_INTERPOLATOR);
- animator.setDuration(PRESSED_ANIM_DURATION);
- animator.addListener(impl);
- animator.addUpdateListener(impl);
- animator.setFloatValues(0, 1);
- return animator;
- }
-
- private abstract class ShadowAnimatorImpl extends AnimatorListenerAdapter
- implements ValueAnimator.AnimatorUpdateListener {
- private boolean mValidValues;
- private float mShadowSizeStart;
- private float mShadowSizeEnd;
-
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- if (!mValidValues) {
- mShadowSizeStart = mShadowDrawable.getShadowSize();
- mShadowSizeEnd = getTargetShadowSize();
- mValidValues = true;
- }
-
- mShadowDrawable.setShadowSize(mShadowSizeStart
- + ((mShadowSizeEnd - mShadowSizeStart) * animator.getAnimatedFraction()));
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- mShadowDrawable.setShadowSize(mShadowSizeEnd);
- mValidValues = false;
- }
-
- /**
- * @return the shadow size we want to animate to.
- */
- protected abstract float getTargetShadowSize();
- }
-
- private class ResetElevationAnimation extends ShadowAnimatorImpl {
- ResetElevationAnimation() {
- }
-
- @Override
- protected float getTargetShadowSize() {
- return mElevation;
- }
- }
-
- private class ElevateToTranslationZAnimation extends ShadowAnimatorImpl {
- ElevateToTranslationZAnimation() {
- }
-
- @Override
- protected float getTargetShadowSize() {
- return mElevation + mPressedTranslationZ;
- }
- }
-
- private class DisabledElevationAnimation extends ShadowAnimatorImpl {
- DisabledElevationAnimation() {
- }
-
- @Override
- protected float getTargetShadowSize() {
- return 0f;
- }
- }
-
- private static ColorStateList createColorStateList(int selectedColor) {
- final int[][] states = new int[3][];
- final int[] colors = new int[3];
- int i = 0;
-
- states[i] = FOCUSED_ENABLED_STATE_SET;
- colors[i] = selectedColor;
- i++;
-
- states[i] = PRESSED_ENABLED_STATE_SET;
- colors[i] = selectedColor;
- i++;
-
- // Default enabled state
- states[i] = new int[0];
- colors[i] = Color.TRANSPARENT;
- i++;
-
- return new ColorStateList(states, colors);
- }
-
- private boolean shouldAnimateVisibilityChange() {
- return ViewCompat.isLaidOut(mView) && !mView.isInEditMode();
- }
-
- private void updateFromViewRotation() {
- if (Build.VERSION.SDK_INT == 19) {
- // KitKat seems to have an issue with views which are rotated with angles which are
- // not divisible by 90. Worked around by moving to software rendering in these cases.
- if ((mRotation % 90) != 0) {
- if (mView.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
- mView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- }
- } else {
- if (mView.getLayerType() != View.LAYER_TYPE_NONE) {
- mView.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- }
- }
-
- // Offset any View rotation
- if (mShadowDrawable != null) {
- mShadowDrawable.setRotation(-mRotation);
- }
- if (mBorderDrawable != null) {
- mBorderDrawable.setRotation(-mRotation);
- }
- }
-}
diff --git a/android/support/design/widget/FloatingActionButtonLollipop.java b/android/support/design/widget/FloatingActionButtonLollipop.java
deleted file mode 100644
index 0df83da5..00000000
--- a/android/support/design/widget/FloatingActionButtonLollipop.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.StateListAnimator;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.InsetDrawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.RippleDrawable;
-import android.os.Build;
-import android.support.annotation.RequiresApi;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.view.View;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RequiresApi(21)
-class FloatingActionButtonLollipop extends FloatingActionButtonImpl {
-
- private InsetDrawable mInsetDrawable;
-
- FloatingActionButtonLollipop(VisibilityAwareImageButton view,
- ShadowViewDelegate shadowViewDelegate) {
- super(view, shadowViewDelegate);
- }
-
- @Override
- void setBackgroundDrawable(ColorStateList backgroundTint,
- PorterDuff.Mode backgroundTintMode, int rippleColor, int borderWidth) {
- // Now we need to tint the shape background with the tint
- mShapeDrawable = DrawableCompat.wrap(createShapeDrawable());
- DrawableCompat.setTintList(mShapeDrawable, backgroundTint);
- if (backgroundTintMode != null) {
- DrawableCompat.setTintMode(mShapeDrawable, backgroundTintMode);
- }
-
- final Drawable rippleContent;
- if (borderWidth > 0) {
- mBorderDrawable = createBorderDrawable(borderWidth, backgroundTint);
- rippleContent = new LayerDrawable(new Drawable[]{mBorderDrawable, mShapeDrawable});
- } else {
- mBorderDrawable = null;
- rippleContent = mShapeDrawable;
- }
-
- mRippleDrawable = new RippleDrawable(ColorStateList.valueOf(rippleColor),
- rippleContent, null);
-
- mContentBackground = mRippleDrawable;
-
- mShadowViewDelegate.setBackgroundDrawable(mRippleDrawable);
- }
-
- @Override
- void setRippleColor(int rippleColor) {
- if (mRippleDrawable instanceof RippleDrawable) {
- ((RippleDrawable) mRippleDrawable).setColor(ColorStateList.valueOf(rippleColor));
- } else {
- super.setRippleColor(rippleColor);
- }
- }
-
- @Override
- void onElevationsChanged(final float elevation, final float pressedTranslationZ) {
- if (Build.VERSION.SDK_INT == 21) {
- // Animations produce NPE in version 21. Bluntly set the values instead (matching the
- // logic in the animations below).
- if (mView.isEnabled()) {
- mView.setElevation(elevation);
- if (mView.isFocused() || mView.isPressed()) {
- mView.setTranslationZ(pressedTranslationZ);
- } else {
- mView.setTranslationZ(0);
- }
- } else {
- mView.setElevation(0);
- mView.setTranslationZ(0);
- }
- } else {
- final StateListAnimator stateListAnimator = new StateListAnimator();
-
- // Animate elevation and translationZ to our values when pressed
- AnimatorSet set = new AnimatorSet();
- set.play(ObjectAnimator.ofFloat(mView, "elevation", elevation).setDuration(0))
- .with(ObjectAnimator.ofFloat(mView, View.TRANSLATION_Z, pressedTranslationZ)
- .setDuration(PRESSED_ANIM_DURATION));
- set.setInterpolator(ANIM_INTERPOLATOR);
- stateListAnimator.addState(PRESSED_ENABLED_STATE_SET, set);
-
- // Same deal for when we're focused
- set = new AnimatorSet();
- set.play(ObjectAnimator.ofFloat(mView, "elevation", elevation).setDuration(0))
- .with(ObjectAnimator.ofFloat(mView, View.TRANSLATION_Z, pressedTranslationZ)
- .setDuration(PRESSED_ANIM_DURATION));
- set.setInterpolator(ANIM_INTERPOLATOR);
- stateListAnimator.addState(FOCUSED_ENABLED_STATE_SET, set);
-
- // Animate translationZ to 0 if not pressed
- set = new AnimatorSet();
- List<Animator> animators = new ArrayList<>();
- animators.add(ObjectAnimator.ofFloat(mView, "elevation", elevation).setDuration(0));
- if (Build.VERSION.SDK_INT >= 22 && Build.VERSION.SDK_INT <= 24) {
- // This is a no-op animation which exists here only for introducing the duration
- // because setting the delay (on the next animation) via "setDelay" or "after"
- // can trigger a NPE between android versions 22 and 24 (due to a framework
- // bug). The issue has been fixed in version 25.
- animators.add(ObjectAnimator.ofFloat(mView, View.TRANSLATION_Z,
- mView.getTranslationZ()).setDuration(PRESSED_ANIM_DELAY));
- }
- animators.add(ObjectAnimator.ofFloat(mView, View.TRANSLATION_Z, 0f)
- .setDuration(PRESSED_ANIM_DURATION));
- set.playSequentially(animators.toArray(new ObjectAnimator[0]));
- set.setInterpolator(ANIM_INTERPOLATOR);
- stateListAnimator.addState(ENABLED_STATE_SET, set);
-
- // Animate everything to 0 when disabled
- set = new AnimatorSet();
- set.play(ObjectAnimator.ofFloat(mView, "elevation", 0f).setDuration(0))
- .with(ObjectAnimator.ofFloat(mView, View.TRANSLATION_Z, 0f).setDuration(0));
- set.setInterpolator(ANIM_INTERPOLATOR);
- stateListAnimator.addState(EMPTY_STATE_SET, set);
-
- mView.setStateListAnimator(stateListAnimator);
- }
-
- if (mShadowViewDelegate.isCompatPaddingEnabled()) {
- updatePadding();
- }
- }
-
- @Override
- public float getElevation() {
- return mView.getElevation();
- }
-
- @Override
- void onCompatShadowChanged() {
- updatePadding();
- }
-
- @Override
- void onPaddingUpdated(Rect padding) {
- if (mShadowViewDelegate.isCompatPaddingEnabled()) {
- mInsetDrawable = new InsetDrawable(mRippleDrawable,
- padding.left, padding.top, padding.right, padding.bottom);
- mShadowViewDelegate.setBackgroundDrawable(mInsetDrawable);
- } else {
- mShadowViewDelegate.setBackgroundDrawable(mRippleDrawable);
- }
- }
-
- @Override
- void onDrawableStateChanged(int[] state) {
- // no-op
- }
-
- @Override
- void jumpDrawableToCurrentState() {
- // no-op
- }
-
- @Override
- boolean requirePreDrawListener() {
- return false;
- }
-
- @Override
- CircularBorderDrawable newCircularDrawable() {
- return new CircularBorderDrawableLollipop();
- }
-
- @Override
- GradientDrawable newGradientDrawableForShape() {
- return new AlwaysStatefulGradientDrawable();
- }
-
- @Override
- void getPadding(Rect rect) {
- if (mShadowViewDelegate.isCompatPaddingEnabled()) {
- final float radius = mShadowViewDelegate.getRadius();
- final float maxShadowSize = getElevation() + mPressedTranslationZ;
- final int hPadding = (int) Math.ceil(
- ShadowDrawableWrapper.calculateHorizontalPadding(maxShadowSize, radius, false));
- final int vPadding = (int) Math.ceil(
- ShadowDrawableWrapper.calculateVerticalPadding(maxShadowSize, radius, false));
- rect.set(hPadding, vPadding, hPadding, vPadding);
- } else {
- rect.set(0, 0, 0, 0);
- }
- }
-
- /**
- * LayerDrawable on L+ caches its isStateful() state and doesn't refresh it,
- * meaning that if we apply a tint to one of its children, the parent doesn't become
- * stateful and the tint doesn't work for state changes. We workaround it by saying that we
- * are always stateful. If we don't have a stateful tint, the change is ignored anyway.
- */
- static class AlwaysStatefulGradientDrawable extends GradientDrawable {
- @Override
- public boolean isStateful() {
- return true;
- }
- }
-}
diff --git a/android/support/design/widget/HeaderBehavior.java b/android/support/design/widget/HeaderBehavior.java
deleted file mode 100644
index a5d0edf6..00000000
--- a/android/support/design/widget/HeaderBehavior.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.content.Context;
-import android.support.design.widget.CoordinatorLayout.Behavior;
-import android.support.v4.math.MathUtils;
-import android.support.v4.view.ViewCompat;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.widget.OverScroller;
-
-/**
- * The {@link Behavior} for a view that sits vertically above scrolling a view.
- * See {@link HeaderScrollingViewBehavior}.
- */
-abstract class HeaderBehavior<V extends View> extends ViewOffsetBehavior<V> {
-
- private static final int INVALID_POINTER = -1;
-
- private Runnable mFlingRunnable;
- OverScroller mScroller;
-
- private boolean mIsBeingDragged;
- private int mActivePointerId = INVALID_POINTER;
- private int mLastMotionY;
- private int mTouchSlop = -1;
- private VelocityTracker mVelocityTracker;
-
- public HeaderBehavior() {}
-
- public HeaderBehavior(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
- if (mTouchSlop < 0) {
- mTouchSlop = ViewConfiguration.get(parent.getContext()).getScaledTouchSlop();
- }
-
- final int action = ev.getAction();
-
- // Shortcut since we're being dragged
- if (action == MotionEvent.ACTION_MOVE && mIsBeingDragged) {
- return true;
- }
-
- switch (ev.getActionMasked()) {
- case MotionEvent.ACTION_DOWN: {
- mIsBeingDragged = false;
- final int x = (int) ev.getX();
- final int y = (int) ev.getY();
- if (canDragView(child) && parent.isPointInChildBounds(child, x, y)) {
- mLastMotionY = y;
- mActivePointerId = ev.getPointerId(0);
- ensureVelocityTracker();
- }
- break;
- }
-
- case MotionEvent.ACTION_MOVE: {
- final int activePointerId = mActivePointerId;
- if (activePointerId == INVALID_POINTER) {
- // If we don't have a valid id, the touch down wasn't on content.
- break;
- }
- final int pointerIndex = ev.findPointerIndex(activePointerId);
- if (pointerIndex == -1) {
- break;
- }
-
- final int y = (int) ev.getY(pointerIndex);
- final int yDiff = Math.abs(y - mLastMotionY);
- if (yDiff > mTouchSlop) {
- mIsBeingDragged = true;
- mLastMotionY = y;
- }
- break;
- }
-
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP: {
- mIsBeingDragged = false;
- mActivePointerId = INVALID_POINTER;
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- break;
- }
- }
-
- if (mVelocityTracker != null) {
- mVelocityTracker.addMovement(ev);
- }
-
- return mIsBeingDragged;
- }
-
- @Override
- public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
- if (mTouchSlop < 0) {
- mTouchSlop = ViewConfiguration.get(parent.getContext()).getScaledTouchSlop();
- }
-
- switch (ev.getActionMasked()) {
- case MotionEvent.ACTION_DOWN: {
- final int x = (int) ev.getX();
- final int y = (int) ev.getY();
-
- if (parent.isPointInChildBounds(child, x, y) && canDragView(child)) {
- mLastMotionY = y;
- mActivePointerId = ev.getPointerId(0);
- ensureVelocityTracker();
- } else {
- return false;
- }
- break;
- }
-
- case MotionEvent.ACTION_MOVE: {
- final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
- if (activePointerIndex == -1) {
- return false;
- }
-
- final int y = (int) ev.getY(activePointerIndex);
- int dy = mLastMotionY - y;
-
- if (!mIsBeingDragged && Math.abs(dy) > mTouchSlop) {
- mIsBeingDragged = true;
- if (dy > 0) {
- dy -= mTouchSlop;
- } else {
- dy += mTouchSlop;
- }
- }
-
- if (mIsBeingDragged) {
- mLastMotionY = y;
- // We're being dragged so scroll the ABL
- scroll(parent, child, dy, getMaxDragOffset(child), 0);
- }
- break;
- }
-
- case MotionEvent.ACTION_UP:
- if (mVelocityTracker != null) {
- mVelocityTracker.addMovement(ev);
- mVelocityTracker.computeCurrentVelocity(1000);
- float yvel = mVelocityTracker.getYVelocity(mActivePointerId);
- fling(parent, child, -getScrollRangeForDragFling(child), 0, yvel);
- }
- // $FALLTHROUGH
- case MotionEvent.ACTION_CANCEL: {
- mIsBeingDragged = false;
- mActivePointerId = INVALID_POINTER;
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- break;
- }
- }
-
- if (mVelocityTracker != null) {
- mVelocityTracker.addMovement(ev);
- }
-
- return true;
- }
-
- int setHeaderTopBottomOffset(CoordinatorLayout parent, V header, int newOffset) {
- return setHeaderTopBottomOffset(parent, header, newOffset,
- Integer.MIN_VALUE, Integer.MAX_VALUE);
- }
-
- int setHeaderTopBottomOffset(CoordinatorLayout parent, V header, int newOffset,
- int minOffset, int maxOffset) {
- final int curOffset = getTopAndBottomOffset();
- int consumed = 0;
-
- if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
- // If we have some scrolling range, and we're currently within the min and max
- // offsets, calculate a new offset
- newOffset = MathUtils.clamp(newOffset, minOffset, maxOffset);
-
- if (curOffset != newOffset) {
- setTopAndBottomOffset(newOffset);
- // Update how much dy we have consumed
- consumed = curOffset - newOffset;
- }
- }
-
- return consumed;
- }
-
- int getTopBottomOffsetForScrollingSibling() {
- return getTopAndBottomOffset();
- }
-
- final int scroll(CoordinatorLayout coordinatorLayout, V header,
- int dy, int minOffset, int maxOffset) {
- return setHeaderTopBottomOffset(coordinatorLayout, header,
- getTopBottomOffsetForScrollingSibling() - dy, minOffset, maxOffset);
- }
-
- final boolean fling(CoordinatorLayout coordinatorLayout, V layout, int minOffset,
- int maxOffset, float velocityY) {
- if (mFlingRunnable != null) {
- layout.removeCallbacks(mFlingRunnable);
- mFlingRunnable = null;
- }
-
- if (mScroller == null) {
- mScroller = new OverScroller(layout.getContext());
- }
-
- mScroller.fling(
- 0, getTopAndBottomOffset(), // curr
- 0, Math.round(velocityY), // velocity.
- 0, 0, // x
- minOffset, maxOffset); // y
-
- if (mScroller.computeScrollOffset()) {
- mFlingRunnable = new FlingRunnable(coordinatorLayout, layout);
- ViewCompat.postOnAnimation(layout, mFlingRunnable);
- return true;
- } else {
- onFlingFinished(coordinatorLayout, layout);
- return false;
- }
- }
-
- /**
- * Called when a fling has finished, or the fling was initiated but there wasn't enough
- * velocity to start it.
- */
- void onFlingFinished(CoordinatorLayout parent, V layout) {
- // no-op
- }
-
- /**
- * Return true if the view can be dragged.
- */
- boolean canDragView(V view) {
- return false;
- }
-
- /**
- * Returns the maximum px offset when {@code view} is being dragged.
- */
- int getMaxDragOffset(V view) {
- return -view.getHeight();
- }
-
- int getScrollRangeForDragFling(V view) {
- return view.getHeight();
- }
-
- private void ensureVelocityTracker() {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- }
-
- private class FlingRunnable implements Runnable {
- private final CoordinatorLayout mParent;
- private final V mLayout;
-
- FlingRunnable(CoordinatorLayout parent, V layout) {
- mParent = parent;
- mLayout = layout;
- }
-
- @Override
- public void run() {
- if (mLayout != null && mScroller != null) {
- if (mScroller.computeScrollOffset()) {
- setHeaderTopBottomOffset(mParent, mLayout, mScroller.getCurrY());
- // Post ourselves so that we run on the next animation
- ViewCompat.postOnAnimation(mLayout, this);
- } else {
- onFlingFinished(mParent, mLayout);
- }
- }
- }
- }
-}
diff --git a/android/support/design/widget/HeaderScrollingViewBehavior.java b/android/support/design/widget/HeaderScrollingViewBehavior.java
deleted file mode 100644
index 81ddde54..00000000
--- a/android/support/design/widget/HeaderScrollingViewBehavior.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.support.design.widget.CoordinatorLayout.Behavior;
-import android.support.v4.math.MathUtils;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.WindowInsetsCompat;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.List;
-
-/**
- * The {@link Behavior} for a scrolling view that is positioned vertically below another view.
- * See {@link HeaderBehavior}.
- */
-abstract class HeaderScrollingViewBehavior extends ViewOffsetBehavior<View> {
-
- final Rect mTempRect1 = new Rect();
- final Rect mTempRect2 = new Rect();
-
- private int mVerticalLayoutGap = 0;
- private int mOverlayTop;
-
- public HeaderScrollingViewBehavior() {}
-
- public HeaderScrollingViewBehavior(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public boolean onMeasureChild(CoordinatorLayout parent, View child,
- int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec,
- int heightUsed) {
- final int childLpHeight = child.getLayoutParams().height;
- if (childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT
- || childLpHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
- // If the menu's height is set to match_parent/wrap_content then measure it
- // with the maximum visible height
-
- final List<View> dependencies = parent.getDependencies(child);
- final View header = findFirstDependency(dependencies);
- if (header != null) {
- if (ViewCompat.getFitsSystemWindows(header)
- && !ViewCompat.getFitsSystemWindows(child)) {
- // If the header is fitting system windows then we need to also,
- // otherwise we'll get CoL's compatible measuring
- ViewCompat.setFitsSystemWindows(child, true);
-
- if (ViewCompat.getFitsSystemWindows(child)) {
- // If the set succeeded, trigger a new layout and return true
- child.requestLayout();
- return true;
- }
- }
-
- int availableHeight = View.MeasureSpec.getSize(parentHeightMeasureSpec);
- if (availableHeight == 0) {
- // If the measure spec doesn't specify a size, use the current height
- availableHeight = parent.getHeight();
- }
-
- final int height = availableHeight - header.getMeasuredHeight()
- + getScrollRange(header);
- final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height,
- childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT
- ? View.MeasureSpec.EXACTLY
- : View.MeasureSpec.AT_MOST);
-
- // Now measure the scrolling view with the correct height
- parent.onMeasureChild(child, parentWidthMeasureSpec,
- widthUsed, heightMeasureSpec, heightUsed);
-
- return true;
- }
- }
- return false;
- }
-
- @Override
- protected void layoutChild(final CoordinatorLayout parent, final View child,
- final int layoutDirection) {
- final List<View> dependencies = parent.getDependencies(child);
- final View header = findFirstDependency(dependencies);
-
- if (header != null) {
- final CoordinatorLayout.LayoutParams lp =
- (CoordinatorLayout.LayoutParams) child.getLayoutParams();
- final Rect available = mTempRect1;
- available.set(parent.getPaddingLeft() + lp.leftMargin,
- header.getBottom() + lp.topMargin,
- parent.getWidth() - parent.getPaddingRight() - lp.rightMargin,
- parent.getHeight() + header.getBottom()
- - parent.getPaddingBottom() - lp.bottomMargin);
-
- final WindowInsetsCompat parentInsets = parent.getLastWindowInsets();
- if (parentInsets != null && ViewCompat.getFitsSystemWindows(parent)
- && !ViewCompat.getFitsSystemWindows(child)) {
- // If we're set to handle insets but this child isn't, then it has been measured as
- // if there are no insets. We need to lay it out to match horizontally.
- // Top and bottom and already handled in the logic above
- available.left += parentInsets.getSystemWindowInsetLeft();
- available.right -= parentInsets.getSystemWindowInsetRight();
- }
-
- final Rect out = mTempRect2;
- GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(),
- child.getMeasuredHeight(), available, out, layoutDirection);
-
- final int overlap = getOverlapPixelsForOffset(header);
-
- child.layout(out.left, out.top - overlap, out.right, out.bottom - overlap);
- mVerticalLayoutGap = out.top - header.getBottom();
- } else {
- // If we don't have a dependency, let super handle it
- super.layoutChild(parent, child, layoutDirection);
- mVerticalLayoutGap = 0;
- }
- }
-
- float getOverlapRatioForOffset(final View header) {
- return 1f;
- }
-
- final int getOverlapPixelsForOffset(final View header) {
- return mOverlayTop == 0 ? 0 : MathUtils.clamp(
- (int) (getOverlapRatioForOffset(header) * mOverlayTop), 0, mOverlayTop);
- }
-
- private static int resolveGravity(int gravity) {
- return gravity == Gravity.NO_GRAVITY ? GravityCompat.START | Gravity.TOP : gravity;
- }
-
- abstract View findFirstDependency(List<View> views);
-
- int getScrollRange(View v) {
- return v.getMeasuredHeight();
- }
-
- /**
- * The gap between the top of the scrolling view and the bottom of the header layout in pixels.
- */
- final int getVerticalLayoutGap() {
- return mVerticalLayoutGap;
- }
-
- /**
- * Set the distance that this view should overlap any {@link AppBarLayout}.
- *
- * @param overlayTop the distance in px
- */
- public final void setOverlayTop(int overlayTop) {
- mOverlayTop = overlayTop;
- }
-
- /**
- * Returns the distance that this view should overlap any {@link AppBarLayout}.
- */
- public final int getOverlayTop() {
- return mOverlayTop;
- }
-} \ No newline at end of file
diff --git a/android/support/design/widget/NavigationView.java b/android/support/design/widget/NavigationView.java
deleted file mode 100644
index 8fc8c76b..00000000
--- a/android/support/design/widget/NavigationView.java
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
- * 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.design.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.IdRes;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StyleRes;
-import android.support.design.R;
-import android.support.design.internal.NavigationMenu;
-import android.support.design.internal.NavigationMenuPresenter;
-import android.support.design.internal.ScrimInsetsFrameLayout;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.view.AbsSavedState;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.WindowInsetsCompat;
-import android.support.v7.content.res.AppCompatResources;
-import android.support.v7.view.SupportMenuInflater;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuItemImpl;
-import android.support.v7.widget.TintTypedArray;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-
-/**
- * Represents a standard navigation menu for application. The menu contents can be populated
- * by a menu resource file.
- * <p>NavigationView is typically placed inside a {@link android.support.v4.widget.DrawerLayout}.
- * </p>
- * <pre>
- * &lt;android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
- * xmlns:app="http://schemas.android.com/apk/res-auto"
- * android:id="@+id/drawer_layout"
- * android:layout_width="match_parent"
- * android:layout_height="match_parent"
- * android:fitsSystemWindows="true"&gt;
- *
- * &lt;!-- Your contents --&gt;
- *
- * &lt;android.support.design.widget.NavigationView
- * android:id="@+id/navigation"
- * android:layout_width="wrap_content"
- * android:layout_height="match_parent"
- * android:layout_gravity="start"
- * app:menu="@menu/my_navigation_items" /&gt;
- * &lt;/android.support.v4.widget.DrawerLayout&gt;
- * </pre>
- */
-public class NavigationView extends ScrimInsetsFrameLayout {
-
- private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
- private static final int[] DISABLED_STATE_SET = {-android.R.attr.state_enabled};
-
- private static final int PRESENTER_NAVIGATION_VIEW_ID = 1;
-
- private final NavigationMenu mMenu;
- private final NavigationMenuPresenter mPresenter = new NavigationMenuPresenter();
-
- OnNavigationItemSelectedListener mListener;
- private int mMaxWidth;
-
- private MenuInflater mMenuInflater;
-
- public NavigationView(Context context) {
- this(context, null);
- }
-
- public NavigationView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public NavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- ThemeUtils.checkAppCompatTheme(context);
-
- // Create the menu
- mMenu = new NavigationMenu(context);
-
- // Custom attributes
- TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
- R.styleable.NavigationView, defStyleAttr,
- R.style.Widget_Design_NavigationView);
-
- ViewCompat.setBackground(
- this, a.getDrawable(R.styleable.NavigationView_android_background));
- if (a.hasValue(R.styleable.NavigationView_elevation)) {
- ViewCompat.setElevation(this, a.getDimensionPixelSize(
- R.styleable.NavigationView_elevation, 0));
- }
- ViewCompat.setFitsSystemWindows(this,
- a.getBoolean(R.styleable.NavigationView_android_fitsSystemWindows, false));
-
- mMaxWidth = a.getDimensionPixelSize(R.styleable.NavigationView_android_maxWidth, 0);
-
- final ColorStateList itemIconTint;
- if (a.hasValue(R.styleable.NavigationView_itemIconTint)) {
- itemIconTint = a.getColorStateList(R.styleable.NavigationView_itemIconTint);
- } else {
- itemIconTint = createDefaultColorStateList(android.R.attr.textColorSecondary);
- }
-
- boolean textAppearanceSet = false;
- int textAppearance = 0;
- if (a.hasValue(R.styleable.NavigationView_itemTextAppearance)) {
- textAppearance = a.getResourceId(R.styleable.NavigationView_itemTextAppearance, 0);
- textAppearanceSet = true;
- }
-
- ColorStateList itemTextColor = null;
- if (a.hasValue(R.styleable.NavigationView_itemTextColor)) {
- itemTextColor = a.getColorStateList(R.styleable.NavigationView_itemTextColor);
- }
-
- if (!textAppearanceSet && itemTextColor == null) {
- // If there isn't a text appearance set, we'll use a default text color
- itemTextColor = createDefaultColorStateList(android.R.attr.textColorPrimary);
- }
-
- final Drawable itemBackground = a.getDrawable(R.styleable.NavigationView_itemBackground);
-
- mMenu.setCallback(new MenuBuilder.Callback() {
- @Override
- public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
- return mListener != null && mListener.onNavigationItemSelected(item);
- }
-
- @Override
- public void onMenuModeChange(MenuBuilder menu) {}
- });
- mPresenter.setId(PRESENTER_NAVIGATION_VIEW_ID);
- mPresenter.initForMenu(context, mMenu);
- mPresenter.setItemIconTintList(itemIconTint);
- if (textAppearanceSet) {
- mPresenter.setItemTextAppearance(textAppearance);
- }
- mPresenter.setItemTextColor(itemTextColor);
- mPresenter.setItemBackground(itemBackground);
- mMenu.addMenuPresenter(mPresenter);
- addView((View) mPresenter.getMenuView(this));
-
- if (a.hasValue(R.styleable.NavigationView_menu)) {
- inflateMenu(a.getResourceId(R.styleable.NavigationView_menu, 0));
- }
-
- if (a.hasValue(R.styleable.NavigationView_headerLayout)) {
- inflateHeaderView(a.getResourceId(R.styleable.NavigationView_headerLayout, 0));
- }
-
- a.recycle();
- }
-
- @Override
- protected Parcelable onSaveInstanceState() {
- Parcelable superState = super.onSaveInstanceState();
- SavedState state = new SavedState(superState);
- state.menuState = new Bundle();
- mMenu.savePresenterStates(state.menuState);
- return state;
- }
-
- @Override
- protected void onRestoreInstanceState(Parcelable savedState) {
- if (!(savedState instanceof SavedState)) {
- super.onRestoreInstanceState(savedState);
- return;
- }
- SavedState state = (SavedState) savedState;
- super.onRestoreInstanceState(state.getSuperState());
- mMenu.restorePresenterStates(state.menuState);
- }
-
- /**
- * Set a listener that will be notified when a menu item is selected.
- *
- * @param listener The listener to notify
- */
- public void setNavigationItemSelectedListener(
- @Nullable OnNavigationItemSelectedListener listener) {
- mListener = listener;
- }
-
- @Override
- protected void onMeasure(int widthSpec, int heightSpec) {
- switch (MeasureSpec.getMode(widthSpec)) {
- case MeasureSpec.EXACTLY:
- // Nothing to do
- break;
- case MeasureSpec.AT_MOST:
- widthSpec = MeasureSpec.makeMeasureSpec(
- Math.min(MeasureSpec.getSize(widthSpec), mMaxWidth), MeasureSpec.EXACTLY);
- break;
- case MeasureSpec.UNSPECIFIED:
- widthSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, MeasureSpec.EXACTLY);
- break;
- }
- // Let super sort out the height
- super.onMeasure(widthSpec, heightSpec);
- }
-
- /**
- * @hide
- */
- @RestrictTo(LIBRARY_GROUP)
- @Override
- protected void onInsetsChanged(WindowInsetsCompat insets) {
- mPresenter.dispatchApplyWindowInsets(insets);
- }
-
- /**
- * Inflate a menu resource into this navigation view.
- *
- * <p>Existing items in the menu will not be modified or removed.</p>
- *
- * @param resId ID of a menu resource to inflate
- */
- public void inflateMenu(int resId) {
- mPresenter.setUpdateSuspended(true);
- getMenuInflater().inflate(resId, mMenu);
- mPresenter.setUpdateSuspended(false);
- mPresenter.updateMenuView(false);
- }
-
- /**
- * Returns the {@link Menu} instance associated with this navigation view.
- */
- public Menu getMenu() {
- return mMenu;
- }
-
- /**
- * Inflates a View and add it as a header of the navigation menu.
- *
- * @param res The layout resource ID.
- * @return a newly inflated View.
- */
- public View inflateHeaderView(@LayoutRes int res) {
- return mPresenter.inflateHeaderView(res);
- }
-
- /**
- * Adds a View as a header of the navigation menu.
- *
- * @param view The view to be added as a header of the navigation menu.
- */
- public void addHeaderView(@NonNull View view) {
- mPresenter.addHeaderView(view);
- }
-
- /**
- * Removes a previously-added header view.
- *
- * @param view The view to remove
- */
- public void removeHeaderView(@NonNull View view) {
- mPresenter.removeHeaderView(view);
- }
-
- /**
- * Gets the number of headers in this NavigationView.
- *
- * @return A positive integer representing the number of headers.
- */
- public int getHeaderCount() {
- return mPresenter.getHeaderCount();
- }
-
- /**
- * Gets the header view at the specified position.
- *
- * @param index The position at which to get the view from.
- * @return The header view the specified position or null if the position does not exist in this
- * NavigationView.
- */
- public View getHeaderView(int index) {
- return mPresenter.getHeaderView(index);
- }
-
- /**
- * Returns the tint which is applied to our menu items' icons.
- *
- * @see #setItemIconTintList(ColorStateList)
- *
- * @attr ref R.styleable#NavigationView_itemIconTint
- */
- @Nullable
- public ColorStateList getItemIconTintList() {
- return mPresenter.getItemTintList();
- }
-
- /**
- * Set the tint which is applied to our menu items' icons.
- *
- * @param tint the tint to apply.
- *
- * @attr ref R.styleable#NavigationView_itemIconTint
- */
- public void setItemIconTintList(@Nullable ColorStateList tint) {
- mPresenter.setItemIconTintList(tint);
- }
-
- /**
- * Returns the tint which is applied to our menu items' icons.
- *
- * @see #setItemTextColor(ColorStateList)
- *
- * @attr ref R.styleable#NavigationView_itemTextColor
- */
- @Nullable
- public ColorStateList getItemTextColor() {
- return mPresenter.getItemTextColor();
- }
-
- /**
- * Set the text color to be used on our menu items.
- *
- * @see #getItemTextColor()
- *
- * @attr ref R.styleable#NavigationView_itemTextColor
- */
- public void setItemTextColor(@Nullable ColorStateList textColor) {
- mPresenter.setItemTextColor(textColor);
- }
-
- /**
- * Returns the background drawable for our menu items.
- *
- * @see #setItemBackgroundResource(int)
- *
- * @attr ref R.styleable#NavigationView_itemBackground
- */
- @Nullable
- public Drawable getItemBackground() {
- return mPresenter.getItemBackground();
- }
-
- /**
- * Set the background of our menu items to the given resource.
- *
- * @param resId The identifier of the resource.
- *
- * @attr ref R.styleable#NavigationView_itemBackground
- */
- public void setItemBackgroundResource(@DrawableRes int resId) {
- setItemBackground(ContextCompat.getDrawable(getContext(), resId));
- }
-
- /**
- * Set the background of our menu items to a given resource. The resource should refer to
- * a Drawable object or null to use the default background set on this navigation menu.
- *
- * @attr ref R.styleable#NavigationView_itemBackground
- */
- public void setItemBackground(@Nullable Drawable itemBackground) {
- mPresenter.setItemBackground(itemBackground);
- }
-
- /**
- * Sets the currently checked item in this navigation menu.
- *
- * @param id The item ID of the currently checked item.
- */
- public void setCheckedItem(@IdRes int id) {
- MenuItem item = mMenu.findItem(id);
- if (item != null) {
- mPresenter.setCheckedItem((MenuItemImpl) item);
- }
- }
-
- /**
- * Set the text appearance of the menu items to a given resource.
- *
- * @attr ref R.styleable#NavigationView_itemTextAppearance
- */
- public void setItemTextAppearance(@StyleRes int resId) {
- mPresenter.setItemTextAppearance(resId);
- }
-
- private MenuInflater getMenuInflater() {
- if (mMenuInflater == null) {
- mMenuInflater = new SupportMenuInflater(getContext());
- }
- return mMenuInflater;
- }
-
- private ColorStateList createDefaultColorStateList(int baseColorThemeAttr) {
- final TypedValue value = new TypedValue();
- if (!getContext().getTheme().resolveAttribute(baseColorThemeAttr, value, true)) {
- return null;
- }
- ColorStateList baseColor = AppCompatResources.getColorStateList(
- getContext(), value.resourceId);
- if (!getContext().getTheme().resolveAttribute(
- android.support.v7.appcompat.R.attr.colorPrimary, value, true)) {
- return null;
- }
- int colorPrimary = value.data;
- int defaultColor = baseColor.getDefaultColor();
- return new ColorStateList(new int[][]{
- DISABLED_STATE_SET,
- CHECKED_STATE_SET,
- EMPTY_STATE_SET
- }, new int[]{
- baseColor.getColorForState(DISABLED_STATE_SET, defaultColor),
- colorPrimary,
- defaultColor
- });
- }
-
- /**
- * Listener for handling events on navigation items.
- */
- public interface OnNavigationItemSelectedListener {
-
- /**
- * Called when an item in the navigation menu is selected.
- *
- * @param item The selected item
- *
- * @return true to display the item as the selected item
- */
- public boolean onNavigationItemSelected(@NonNull MenuItem item);
- }
-
- /**
- * User interface state that is stored by NavigationView for implementing
- * onSaveInstanceState().
- */
- public static class SavedState extends AbsSavedState {
- public Bundle menuState;
-
- public SavedState(Parcel in, ClassLoader loader) {
- super(in, loader);
- menuState = in.readBundle(loader);
- }
-
- public SavedState(Parcelable superState) {
- super(superState);
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeBundle(menuState);
- }
-
- public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
- @Override
- public SavedState createFromParcel(Parcel in, ClassLoader loader) {
- return new SavedState(in, loader);
- }
-
- @Override
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in, null);
- }
-
- @Override
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
-}
diff --git a/android/support/design/widget/ShadowDrawableWrapper.java b/android/support/design/widget/ShadowDrawableWrapper.java
deleted file mode 100644
index dfb8e1d6..00000000
--- a/android/support/design/widget/ShadowDrawableWrapper.java
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.LinearGradient;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.RadialGradient;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Shader;
-import android.graphics.drawable.Drawable;
-import android.support.design.R;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.graphics.drawable.DrawableWrapper;
-
-/**
- * A {@link android.graphics.drawable.Drawable} which wraps another drawable and
- * draws a shadow around it.
- */
-class ShadowDrawableWrapper extends DrawableWrapper {
- // used to calculate content padding
- static final double COS_45 = Math.cos(Math.toRadians(45));
-
- static final float SHADOW_MULTIPLIER = 1.5f;
-
- static final float SHADOW_TOP_SCALE = 0.25f;
- static final float SHADOW_HORIZ_SCALE = 0.5f;
- static final float SHADOW_BOTTOM_SCALE = 1f;
-
- final Paint mCornerShadowPaint;
- final Paint mEdgeShadowPaint;
-
- final RectF mContentBounds;
-
- float mCornerRadius;
-
- Path mCornerShadowPath;
-
- // updated value with inset
- float mMaxShadowSize;
- // actual value set by developer
- float mRawMaxShadowSize;
-
- // multiplied value to account for shadow offset
- float mShadowSize;
- // actual value set by developer
- float mRawShadowSize;
-
- private boolean mDirty = true;
-
- private final int mShadowStartColor;
- private final int mShadowMiddleColor;
- private final int mShadowEndColor;
-
- private boolean mAddPaddingForCorners = true;
-
- private float mRotation;
-
- /**
- * If shadow size is set to a value above max shadow, we print a warning
- */
- private boolean mPrintedShadowClipWarning = false;
-
- public ShadowDrawableWrapper(Context context, Drawable content, float radius,
- float shadowSize, float maxShadowSize) {
- super(content);
-
- mShadowStartColor = ContextCompat.getColor(context, R.color.design_fab_shadow_start_color);
- mShadowMiddleColor = ContextCompat.getColor(context, R.color.design_fab_shadow_mid_color);
- mShadowEndColor = ContextCompat.getColor(context, R.color.design_fab_shadow_end_color);
-
- mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
- mCornerShadowPaint.setStyle(Paint.Style.FILL);
- mCornerRadius = Math.round(radius);
- mContentBounds = new RectF();
- mEdgeShadowPaint = new Paint(mCornerShadowPaint);
- mEdgeShadowPaint.setAntiAlias(false);
- setShadowSize(shadowSize, maxShadowSize);
- }
-
- /**
- * Casts the value to an even integer.
- */
- private static int toEven(float value) {
- int i = Math.round(value);
- return (i % 2 == 1) ? i - 1 : i;
- }
-
- public void setAddPaddingForCorners(boolean addPaddingForCorners) {
- mAddPaddingForCorners = addPaddingForCorners;
- invalidateSelf();
- }
-
- @Override
- public void setAlpha(int alpha) {
- super.setAlpha(alpha);
- mCornerShadowPaint.setAlpha(alpha);
- mEdgeShadowPaint.setAlpha(alpha);
- }
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- mDirty = true;
- }
-
- void setShadowSize(float shadowSize, float maxShadowSize) {
- if (shadowSize < 0 || maxShadowSize < 0) {
- throw new IllegalArgumentException("invalid shadow size");
- }
- shadowSize = toEven(shadowSize);
- maxShadowSize = toEven(maxShadowSize);
- if (shadowSize > maxShadowSize) {
- shadowSize = maxShadowSize;
- if (!mPrintedShadowClipWarning) {
- mPrintedShadowClipWarning = true;
- }
- }
- if (mRawShadowSize == shadowSize && mRawMaxShadowSize == maxShadowSize) {
- return;
- }
- mRawShadowSize = shadowSize;
- mRawMaxShadowSize = maxShadowSize;
- mShadowSize = Math.round(shadowSize * SHADOW_MULTIPLIER);
- mMaxShadowSize = maxShadowSize;
- mDirty = true;
- invalidateSelf();
- }
-
- @Override
- public boolean getPadding(Rect padding) {
- int vOffset = (int) Math.ceil(calculateVerticalPadding(mRawMaxShadowSize, mCornerRadius,
- mAddPaddingForCorners));
- int hOffset = (int) Math.ceil(calculateHorizontalPadding(mRawMaxShadowSize, mCornerRadius,
- mAddPaddingForCorners));
- padding.set(hOffset, vOffset, hOffset, vOffset);
- return true;
- }
-
- public static float calculateVerticalPadding(float maxShadowSize, float cornerRadius,
- boolean addPaddingForCorners) {
- if (addPaddingForCorners) {
- return (float) (maxShadowSize * SHADOW_MULTIPLIER + (1 - COS_45) * cornerRadius);
- } else {
- return maxShadowSize * SHADOW_MULTIPLIER;
- }
- }
-
- public static float calculateHorizontalPadding(float maxShadowSize, float cornerRadius,
- boolean addPaddingForCorners) {
- if (addPaddingForCorners) {
- return (float) (maxShadowSize + (1 - COS_45) * cornerRadius);
- } else {
- return maxShadowSize;
- }
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- public void setCornerRadius(float radius) {
- radius = Math.round(radius);
- if (mCornerRadius == radius) {
- return;
- }
- mCornerRadius = radius;
- mDirty = true;
- invalidateSelf();
- }
-
- @Override
- public void draw(Canvas canvas) {
- if (mDirty) {
- buildComponents(getBounds());
- mDirty = false;
- }
- drawShadow(canvas);
-
- super.draw(canvas);
- }
-
- final void setRotation(float rotation) {
- if (mRotation != rotation) {
- mRotation = rotation;
- invalidateSelf();
- }
- }
-
- private void drawShadow(Canvas canvas) {
- final int rotateSaved = canvas.save();
- canvas.rotate(mRotation, mContentBounds.centerX(), mContentBounds.centerY());
-
- final float edgeShadowTop = -mCornerRadius - mShadowSize;
- final float shadowOffset = mCornerRadius;
- final boolean drawHorizontalEdges = mContentBounds.width() - 2 * shadowOffset > 0;
- final boolean drawVerticalEdges = mContentBounds.height() - 2 * shadowOffset > 0;
-
- final float shadowOffsetTop = mRawShadowSize - (mRawShadowSize * SHADOW_TOP_SCALE);
- final float shadowOffsetHorizontal = mRawShadowSize - (mRawShadowSize * SHADOW_HORIZ_SCALE);
- final float shadowOffsetBottom = mRawShadowSize - (mRawShadowSize * SHADOW_BOTTOM_SCALE);
-
- final float shadowScaleHorizontal = shadowOffset / (shadowOffset + shadowOffsetHorizontal);
- final float shadowScaleTop = shadowOffset / (shadowOffset + shadowOffsetTop);
- final float shadowScaleBottom = shadowOffset / (shadowOffset + shadowOffsetBottom);
-
- // LT
- int saved = canvas.save();
- canvas.translate(mContentBounds.left + shadowOffset, mContentBounds.top + shadowOffset);
- canvas.scale(shadowScaleHorizontal, shadowScaleTop);
- canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
- if (drawHorizontalEdges) {
- // TE
- canvas.scale(1f / shadowScaleHorizontal, 1f);
- canvas.drawRect(0, edgeShadowTop,
- mContentBounds.width() - 2 * shadowOffset, -mCornerRadius,
- mEdgeShadowPaint);
- }
- canvas.restoreToCount(saved);
- // RB
- saved = canvas.save();
- canvas.translate(mContentBounds.right - shadowOffset, mContentBounds.bottom - shadowOffset);
- canvas.scale(shadowScaleHorizontal, shadowScaleBottom);
- canvas.rotate(180f);
- canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
- if (drawHorizontalEdges) {
- // BE
- canvas.scale(1f / shadowScaleHorizontal, 1f);
- canvas.drawRect(0, edgeShadowTop,
- mContentBounds.width() - 2 * shadowOffset, -mCornerRadius + mShadowSize,
- mEdgeShadowPaint);
- }
- canvas.restoreToCount(saved);
- // LB
- saved = canvas.save();
- canvas.translate(mContentBounds.left + shadowOffset, mContentBounds.bottom - shadowOffset);
- canvas.scale(shadowScaleHorizontal, shadowScaleBottom);
- canvas.rotate(270f);
- canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
- if (drawVerticalEdges) {
- // LE
- canvas.scale(1f / shadowScaleBottom, 1f);
- canvas.drawRect(0, edgeShadowTop,
- mContentBounds.height() - 2 * shadowOffset, -mCornerRadius, mEdgeShadowPaint);
- }
- canvas.restoreToCount(saved);
- // RT
- saved = canvas.save();
- canvas.translate(mContentBounds.right - shadowOffset, mContentBounds.top + shadowOffset);
- canvas.scale(shadowScaleHorizontal, shadowScaleTop);
- canvas.rotate(90f);
- canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
- if (drawVerticalEdges) {
- // RE
- canvas.scale(1f / shadowScaleTop, 1f);
- canvas.drawRect(0, edgeShadowTop,
- mContentBounds.height() - 2 * shadowOffset, -mCornerRadius, mEdgeShadowPaint);
- }
- canvas.restoreToCount(saved);
-
- canvas.restoreToCount(rotateSaved);
- }
-
- private void buildShadowCorners() {
- RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius);
- RectF outerBounds = new RectF(innerBounds);
- outerBounds.inset(-mShadowSize, -mShadowSize);
-
- if (mCornerShadowPath == null) {
- mCornerShadowPath = new Path();
- } else {
- mCornerShadowPath.reset();
- }
- mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD);
- mCornerShadowPath.moveTo(-mCornerRadius, 0);
- mCornerShadowPath.rLineTo(-mShadowSize, 0);
- // outer arc
- mCornerShadowPath.arcTo(outerBounds, 180f, 90f, false);
- // inner arc
- mCornerShadowPath.arcTo(innerBounds, 270f, -90f, false);
- mCornerShadowPath.close();
-
- float shadowRadius = -outerBounds.top;
- if (shadowRadius > 0f) {
- float startRatio = mCornerRadius / shadowRadius;
- float midRatio = startRatio + ((1f - startRatio) / 2f);
- mCornerShadowPaint.setShader(new RadialGradient(0, 0, shadowRadius,
- new int[]{0, mShadowStartColor, mShadowMiddleColor, mShadowEndColor},
- new float[]{0f, startRatio, midRatio, 1f},
- Shader.TileMode.CLAMP));
- }
-
- // we offset the content shadowSize/2 pixels up to make it more realistic.
- // this is why edge shadow shader has some extra space
- // When drawing bottom edge shadow, we use that extra space.
- mEdgeShadowPaint.setShader(new LinearGradient(0, innerBounds.top, 0, outerBounds.top,
- new int[]{mShadowStartColor, mShadowMiddleColor, mShadowEndColor},
- new float[]{0f, .5f, 1f}, Shader.TileMode.CLAMP));
- mEdgeShadowPaint.setAntiAlias(false);
- }
-
- private void buildComponents(Rect bounds) {
- // Card is offset SHADOW_MULTIPLIER * maxShadowSize to account for the shadow shift.
- // We could have different top-bottom offsets to avoid extra gap above but in that case
- // center aligning Views inside the CardView would be problematic.
- final float verticalOffset = mRawMaxShadowSize * SHADOW_MULTIPLIER;
- mContentBounds.set(bounds.left + mRawMaxShadowSize, bounds.top + verticalOffset,
- bounds.right - mRawMaxShadowSize, bounds.bottom - verticalOffset);
-
- getWrappedDrawable().setBounds((int) mContentBounds.left, (int) mContentBounds.top,
- (int) mContentBounds.right, (int) mContentBounds.bottom);
-
- buildShadowCorners();
- }
-
- public float getCornerRadius() {
- return mCornerRadius;
- }
-
- public void setShadowSize(float size) {
- setShadowSize(size, mRawMaxShadowSize);
- }
-
- public void setMaxShadowSize(float size) {
- setShadowSize(mRawShadowSize, size);
- }
-
- public float getShadowSize() {
- return mRawShadowSize;
- }
-
- public float getMaxShadowSize() {
- return mRawMaxShadowSize;
- }
-
- public float getMinWidth() {
- final float content = 2 *
- Math.max(mRawMaxShadowSize, mCornerRadius + mRawMaxShadowSize / 2);
- return content + mRawMaxShadowSize * 2;
- }
-
- public float getMinHeight() {
- final float content = 2 * Math.max(mRawMaxShadowSize, mCornerRadius
- + mRawMaxShadowSize * SHADOW_MULTIPLIER / 2);
- return content + (mRawMaxShadowSize * SHADOW_MULTIPLIER) * 2;
- }
-}
diff --git a/android/support/design/widget/ShadowViewDelegate.java b/android/support/design/widget/ShadowViewDelegate.java
deleted file mode 100644
index 83a3a7a1..00000000
--- a/android/support/design/widget/ShadowViewDelegate.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.graphics.drawable.Drawable;
-
-interface ShadowViewDelegate {
- float getRadius();
- void setShadowPadding(int left, int top, int right, int bottom);
- void setBackgroundDrawable(Drawable background);
- boolean isCompatPaddingEnabled();
-}
diff --git a/android/support/design/widget/Snackbar.java b/android/support/design/widget/Snackbar.java
deleted file mode 100644
index bd5ffbab..00000000
--- a/android/support/design/widget/Snackbar.java
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * 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.design.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.support.annotation.ColorInt;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StringRes;
-import android.support.design.R;
-import android.support.design.internal.SnackbarContentLayout;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-/**
- * Snackbars provide lightweight feedback about an operation. They show a brief message at the
- * bottom of the screen on mobile and lower left on larger devices. Snackbars appear above all other
- * elements on screen and only one can be displayed at a time.
- * <p>
- * They automatically disappear after a timeout or after user interaction elsewhere on the screen,
- * particularly after interactions that summon a new surface or activity. Snackbars can be swiped
- * off screen.
- * <p>
- * Snackbars can contain an action which is set via
- * {@link #setAction(CharSequence, android.view.View.OnClickListener)}.
- * <p>
- * To be notified when a snackbar has been shown or dismissed, you can provide a {@link Callback}
- * via {@link BaseTransientBottomBar#addCallback(BaseCallback)}.</p>
- */
-public final class Snackbar extends BaseTransientBottomBar<Snackbar> {
-
- /**
- * Show the Snackbar indefinitely. This means that the Snackbar will be displayed from the time
- * that is {@link #show() shown} until either it is dismissed, or another Snackbar is shown.
- *
- * @see #setDuration
- */
- public static final int LENGTH_INDEFINITE = BaseTransientBottomBar.LENGTH_INDEFINITE;
-
- /**
- * Show the Snackbar for a short period of time.
- *
- * @see #setDuration
- */
- public static final int LENGTH_SHORT = BaseTransientBottomBar.LENGTH_SHORT;
-
- /**
- * Show the Snackbar for a long period of time.
- *
- * @see #setDuration
- */
- public static final int LENGTH_LONG = BaseTransientBottomBar.LENGTH_LONG;
-
- /**
- * Callback class for {@link Snackbar} instances.
- *
- * Note: this class is here to provide backwards-compatible way for apps written before
- * the existence of the base {@link BaseTransientBottomBar} class.
- *
- * @see BaseTransientBottomBar#addCallback(BaseCallback)
- */
- public static class Callback extends BaseCallback<Snackbar> {
- /** Indicates that the Snackbar was dismissed via a swipe.*/
- public static final int DISMISS_EVENT_SWIPE = BaseCallback.DISMISS_EVENT_SWIPE;
- /** Indicates that the Snackbar was dismissed via an action click.*/
- public static final int DISMISS_EVENT_ACTION = BaseCallback.DISMISS_EVENT_ACTION;
- /** Indicates that the Snackbar was dismissed via a timeout.*/
- public static final int DISMISS_EVENT_TIMEOUT = BaseCallback.DISMISS_EVENT_TIMEOUT;
- /** Indicates that the Snackbar was dismissed via a call to {@link #dismiss()}.*/
- public static final int DISMISS_EVENT_MANUAL = BaseCallback.DISMISS_EVENT_MANUAL;
- /** Indicates that the Snackbar was dismissed from a new Snackbar being shown.*/
- public static final int DISMISS_EVENT_CONSECUTIVE = BaseCallback.DISMISS_EVENT_CONSECUTIVE;
-
- @Override
- public void onShown(Snackbar sb) {
- // Stub implementation to make API check happy.
- }
-
- @Override
- public void onDismissed(Snackbar transientBottomBar, @DismissEvent int event) {
- // Stub implementation to make API check happy.
- }
- }
-
- @Nullable private BaseCallback<Snackbar> mCallback;
-
- private Snackbar(ViewGroup parent, View content, ContentViewCallback contentViewCallback) {
- super(parent, content, contentViewCallback);
- }
-
- /**
- * Make a Snackbar to display a message
- *
- * <p>Snackbar will try and find a parent view to hold Snackbar's view from the value given
- * to {@code view}. Snackbar will walk up the view tree trying to find a suitable parent,
- * which is defined as a {@link CoordinatorLayout} or the window decor's content view,
- * whichever comes first.
- *
- * <p>Having a {@link CoordinatorLayout} in your view hierarchy allows Snackbar to enable
- * certain features, such as swipe-to-dismiss and automatically moving of widgets like
- * {@link FloatingActionButton}.
- *
- * @param view The view to find a parent from.
- * @param text The text to show. Can be formatted text.
- * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or {@link
- * #LENGTH_LONG}
- */
- @NonNull
- public static Snackbar make(@NonNull View view, @NonNull CharSequence text,
- @Duration int duration) {
- final ViewGroup parent = findSuitableParent(view);
- if (parent == null) {
- throw new IllegalArgumentException("No suitable parent found from the given view. "
- + "Please provide a valid view.");
- }
-
- final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
- final SnackbarContentLayout content =
- (SnackbarContentLayout) inflater.inflate(
- R.layout.design_layout_snackbar_include, parent, false);
- final Snackbar snackbar = new Snackbar(parent, content, content);
- snackbar.setText(text);
- snackbar.setDuration(duration);
- return snackbar;
- }
-
- /**
- * Make a Snackbar to display a message.
- *
- * <p>Snackbar will try and find a parent view to hold Snackbar's view from the value given
- * to {@code view}. Snackbar will walk up the view tree trying to find a suitable parent,
- * which is defined as a {@link CoordinatorLayout} or the window decor's content view,
- * whichever comes first.
- *
- * <p>Having a {@link CoordinatorLayout} in your view hierarchy allows Snackbar to enable
- * certain features, such as swipe-to-dismiss and automatically moving of widgets like
- * {@link FloatingActionButton}.
- *
- * @param view The view to find a parent from.
- * @param resId The resource id of the string resource to use. Can be formatted text.
- * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or {@link
- * #LENGTH_LONG}
- */
- @NonNull
- public static Snackbar make(@NonNull View view, @StringRes int resId, @Duration int duration) {
- return make(view, view.getResources().getText(resId), duration);
- }
-
- private static ViewGroup findSuitableParent(View view) {
- ViewGroup fallback = null;
- do {
- if (view instanceof CoordinatorLayout) {
- // We've found a CoordinatorLayout, use it
- return (ViewGroup) view;
- } else if (view instanceof FrameLayout) {
- if (view.getId() == android.R.id.content) {
- // If we've hit the decor content view, then we didn't find a CoL in the
- // hierarchy, so use it.
- return (ViewGroup) view;
- } else {
- // It's not the content view but we'll use it as our fallback
- fallback = (ViewGroup) view;
- }
- }
-
- if (view != null) {
- // Else, we will loop and crawl up the view hierarchy and try to find a parent
- final ViewParent parent = view.getParent();
- view = parent instanceof View ? (View) parent : null;
- }
- } while (view != null);
-
- // If we reach here then we didn't find a CoL or a suitable content view so we'll fallback
- return fallback;
- }
-
- /**
- * Update the text in this {@link Snackbar}.
- *
- * @param message The new text for this {@link BaseTransientBottomBar}.
- */
- @NonNull
- public Snackbar setText(@NonNull CharSequence message) {
- final SnackbarContentLayout contentLayout = (SnackbarContentLayout) mView.getChildAt(0);
- final TextView tv = contentLayout.getMessageView();
- tv.setText(message);
- return this;
- }
-
- /**
- * Update the text in this {@link Snackbar}.
- *
- * @param resId The new text for this {@link BaseTransientBottomBar}.
- */
- @NonNull
- public Snackbar setText(@StringRes int resId) {
- return setText(getContext().getText(resId));
- }
-
- /**
- * Set the action to be displayed in this {@link BaseTransientBottomBar}.
- *
- * @param resId String resource to display for the action
- * @param listener callback to be invoked when the action is clicked
- */
- @NonNull
- public Snackbar setAction(@StringRes int resId, View.OnClickListener listener) {
- return setAction(getContext().getText(resId), listener);
- }
-
- /**
- * Set the action to be displayed in this {@link BaseTransientBottomBar}.
- *
- * @param text Text to display for the action
- * @param listener callback to be invoked when the action is clicked
- */
- @NonNull
- public Snackbar setAction(CharSequence text, final View.OnClickListener listener) {
- final SnackbarContentLayout contentLayout = (SnackbarContentLayout) mView.getChildAt(0);
- final TextView tv = contentLayout.getActionView();
-
- if (TextUtils.isEmpty(text) || listener == null) {
- tv.setVisibility(View.GONE);
- tv.setOnClickListener(null);
- } else {
- tv.setVisibility(View.VISIBLE);
- tv.setText(text);
- tv.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- listener.onClick(view);
- // Now dismiss the Snackbar
- dispatchDismiss(BaseCallback.DISMISS_EVENT_ACTION);
- }
- });
- }
- return this;
- }
-
- /**
- * Sets the text color of the action specified in
- * {@link #setAction(CharSequence, View.OnClickListener)}.
- */
- @NonNull
- public Snackbar setActionTextColor(ColorStateList colors) {
- final SnackbarContentLayout contentLayout = (SnackbarContentLayout) mView.getChildAt(0);
- final TextView tv = contentLayout.getActionView();
- tv.setTextColor(colors);
- return this;
- }
-
- /**
- * Sets the text color of the action specified in
- * {@link #setAction(CharSequence, View.OnClickListener)}.
- */
- @NonNull
- public Snackbar setActionTextColor(@ColorInt int color) {
- final SnackbarContentLayout contentLayout = (SnackbarContentLayout) mView.getChildAt(0);
- final TextView tv = contentLayout.getActionView();
- tv.setTextColor(color);
- return this;
- }
-
- /**
- * Set a callback to be called when this the visibility of this {@link Snackbar}
- * changes. Note that this method is deprecated
- * and you should use {@link #addCallback(BaseCallback)} to add a callback and
- * {@link #removeCallback(BaseCallback)} to remove a registered callback.
- *
- * @param callback Callback to notify when transient bottom bar events occur.
- * @deprecated Use {@link #addCallback(BaseCallback)}
- * @see Callback
- * @see #addCallback(BaseCallback)
- * @see #removeCallback(BaseCallback)
- */
- @Deprecated
- @NonNull
- public Snackbar setCallback(Callback callback) {
- // The logic in this method emulates what we had before support for multiple
- // registered callbacks.
- if (mCallback != null) {
- removeCallback(mCallback);
- }
- if (callback != null) {
- addCallback(callback);
- }
- // Update the deprecated field so that we can remove the passed callback the next
- // time we're called
- mCallback = callback;
- return this;
- }
-
- /**
- * @hide
- *
- * Note: this class is here to provide backwards-compatible way for apps written before
- * the existence of the base {@link BaseTransientBottomBar} class.
- */
- @RestrictTo(LIBRARY_GROUP)
- public static final class SnackbarLayout extends BaseTransientBottomBar.SnackbarBaseLayout {
- public SnackbarLayout(Context context) {
- super(context);
- }
-
- public SnackbarLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- // Work around our backwards-compatible refactoring of Snackbar and inner content
- // being inflated against snackbar's parent (instead of against the snackbar itself).
- // Every child that is width=MATCH_PARENT is remeasured again and given the full width
- // minus the paddings.
- int childCount = getChildCount();
- int availableWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT) {
- child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
- MeasureSpec.EXACTLY));
- }
- }
- }
- }
-}
-
diff --git a/android/support/design/widget/SnackbarManager.java b/android/support/design/widget/SnackbarManager.java
deleted file mode 100644
index 43892d36..00000000
--- a/android/support/design/widget/SnackbarManager.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Manages {@link Snackbar}s.
- */
-class SnackbarManager {
-
- static final int MSG_TIMEOUT = 0;
-
- private static final int SHORT_DURATION_MS = 1500;
- private static final int LONG_DURATION_MS = 2750;
-
- private static SnackbarManager sSnackbarManager;
-
- static SnackbarManager getInstance() {
- if (sSnackbarManager == null) {
- sSnackbarManager = new SnackbarManager();
- }
- return sSnackbarManager;
- }
-
- private final Object mLock;
- private final Handler mHandler;
-
- private SnackbarRecord mCurrentSnackbar;
- private SnackbarRecord mNextSnackbar;
-
- private SnackbarManager() {
- mLock = new Object();
- mHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
- @Override
- public boolean handleMessage(Message message) {
- switch (message.what) {
- case MSG_TIMEOUT:
- handleTimeout((SnackbarRecord) message.obj);
- return true;
- }
- return false;
- }
- });
- }
-
- interface Callback {
- void show();
- void dismiss(int event);
- }
-
- public void show(int duration, Callback callback) {
- synchronized (mLock) {
- if (isCurrentSnackbarLocked(callback)) {
- // Means that the callback is already in the queue. We'll just update the duration
- mCurrentSnackbar.duration = duration;
-
- // If this is the Snackbar currently being shown, call re-schedule it's
- // timeout
- mHandler.removeCallbacksAndMessages(mCurrentSnackbar);
- scheduleTimeoutLocked(mCurrentSnackbar);
- return;
- } else if (isNextSnackbarLocked(callback)) {
- // We'll just update the duration
- mNextSnackbar.duration = duration;
- } else {
- // Else, we need to create a new record and queue it
- mNextSnackbar = new SnackbarRecord(duration, callback);
- }
-
- if (mCurrentSnackbar != null && cancelSnackbarLocked(mCurrentSnackbar,
- Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE)) {
- // If we currently have a Snackbar, try and cancel it and wait in line
- return;
- } else {
- // Clear out the current snackbar
- mCurrentSnackbar = null;
- // Otherwise, just show it now
- showNextSnackbarLocked();
- }
- }
- }
-
- public void dismiss(Callback callback, int event) {
- synchronized (mLock) {
- if (isCurrentSnackbarLocked(callback)) {
- cancelSnackbarLocked(mCurrentSnackbar, event);
- } else if (isNextSnackbarLocked(callback)) {
- cancelSnackbarLocked(mNextSnackbar, event);
- }
- }
- }
-
- /**
- * Should be called when a Snackbar is no longer displayed. This is after any exit
- * animation has finished.
- */
- public void onDismissed(Callback callback) {
- synchronized (mLock) {
- if (isCurrentSnackbarLocked(callback)) {
- // If the callback is from a Snackbar currently show, remove it and show a new one
- mCurrentSnackbar = null;
- if (mNextSnackbar != null) {
- showNextSnackbarLocked();
- }
- }
- }
- }
-
- /**
- * Should be called when a Snackbar is being shown. This is after any entrance animation has
- * finished.
- */
- public void onShown(Callback callback) {
- synchronized (mLock) {
- if (isCurrentSnackbarLocked(callback)) {
- scheduleTimeoutLocked(mCurrentSnackbar);
- }
- }
- }
-
- public void pauseTimeout(Callback callback) {
- synchronized (mLock) {
- if (isCurrentSnackbarLocked(callback) && !mCurrentSnackbar.paused) {
- mCurrentSnackbar.paused = true;
- mHandler.removeCallbacksAndMessages(mCurrentSnackbar);
- }
- }
- }
-
- public void restoreTimeoutIfPaused(Callback callback) {
- synchronized (mLock) {
- if (isCurrentSnackbarLocked(callback) && mCurrentSnackbar.paused) {
- mCurrentSnackbar.paused = false;
- scheduleTimeoutLocked(mCurrentSnackbar);
- }
- }
- }
-
- public boolean isCurrent(Callback callback) {
- synchronized (mLock) {
- return isCurrentSnackbarLocked(callback);
- }
- }
-
- public boolean isCurrentOrNext(Callback callback) {
- synchronized (mLock) {
- return isCurrentSnackbarLocked(callback) || isNextSnackbarLocked(callback);
- }
- }
-
- private static class SnackbarRecord {
- final WeakReference<Callback> callback;
- int duration;
- boolean paused;
-
- SnackbarRecord(int duration, Callback callback) {
- this.callback = new WeakReference<>(callback);
- this.duration = duration;
- }
-
- boolean isSnackbar(Callback callback) {
- return callback != null && this.callback.get() == callback;
- }
- }
-
- private void showNextSnackbarLocked() {
- if (mNextSnackbar != null) {
- mCurrentSnackbar = mNextSnackbar;
- mNextSnackbar = null;
-
- final Callback callback = mCurrentSnackbar.callback.get();
- if (callback != null) {
- callback.show();
- } else {
- // The callback doesn't exist any more, clear out the Snackbar
- mCurrentSnackbar = null;
- }
- }
- }
-
- private boolean cancelSnackbarLocked(SnackbarRecord record, int event) {
- final Callback callback = record.callback.get();
- if (callback != null) {
- // Make sure we remove any timeouts for the SnackbarRecord
- mHandler.removeCallbacksAndMessages(record);
- callback.dismiss(event);
- return true;
- }
- return false;
- }
-
- private boolean isCurrentSnackbarLocked(Callback callback) {
- return mCurrentSnackbar != null && mCurrentSnackbar.isSnackbar(callback);
- }
-
- private boolean isNextSnackbarLocked(Callback callback) {
- return mNextSnackbar != null && mNextSnackbar.isSnackbar(callback);
- }
-
- private void scheduleTimeoutLocked(SnackbarRecord r) {
- if (r.duration == Snackbar.LENGTH_INDEFINITE) {
- // If we're set to indefinite, we don't want to set a timeout
- return;
- }
-
- int durationMs = LONG_DURATION_MS;
- if (r.duration > 0) {
- durationMs = r.duration;
- } else if (r.duration == Snackbar.LENGTH_SHORT) {
- durationMs = SHORT_DURATION_MS;
- }
- mHandler.removeCallbacksAndMessages(r);
- mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_TIMEOUT, r), durationMs);
- }
-
- void handleTimeout(SnackbarRecord record) {
- synchronized (mLock) {
- if (mCurrentSnackbar == record || mNextSnackbar == record) {
- cancelSnackbarLocked(record, Snackbar.Callback.DISMISS_EVENT_TIMEOUT);
- }
- }
- }
-
-}
diff --git a/android/support/design/widget/StateListAnimator.java b/android/support/design/widget/StateListAnimator.java
deleted file mode 100644
index aef24be3..00000000
--- a/android/support/design/widget/StateListAnimator.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.util.StateSet;
-
-import java.util.ArrayList;
-
-final class StateListAnimator {
-
- private final ArrayList<Tuple> mTuples = new ArrayList<>();
-
- private Tuple mLastMatch = null;
- ValueAnimator mRunningAnimator = null;
-
- private final ValueAnimator.AnimatorListener mAnimationListener =
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- if (mRunningAnimator == animator) {
- mRunningAnimator = null;
- }
- }
- };
-
- /**
- * Associates the given Animation with the provided drawable state specs so that it will be run
- * when the View's drawable state matches the specs.
- *
- * @param specs The drawable state specs to match against
- * @param animator The animator to run when the specs match
- */
- public void addState(int[] specs, ValueAnimator animator) {
- Tuple tuple = new Tuple(specs, animator);
- animator.addListener(mAnimationListener);
- mTuples.add(tuple);
- }
-
- /**
- * Called by View
- */
- void setState(int[] state) {
- Tuple match = null;
- final int count = mTuples.size();
- for (int i = 0; i < count; i++) {
- final Tuple tuple = mTuples.get(i);
- if (StateSet.stateSetMatches(tuple.mSpecs, state)) {
- match = tuple;
- break;
- }
- }
- if (match == mLastMatch) {
- return;
- }
- if (mLastMatch != null) {
- cancel();
- }
-
- mLastMatch = match;
-
- if (match != null) {
- start(match);
- }
- }
-
- private void start(Tuple match) {
- mRunningAnimator = match.mAnimator;
- mRunningAnimator.start();
- }
-
- private void cancel() {
- if (mRunningAnimator != null) {
- mRunningAnimator.cancel();
- mRunningAnimator = null;
- }
- }
-
- /**
- * If there is an animation running for a recent state change, ends it.
- *
- * <p>This causes the animation to assign the end value(s) to the View.</p>
- */
- public void jumpToCurrentState() {
- if (mRunningAnimator != null) {
- mRunningAnimator.end();
- mRunningAnimator = null;
- }
- }
-
- static class Tuple {
- final int[] mSpecs;
- final ValueAnimator mAnimator;
-
- Tuple(int[] specs, ValueAnimator animator) {
- mSpecs = specs;
- mAnimator = animator;
- }
- }
-} \ No newline at end of file
diff --git a/android/support/design/widget/SwipeDismissBehavior.java b/android/support/design/widget/SwipeDismissBehavior.java
deleted file mode 100644
index d8573340..00000000
--- a/android/support/design/widget/SwipeDismissBehavior.java
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * 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.design.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.widget.ViewDragHelper;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * An interaction behavior plugin for child views of {@link CoordinatorLayout} to provide support
- * for the 'swipe-to-dismiss' gesture.
- */
-public class SwipeDismissBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {
-
- /**
- * A view is not currently being dragged or animating as a result of a fling/snap.
- */
- public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE;
-
- /**
- * A view is currently being dragged. The position is currently changing as a result
- * of user input or simulated user input.
- */
- public static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING;
-
- /**
- * A view is currently settling into place as a result of a fling or
- * predefined non-interactive motion.
- */
- public static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING;
-
- /** @hide */
- @RestrictTo(LIBRARY_GROUP)
- @IntDef({SWIPE_DIRECTION_START_TO_END, SWIPE_DIRECTION_END_TO_START, SWIPE_DIRECTION_ANY})
- @Retention(RetentionPolicy.SOURCE)
- private @interface SwipeDirection {}
-
- /**
- * Swipe direction that only allows swiping in the direction of start-to-end. That is
- * left-to-right in LTR, or right-to-left in RTL.
- */
- public static final int SWIPE_DIRECTION_START_TO_END = 0;
-
- /**
- * Swipe direction that only allows swiping in the direction of end-to-start. That is
- * right-to-left in LTR or left-to-right in RTL.
- */
- public static final int SWIPE_DIRECTION_END_TO_START = 1;
-
- /**
- * Swipe direction which allows swiping in either direction.
- */
- public static final int SWIPE_DIRECTION_ANY = 2;
-
- private static final float DEFAULT_DRAG_DISMISS_THRESHOLD = 0.5f;
- private static final float DEFAULT_ALPHA_START_DISTANCE = 0f;
- private static final float DEFAULT_ALPHA_END_DISTANCE = DEFAULT_DRAG_DISMISS_THRESHOLD;
-
- ViewDragHelper mViewDragHelper;
- OnDismissListener mListener;
- private boolean mInterceptingEvents;
-
- private float mSensitivity = 0f;
- private boolean mSensitivitySet;
-
- int mSwipeDirection = SWIPE_DIRECTION_ANY;
- float mDragDismissThreshold = DEFAULT_DRAG_DISMISS_THRESHOLD;
- float mAlphaStartSwipeDistance = DEFAULT_ALPHA_START_DISTANCE;
- float mAlphaEndSwipeDistance = DEFAULT_ALPHA_END_DISTANCE;
-
- /**
- * Callback interface used to notify the application that the view has been dismissed.
- */
- public interface OnDismissListener {
- /**
- * Called when {@code view} has been dismissed via swiping.
- */
- public void onDismiss(View view);
-
- /**
- * Called when the drag state has changed.
- *
- * @param state the new state. One of
- * {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
- */
- public void onDragStateChanged(int state);
- }
-
- /**
- * Set the listener to be used when a dismiss event occurs.
- *
- * @param listener the listener to use.
- */
- public void setListener(OnDismissListener listener) {
- mListener = listener;
- }
-
- /**
- * Sets the swipe direction for this behavior.
- *
- * @param direction one of the {@link #SWIPE_DIRECTION_START_TO_END},
- * {@link #SWIPE_DIRECTION_END_TO_START} or {@link #SWIPE_DIRECTION_ANY}
- */
- public void setSwipeDirection(@SwipeDirection int direction) {
- mSwipeDirection = direction;
- }
-
- /**
- * Set the threshold for telling if a view has been dragged enough to be dismissed.
- *
- * @param distance a ratio of a view's width, values are clamped to 0 >= x <= 1f;
- */
- public void setDragDismissDistance(float distance) {
- mDragDismissThreshold = clamp(0f, distance, 1f);
- }
-
- /**
- * The minimum swipe distance before the view's alpha is modified.
- *
- * @param fraction the distance as a fraction of the view's width.
- */
- public void setStartAlphaSwipeDistance(float fraction) {
- mAlphaStartSwipeDistance = clamp(0f, fraction, 1f);
- }
-
- /**
- * The maximum swipe distance for the view's alpha is modified.
- *
- * @param fraction the distance as a fraction of the view's width.
- */
- public void setEndAlphaSwipeDistance(float fraction) {
- mAlphaEndSwipeDistance = clamp(0f, fraction, 1f);
- }
-
- /**
- * Set the sensitivity used for detecting the start of a swipe. This only takes effect if
- * no touch handling has occured yet.
- *
- * @param sensitivity Multiplier for how sensitive we should be about detecting
- * the start of a drag. Larger values are more sensitive. 1.0f is normal.
- */
- public void setSensitivity(float sensitivity) {
- mSensitivity = sensitivity;
- mSensitivitySet = true;
- }
-
- @Override
- public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
- boolean dispatchEventToHelper = mInterceptingEvents;
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- mInterceptingEvents = parent.isPointInChildBounds(child,
- (int) event.getX(), (int) event.getY());
- dispatchEventToHelper = mInterceptingEvents;
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- // Reset the ignore flag for next time
- mInterceptingEvents = false;
- break;
- }
-
- if (dispatchEventToHelper) {
- ensureViewDragHelper(parent);
- return mViewDragHelper.shouldInterceptTouchEvent(event);
- }
- return false;
- }
-
- @Override
- public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
- if (mViewDragHelper != null) {
- mViewDragHelper.processTouchEvent(event);
- return true;
- }
- return false;
- }
-
- /**
- * Called when the user's input indicates that they want to swipe the given view.
- *
- * @param view View the user is attempting to swipe
- * @return true if the view can be dismissed via swiping, false otherwise
- */
- public boolean canSwipeDismissView(@NonNull View view) {
- return true;
- }
-
- private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() {
- private static final int INVALID_POINTER_ID = -1;
-
- private int mOriginalCapturedViewLeft;
- private int mActivePointerId = INVALID_POINTER_ID;
-
- @Override
- public boolean tryCaptureView(View child, int pointerId) {
- // Only capture if we don't already have an active pointer id
- return mActivePointerId == INVALID_POINTER_ID && canSwipeDismissView(child);
- }
-
- @Override
- public void onViewCaptured(View capturedChild, int activePointerId) {
- mActivePointerId = activePointerId;
- mOriginalCapturedViewLeft = capturedChild.getLeft();
-
- // The view has been captured, and thus a drag is about to start so stop any parents
- // intercepting
- final ViewParent parent = capturedChild.getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(true);
- }
- }
-
- @Override
- public void onViewDragStateChanged(int state) {
- if (mListener != null) {
- mListener.onDragStateChanged(state);
- }
- }
-
- @Override
- public void onViewReleased(View child, float xvel, float yvel) {
- // Reset the active pointer ID
- mActivePointerId = INVALID_POINTER_ID;
-
- final int childWidth = child.getWidth();
- int targetLeft;
- boolean dismiss = false;
-
- if (shouldDismiss(child, xvel)) {
- targetLeft = child.getLeft() < mOriginalCapturedViewLeft
- ? mOriginalCapturedViewLeft - childWidth
- : mOriginalCapturedViewLeft + childWidth;
- dismiss = true;
- } else {
- // Else, reset back to the original left
- targetLeft = mOriginalCapturedViewLeft;
- }
-
- if (mViewDragHelper.settleCapturedViewAt(targetLeft, child.getTop())) {
- ViewCompat.postOnAnimation(child, new SettleRunnable(child, dismiss));
- } else if (dismiss && mListener != null) {
- mListener.onDismiss(child);
- }
- }
-
- private boolean shouldDismiss(View child, float xvel) {
- if (xvel != 0f) {
- final boolean isRtl = ViewCompat.getLayoutDirection(child)
- == ViewCompat.LAYOUT_DIRECTION_RTL;
-
- if (mSwipeDirection == SWIPE_DIRECTION_ANY) {
- // We don't care about the direction so return true
- return true;
- } else if (mSwipeDirection == SWIPE_DIRECTION_START_TO_END) {
- // We only allow start-to-end swiping, so the fling needs to be in the
- // correct direction
- return isRtl ? xvel < 0f : xvel > 0f;
- } else if (mSwipeDirection == SWIPE_DIRECTION_END_TO_START) {
- // We only allow end-to-start swiping, so the fling needs to be in the
- // correct direction
- return isRtl ? xvel > 0f : xvel < 0f;
- }
- } else {
- final int distance = child.getLeft() - mOriginalCapturedViewLeft;
- final int thresholdDistance = Math.round(child.getWidth() * mDragDismissThreshold);
- return Math.abs(distance) >= thresholdDistance;
- }
-
- return false;
- }
-
- @Override
- public int getViewHorizontalDragRange(View child) {
- return child.getWidth();
- }
-
- @Override
- public int clampViewPositionHorizontal(View child, int left, int dx) {
- final boolean isRtl = ViewCompat.getLayoutDirection(child)
- == ViewCompat.LAYOUT_DIRECTION_RTL;
- int min, max;
-
- if (mSwipeDirection == SWIPE_DIRECTION_START_TO_END) {
- if (isRtl) {
- min = mOriginalCapturedViewLeft - child.getWidth();
- max = mOriginalCapturedViewLeft;
- } else {
- min = mOriginalCapturedViewLeft;
- max = mOriginalCapturedViewLeft + child.getWidth();
- }
- } else if (mSwipeDirection == SWIPE_DIRECTION_END_TO_START) {
- if (isRtl) {
- min = mOriginalCapturedViewLeft;
- max = mOriginalCapturedViewLeft + child.getWidth();
- } else {
- min = mOriginalCapturedViewLeft - child.getWidth();
- max = mOriginalCapturedViewLeft;
- }
- } else {
- min = mOriginalCapturedViewLeft - child.getWidth();
- max = mOriginalCapturedViewLeft + child.getWidth();
- }
-
- return clamp(min, left, max);
- }
-
- @Override
- public int clampViewPositionVertical(View child, int top, int dy) {
- return child.getTop();
- }
-
- @Override
- public void onViewPositionChanged(View child, int left, int top, int dx, int dy) {
- final float startAlphaDistance = mOriginalCapturedViewLeft
- + child.getWidth() * mAlphaStartSwipeDistance;
- final float endAlphaDistance = mOriginalCapturedViewLeft
- + child.getWidth() * mAlphaEndSwipeDistance;
-
- if (left <= startAlphaDistance) {
- child.setAlpha(1f);
- } else if (left >= endAlphaDistance) {
- child.setAlpha(0f);
- } else {
- // We're between the start and end distances
- final float distance = fraction(startAlphaDistance, endAlphaDistance, left);
- child.setAlpha(clamp(0f, 1f - distance, 1f));
- }
- }
- };
-
- private void ensureViewDragHelper(ViewGroup parent) {
- if (mViewDragHelper == null) {
- mViewDragHelper = mSensitivitySet
- ? ViewDragHelper.create(parent, mSensitivity, mDragCallback)
- : ViewDragHelper.create(parent, mDragCallback);
- }
- }
-
- private class SettleRunnable implements Runnable {
- private final View mView;
- private final boolean mDismiss;
-
- SettleRunnable(View view, boolean dismiss) {
- mView = view;
- mDismiss = dismiss;
- }
-
- @Override
- public void run() {
- if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
- ViewCompat.postOnAnimation(mView, this);
- } else {
- if (mDismiss && mListener != null) {
- mListener.onDismiss(mView);
- }
- }
- }
- }
-
- static float clamp(float min, float value, float max) {
- return Math.min(Math.max(min, value), max);
- }
-
- static int clamp(int min, int value, int max) {
- return Math.min(Math.max(min, value), max);
- }
-
- /**
- * Retrieve the current drag state of this behavior. This will return one of
- * {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
- *
- * @return The current drag state
- */
- public int getDragState() {
- return mViewDragHelper != null ? mViewDragHelper.getViewDragState() : STATE_IDLE;
- }
-
- /**
- * The fraction that {@code value} is between {@code startValue} and {@code endValue}.
- */
- static float fraction(float startValue, float endValue, float value) {
- return (value - startValue) / (endValue - startValue);
- }
-} \ No newline at end of file
diff --git a/android/support/design/widget/TabItem.java b/android/support/design/widget/TabItem.java
deleted file mode 100644
index 09b01dbe..00000000
--- a/android/support/design/widget/TabItem.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2016 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.design.widget;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.support.design.R;
-import android.support.v7.widget.TintTypedArray;
-import android.util.AttributeSet;
-import android.view.View;
-
-/**
- * TabItem is a special 'view' which allows you to declare tab items for a {@link TabLayout}
- * within a layout. This view is not actually added to TabLayout, it is just a dummy which allows
- * setting of a tab items's text, icon and custom layout. See TabLayout for more information on how
- * to use it.
- *
- * @attr ref android.support.design.R.styleable#TabItem_android_icon
- * @attr ref android.support.design.R.styleable#TabItem_android_text
- * @attr ref android.support.design.R.styleable#TabItem_android_layout
- *
- * @see TabLayout
- */
-public final class TabItem extends View {
- final CharSequence mText;
- final Drawable mIcon;
- final int mCustomLayout;
-
- public TabItem(Context context) {
- this(context, null);
- }
-
- public TabItem(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
- R.styleable.TabItem);
- mText = a.getText(R.styleable.TabItem_android_text);
- mIcon = a.getDrawable(R.styleable.TabItem_android_icon);
- mCustomLayout = a.getResourceId(R.styleable.TabItem_android_layout, 0);
- a.recycle();
- }
-} \ No newline at end of file
diff --git a/android/support/design/widget/TabLayout.java b/android/support/design/widget/TabLayout.java
deleted file mode 100644
index 9b81465a..00000000
--- a/android/support/design/widget/TabLayout.java
+++ /dev/null
@@ -1,2217 +0,0 @@
-/*
- * 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.design.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.support.v4.view.ViewPager.SCROLL_STATE_DRAGGING;
-import static android.support.v4.view.ViewPager.SCROLL_STATE_IDLE;
-import static android.support.v4.view.ViewPager.SCROLL_STATE_SETTLING;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.ColorInt;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.IntDef;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StringRes;
-import android.support.design.R;
-import android.support.v4.util.Pools;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.PointerIconCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPager;
-import android.support.v4.widget.TextViewCompat;
-import android.support.v7.app.ActionBar;
-import android.support.v7.content.res.AppCompatResources;
-import android.support.v7.widget.TooltipCompat;
-import android.text.Layout;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.SoundEffectConstants;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.HorizontalScrollView;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Iterator;
-
-/**
- * TabLayout provides a horizontal layout to display tabs.
- *
- * <p>Population of the tabs to display is
- * done through {@link Tab} instances. You create tabs via {@link #newTab()}. From there you can
- * change the tab's label or icon via {@link Tab#setText(int)} and {@link Tab#setIcon(int)}
- * respectively. To display the tab, you need to add it to the layout via one of the
- * {@link #addTab(Tab)} methods. For example:
- * <pre>
- * TabLayout tabLayout = ...;
- * tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
- * tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
- * tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
- * </pre>
- * You should set a listener via {@link #setOnTabSelectedListener(OnTabSelectedListener)} to be
- * notified when any tab's selection state has been changed.
- *
- * <p>You can also add items to TabLayout in your layout through the use of {@link TabItem}.
- * An example usage is like so:</p>
- *
- * <pre>
- * &lt;android.support.design.widget.TabLayout
- * android:layout_height=&quot;wrap_content&quot;
- * android:layout_width=&quot;match_parent&quot;&gt;
- *
- * &lt;android.support.design.widget.TabItem
- * android:text=&quot;@string/tab_text&quot;/&gt;
- *
- * &lt;android.support.design.widget.TabItem
- * android:icon=&quot;@drawable/ic_android&quot;/&gt;
- *
- * &lt;/android.support.design.widget.TabLayout&gt;
- * </pre>
- *
- * <h3>ViewPager integration</h3>
- * <p>
- * If you're using a {@link android.support.v4.view.ViewPager} together
- * with this layout, you can call {@link #setupWithViewPager(ViewPager)} to link the two together.
- * This layout will be automatically populated from the {@link PagerAdapter}'s page titles.</p>
- *
- * <p>
- * This view also supports being used as part of a ViewPager's decor, and can be added
- * directly to the ViewPager in a layout resource file like so:</p>
- *
- * <pre>
- * &lt;android.support.v4.view.ViewPager
- * android:layout_width=&quot;match_parent&quot;
- * android:layout_height=&quot;match_parent&quot;&gt;
- *
- * &lt;android.support.design.widget.TabLayout
- * android:layout_width=&quot;match_parent&quot;
- * android:layout_height=&quot;wrap_content&quot;
- * android:layout_gravity=&quot;top&quot; /&gt;
- *
- * &lt;/android.support.v4.view.ViewPager&gt;
- * </pre>
- *
- * @see <a href="http://www.google.com/design/spec/components/tabs.html">Tabs</a>
- *
- * @attr ref android.support.design.R.styleable#TabLayout_tabPadding
- * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingStart
- * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingTop
- * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingEnd
- * @attr ref android.support.design.R.styleable#TabLayout_tabPaddingBottom
- * @attr ref android.support.design.R.styleable#TabLayout_tabContentStart
- * @attr ref android.support.design.R.styleable#TabLayout_tabBackground
- * @attr ref android.support.design.R.styleable#TabLayout_tabMinWidth
- * @attr ref android.support.design.R.styleable#TabLayout_tabMaxWidth
- * @attr ref android.support.design.R.styleable#TabLayout_tabTextAppearance
- */
-@ViewPager.DecorView
-public class TabLayout extends HorizontalScrollView {
-
- private static final int DEFAULT_HEIGHT_WITH_TEXT_ICON = 72; // dps
- static final int DEFAULT_GAP_TEXT_ICON = 8; // dps
- private static final int INVALID_WIDTH = -1;
- private static final int DEFAULT_HEIGHT = 48; // dps
- private static final int TAB_MIN_WIDTH_MARGIN = 56; //dps
- static final int FIXED_WRAP_GUTTER_MIN = 16; //dps
- static final int MOTION_NON_ADJACENT_OFFSET = 24;
-
- private static final int ANIMATION_DURATION = 300;
-
- private static final Pools.Pool<Tab> sTabPool = new Pools.SynchronizedPool<>(16);
-
- /**
- * Scrollable tabs display a subset of tabs at any given moment, and can contain longer tab
- * labels and a larger number of tabs. They are best used for browsing contexts in touch
- * interfaces when users don’t need to directly compare the tab labels.
- *
- * @see #setTabMode(int)
- * @see #getTabMode()
- */
- public static final int MODE_SCROLLABLE = 0;
-
- /**
- * Fixed tabs display all tabs concurrently and are best used with content that benefits from
- * quick pivots between tabs. The maximum number of tabs is limited by the view’s width.
- * Fixed tabs have equal width, based on the widest tab label.
- *
- * @see #setTabMode(int)
- * @see #getTabMode()
- */
- public static final int MODE_FIXED = 1;
-
- /**
- * @hide
- */
- @RestrictTo(LIBRARY_GROUP)
- @IntDef(value = {MODE_SCROLLABLE, MODE_FIXED})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Mode {}
-
- /**
- * Gravity used to fill the {@link TabLayout} as much as possible. This option only takes effect
- * when used with {@link #MODE_FIXED}.
- *
- * @see #setTabGravity(int)
- * @see #getTabGravity()
- */
- public static final int GRAVITY_FILL = 0;
-
- /**
- * Gravity used to lay out the tabs in the center of the {@link TabLayout}.
- *
- * @see #setTabGravity(int)
- * @see #getTabGravity()
- */
- public static final int GRAVITY_CENTER = 1;
-
- /**
- * @hide
- */
- @RestrictTo(LIBRARY_GROUP)
- @IntDef(flag = true, value = {GRAVITY_FILL, GRAVITY_CENTER})
- @Retention(RetentionPolicy.SOURCE)
- public @interface TabGravity {}
-
- /**
- * Callback interface invoked when a tab's selection state changes.
- */
- public interface OnTabSelectedListener {
-
- /**
- * Called when a tab enters the selected state.
- *
- * @param tab The tab that was selected
- */
- public void onTabSelected(Tab tab);
-
- /**
- * Called when a tab exits the selected state.
- *
- * @param tab The tab that was unselected
- */
- public void onTabUnselected(Tab tab);
-
- /**
- * Called when a tab that is already selected is chosen again by the user. Some applications
- * may use this action to return to the top level of a category.
- *
- * @param tab The tab that was reselected.
- */
- public void onTabReselected(Tab tab);
- }
-
- private final ArrayList<Tab> mTabs = new ArrayList<>();
- private Tab mSelectedTab;
-
- private final SlidingTabStrip mTabStrip;
-
- int mTabPaddingStart;
- int mTabPaddingTop;
- int mTabPaddingEnd;
- int mTabPaddingBottom;
-
- int mTabTextAppearance;
- ColorStateList mTabTextColors;
- float mTabTextSize;
- float mTabTextMultiLineSize;
-
- final int mTabBackgroundResId;
-
- int mTabMaxWidth = Integer.MAX_VALUE;
- private final int mRequestedTabMinWidth;
- private final int mRequestedTabMaxWidth;
- private final int mScrollableTabMinWidth;
-
- private int mContentInsetStart;
-
- int mTabGravity;
- int mMode;
-
- private OnTabSelectedListener mSelectedListener;
- private final ArrayList<OnTabSelectedListener> mSelectedListeners = new ArrayList<>();
- private OnTabSelectedListener mCurrentVpSelectedListener;
-
- private ValueAnimator mScrollAnimator;
-
- ViewPager mViewPager;
- private PagerAdapter mPagerAdapter;
- private DataSetObserver mPagerAdapterObserver;
- private TabLayoutOnPageChangeListener mPageChangeListener;
- private AdapterChangeListener mAdapterChangeListener;
- private boolean mSetupViewPagerImplicitly;
-
- // Pool we use as a simple RecyclerBin
- private final Pools.Pool<TabView> mTabViewPool = new Pools.SimplePool<>(12);
-
- public TabLayout(Context context) {
- this(context, null);
- }
-
- public TabLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- ThemeUtils.checkAppCompatTheme(context);
-
- // Disable the Scroll Bar
- setHorizontalScrollBarEnabled(false);
-
- // Add the TabStrip
- mTabStrip = new SlidingTabStrip(context);
- super.addView(mTabStrip, 0, new HorizontalScrollView.LayoutParams(
- LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabLayout,
- defStyleAttr, R.style.Widget_Design_TabLayout);
-
- mTabStrip.setSelectedIndicatorHeight(
- a.getDimensionPixelSize(R.styleable.TabLayout_tabIndicatorHeight, 0));
- mTabStrip.setSelectedIndicatorColor(a.getColor(R.styleable.TabLayout_tabIndicatorColor, 0));
-
- mTabPaddingStart = mTabPaddingTop = mTabPaddingEnd = mTabPaddingBottom = a
- .getDimensionPixelSize(R.styleable.TabLayout_tabPadding, 0);
- mTabPaddingStart = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingStart,
- mTabPaddingStart);
- mTabPaddingTop = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingTop,
- mTabPaddingTop);
- mTabPaddingEnd = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingEnd,
- mTabPaddingEnd);
- mTabPaddingBottom = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingBottom,
- mTabPaddingBottom);
-
- mTabTextAppearance = a.getResourceId(R.styleable.TabLayout_tabTextAppearance,
- R.style.TextAppearance_Design_Tab);
-
- // Text colors/sizes come from the text appearance first
- final TypedArray ta = context.obtainStyledAttributes(mTabTextAppearance,
- android.support.v7.appcompat.R.styleable.TextAppearance);
- try {
- mTabTextSize = ta.getDimensionPixelSize(
- android.support.v7.appcompat.R.styleable.TextAppearance_android_textSize, 0);
- mTabTextColors = ta.getColorStateList(
- android.support.v7.appcompat.R.styleable.TextAppearance_android_textColor);
- } finally {
- ta.recycle();
- }
-
- if (a.hasValue(R.styleable.TabLayout_tabTextColor)) {
- // If we have an explicit text color set, use it instead
- mTabTextColors = a.getColorStateList(R.styleable.TabLayout_tabTextColor);
- }
-
- if (a.hasValue(R.styleable.TabLayout_tabSelectedTextColor)) {
- // We have an explicit selected text color set, so we need to make merge it with the
- // current colors. This is exposed so that developers can use theme attributes to set
- // this (theme attrs in ColorStateLists are Lollipop+)
- final int selected = a.getColor(R.styleable.TabLayout_tabSelectedTextColor, 0);
- mTabTextColors = createColorStateList(mTabTextColors.getDefaultColor(), selected);
- }
-
- mRequestedTabMinWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMinWidth,
- INVALID_WIDTH);
- mRequestedTabMaxWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMaxWidth,
- INVALID_WIDTH);
- mTabBackgroundResId = a.getResourceId(R.styleable.TabLayout_tabBackground, 0);
- mContentInsetStart = a.getDimensionPixelSize(R.styleable.TabLayout_tabContentStart, 0);
- mMode = a.getInt(R.styleable.TabLayout_tabMode, MODE_FIXED);
- mTabGravity = a.getInt(R.styleable.TabLayout_tabGravity, GRAVITY_FILL);
- a.recycle();
-
- // TODO add attr for these
- final Resources res = getResources();
- mTabTextMultiLineSize = res.getDimensionPixelSize(R.dimen.design_tab_text_size_2line);
- mScrollableTabMinWidth = res.getDimensionPixelSize(R.dimen.design_tab_scrollable_min_width);
-
- // Now apply the tab mode and gravity
- applyModeAndGravity();
- }
-
- /**
- * Sets the tab indicator's color for the currently selected tab.
- *
- * @param color color to use for the indicator
- *
- * @attr ref android.support.design.R.styleable#TabLayout_tabIndicatorColor
- */
- public void setSelectedTabIndicatorColor(@ColorInt int color) {
- mTabStrip.setSelectedIndicatorColor(color);
- }
-
- /**
- * Sets the tab indicator's height for the currently selected tab.
- *
- * @param height height to use for the indicator in pixels
- *
- * @attr ref android.support.design.R.styleable#TabLayout_tabIndicatorHeight
- */
- public void setSelectedTabIndicatorHeight(int height) {
- mTabStrip.setSelectedIndicatorHeight(height);
- }
-
- /**
- * Set the scroll position of the tabs. This is useful for when the tabs are being displayed as
- * part of a scrolling container such as {@link android.support.v4.view.ViewPager}.
- * <p>
- * Calling this method does not update the selected tab, it is only used for drawing purposes.
- *
- * @param position current scroll position
- * @param positionOffset Value from [0, 1) indicating the offset from {@code position}.
- * @param updateSelectedText Whether to update the text's selected state.
- */
- public void setScrollPosition(int position, float positionOffset, boolean updateSelectedText) {
- setScrollPosition(position, positionOffset, updateSelectedText, true);
- }
-
- void setScrollPosition(int position, float positionOffset, boolean updateSelectedText,
- boolean updateIndicatorPosition) {
- final int roundedPosition = Math.round(position + positionOffset);
- if (roundedPosition < 0 || roundedPosition >= mTabStrip.getChildCount()) {
- return;
- }
-
- // Set the indicator position, if enabled
- if (updateIndicatorPosition) {
- mTabStrip.setIndicatorPositionFromTabPosition(position, positionOffset);
- }
-
- // Now update the scroll position, canceling any running animation
- if (mScrollAnimator != null && mScrollAnimator.isRunning()) {
- mScrollAnimator.cancel();
- }
- scrollTo(calculateScrollXForTab(position, positionOffset), 0);
-
- // Update the 'selected state' view as we scroll, if enabled
- if (updateSelectedText) {
- setSelectedTabView(roundedPosition);
- }
- }
-
- private float getScrollPosition() {
- return mTabStrip.getIndicatorPosition();
- }
-
- /**
- * Add a tab to this layout. The tab will be added at the end of the list.
- * If this is the first tab to be added it will become the selected tab.
- *
- * @param tab Tab to add
- */
- public void addTab(@NonNull Tab tab) {
- addTab(tab, mTabs.isEmpty());
- }
-
- /**
- * Add a tab to this layout. The tab will be inserted at <code>position</code>.
- * If this is the first tab to be added it will become the selected tab.
- *
- * @param tab The tab to add
- * @param position The new position of the tab
- */
- public void addTab(@NonNull Tab tab, int position) {
- addTab(tab, position, mTabs.isEmpty());
- }
-
- /**
- * Add a tab to this layout. The tab will be added at the end of the list.
- *
- * @param tab Tab to add
- * @param setSelected True if the added tab should become the selected tab.
- */
- public void addTab(@NonNull Tab tab, boolean setSelected) {
- addTab(tab, mTabs.size(), setSelected);
- }
-
- /**
- * Add a tab to this layout. The tab will be inserted at <code>position</code>.
- *
- * @param tab The tab to add
- * @param position The new position of the tab
- * @param setSelected True if the added tab should become the selected tab.
- */
- public void addTab(@NonNull Tab tab, int position, boolean setSelected) {
- if (tab.mParent != this) {
- throw new IllegalArgumentException("Tab belongs to a different TabLayout.");
- }
- configureTab(tab, position);
- addTabView(tab);
-
- if (setSelected) {
- tab.select();
- }
- }
-
- private void addTabFromItemView(@NonNull TabItem item) {
- final Tab tab = newTab();
- if (item.mText != null) {
- tab.setText(item.mText);
- }
- if (item.mIcon != null) {
- tab.setIcon(item.mIcon);
- }
- if (item.mCustomLayout != 0) {
- tab.setCustomView(item.mCustomLayout);
- }
- if (!TextUtils.isEmpty(item.getContentDescription())) {
- tab.setContentDescription(item.getContentDescription());
- }
- addTab(tab);
- }
-
- /**
- * @deprecated Use {@link #addOnTabSelectedListener(OnTabSelectedListener)} and
- * {@link #removeOnTabSelectedListener(OnTabSelectedListener)}.
- */
- @Deprecated
- public void setOnTabSelectedListener(@Nullable OnTabSelectedListener listener) {
- // The logic in this method emulates what we had before support for multiple
- // registered listeners.
- if (mSelectedListener != null) {
- removeOnTabSelectedListener(mSelectedListener);
- }
- // Update the deprecated field so that we can remove the passed listener the next
- // time we're called
- mSelectedListener = listener;
- if (listener != null) {
- addOnTabSelectedListener(listener);
- }
- }
-
- /**
- * Add a {@link TabLayout.OnTabSelectedListener} that will be invoked when tab selection
- * changes.
- *
- * <p>Components that add a listener should take care to remove it when finished via
- * {@link #removeOnTabSelectedListener(OnTabSelectedListener)}.</p>
- *
- * @param listener listener to add
- */
- public void addOnTabSelectedListener(@NonNull OnTabSelectedListener listener) {
- if (!mSelectedListeners.contains(listener)) {
- mSelectedListeners.add(listener);
- }
- }
-
- /**
- * Remove the given {@link TabLayout.OnTabSelectedListener} that was previously added via
- * {@link #addOnTabSelectedListener(OnTabSelectedListener)}.
- *
- * @param listener listener to remove
- */
- public void removeOnTabSelectedListener(@NonNull OnTabSelectedListener listener) {
- mSelectedListeners.remove(listener);
- }
-
- /**
- * Remove all previously added {@link TabLayout.OnTabSelectedListener}s.
- */
- public void clearOnTabSelectedListeners() {
- mSelectedListeners.clear();
- }
-
- /**
- * Create and return a new {@link Tab}. You need to manually add this using
- * {@link #addTab(Tab)} or a related method.
- *
- * @return A new Tab
- * @see #addTab(Tab)
- */
- @NonNull
- public Tab newTab() {
- Tab tab = sTabPool.acquire();
- if (tab == null) {
- tab = new Tab();
- }
- tab.mParent = this;
- tab.mView = createTabView(tab);
- return tab;
- }
-
- /**
- * Returns the number of tabs currently registered with the action bar.
- *
- * @return Tab count
- */
- public int getTabCount() {
- return mTabs.size();
- }
-
- /**
- * Returns the tab at the specified index.
- */
- @Nullable
- public Tab getTabAt(int index) {
- return (index < 0 || index >= getTabCount()) ? null : mTabs.get(index);
- }
-
- /**
- * Returns the position of the current selected tab.
- *
- * @return selected tab position, or {@code -1} if there isn't a selected tab.
- */
- public int getSelectedTabPosition() {
- return mSelectedTab != null ? mSelectedTab.getPosition() : -1;
- }
-
- /**
- * Remove a tab from the layout. If the removed tab was selected it will be deselected
- * and another tab will be selected if present.
- *
- * @param tab The tab to remove
- */
- public void removeTab(Tab tab) {
- if (tab.mParent != this) {
- throw new IllegalArgumentException("Tab does not belong to this TabLayout.");
- }
-
- removeTabAt(tab.getPosition());
- }
-
- /**
- * Remove a tab from the layout. If the removed tab was selected it will be deselected
- * and another tab will be selected if present.
- *
- * @param position Position of the tab to remove
- */
- public void removeTabAt(int position) {
- final int selectedTabPosition = mSelectedTab != null ? mSelectedTab.getPosition() : 0;
- removeTabViewAt(position);
-
- final Tab removedTab = mTabs.remove(position);
- if (removedTab != null) {
- removedTab.reset();
- sTabPool.release(removedTab);
- }
-
- final int newTabCount = mTabs.size();
- for (int i = position; i < newTabCount; i++) {
- mTabs.get(i).setPosition(i);
- }
-
- if (selectedTabPosition == position) {
- selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
- }
- }
-
- /**
- * Remove all tabs from the action bar and deselect the current tab.
- */
- public void removeAllTabs() {
- // Remove all the views
- for (int i = mTabStrip.getChildCount() - 1; i >= 0; i--) {
- removeTabViewAt(i);
- }
-
- for (final Iterator<Tab> i = mTabs.iterator(); i.hasNext();) {
- final Tab tab = i.next();
- i.remove();
- tab.reset();
- sTabPool.release(tab);
- }
-
- mSelectedTab = null;
- }
-
- /**
- * Set the behavior mode for the Tabs in this layout. The valid input options are:
- * <ul>
- * <li>{@link #MODE_FIXED}: Fixed tabs display all tabs concurrently and are best used
- * with content that benefits from quick pivots between tabs.</li>
- * <li>{@link #MODE_SCROLLABLE}: Scrollable tabs display a subset of tabs at any given moment,
- * and can contain longer tab labels and a larger number of tabs. They are best used for
- * browsing contexts in touch interfaces when users don’t need to directly compare the tab
- * labels. This mode is commonly used with a {@link android.support.v4.view.ViewPager}.</li>
- * </ul>
- *
- * @param mode one of {@link #MODE_FIXED} or {@link #MODE_SCROLLABLE}.
- *
- * @attr ref android.support.design.R.styleable#TabLayout_tabMode
- */
- public void setTabMode(@Mode int mode) {
- if (mode != mMode) {
- mMode = mode;
- applyModeAndGravity();
- }
- }
-
- /**
- * Returns the current mode used by this {@link TabLayout}.
- *
- * @see #setTabMode(int)
- */
- @Mode
- public int getTabMode() {
- return mMode;
- }
-
- /**
- * Set the gravity to use when laying out the tabs.
- *
- * @param gravity one of {@link #GRAVITY_CENTER} or {@link #GRAVITY_FILL}.
- *
- * @attr ref android.support.design.R.styleable#TabLayout_tabGravity
- */
- public void setTabGravity(@TabGravity int gravity) {
- if (mTabGravity != gravity) {
- mTabGravity = gravity;
- applyModeAndGravity();
- }
- }
-
- /**
- * The current gravity used for laying out tabs.
- *
- * @return one of {@link #GRAVITY_CENTER} or {@link #GRAVITY_FILL}.
- */
- @TabGravity
- public int getTabGravity() {
- return mTabGravity;
- }
-
- /**
- * Sets the text colors for the different states (normal, selected) used for the tabs.
- *
- * @see #getTabTextColors()
- */
- public void setTabTextColors(@Nullable ColorStateList textColor) {
- if (mTabTextColors != textColor) {
- mTabTextColors = textColor;
- updateAllTabs();
- }
- }
-
- /**
- * Gets the text colors for the different states (normal, selected) used for the tabs.
- */
- @Nullable
- public ColorStateList getTabTextColors() {
- return mTabTextColors;
- }
-
- /**
- * Sets the text colors for the different states (normal, selected) used for the tabs.
- *
- * @attr ref android.support.design.R.styleable#TabLayout_tabTextColor
- * @attr ref android.support.design.R.styleable#TabLayout_tabSelectedTextColor
- */
- public void setTabTextColors(int normalColor, int selectedColor) {
- setTabTextColors(createColorStateList(normalColor, selectedColor));
- }
-
- /**
- * The one-stop shop for setting up this {@link TabLayout} with a {@link ViewPager}.
- *
- * <p>This is the same as calling {@link #setupWithViewPager(ViewPager, boolean)} with
- * auto-refresh enabled.</p>
- *
- * @param viewPager the ViewPager to link to, or {@code null} to clear any previous link
- */
- public void setupWithViewPager(@Nullable ViewPager viewPager) {
- setupWithViewPager(viewPager, true);
- }
-
- /**
- * The one-stop shop for setting up this {@link TabLayout} with a {@link ViewPager}.
- *
- * <p>This method will link the given ViewPager and this TabLayout together so that
- * changes in one are automatically reflected in the other. This includes scroll state changes
- * and clicks. The tabs displayed in this layout will be populated
- * from the ViewPager adapter's page titles.</p>
- *
- * <p>If {@code autoRefresh} is {@code true}, any changes in the {@link PagerAdapter} will
- * trigger this layout to re-populate itself from the adapter's titles.</p>
- *
- * <p>If the given ViewPager is non-null, it needs to already have a
- * {@link PagerAdapter} set.</p>
- *
- * @param viewPager the ViewPager to link to, or {@code null} to clear any previous link
- * @param autoRefresh whether this layout should refresh its contents if the given ViewPager's
- * content changes
- */
- public void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh) {
- setupWithViewPager(viewPager, autoRefresh, false);
- }
-
- private void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh,
- boolean implicitSetup) {
- if (mViewPager != null) {
- // If we've already been setup with a ViewPager, remove us from it
- if (mPageChangeListener != null) {
- mViewPager.removeOnPageChangeListener(mPageChangeListener);
- }
- if (mAdapterChangeListener != null) {
- mViewPager.removeOnAdapterChangeListener(mAdapterChangeListener);
- }
- }
-
- if (mCurrentVpSelectedListener != null) {
- // If we already have a tab selected listener for the ViewPager, remove it
- removeOnTabSelectedListener(mCurrentVpSelectedListener);
- mCurrentVpSelectedListener = null;
- }
-
- if (viewPager != null) {
- mViewPager = viewPager;
-
- // Add our custom OnPageChangeListener to the ViewPager
- if (mPageChangeListener == null) {
- mPageChangeListener = new TabLayoutOnPageChangeListener(this);
- }
- mPageChangeListener.reset();
- viewPager.addOnPageChangeListener(mPageChangeListener);
-
- // Now we'll add a tab selected listener to set ViewPager's current item
- mCurrentVpSelectedListener = new ViewPagerOnTabSelectedListener(viewPager);
- addOnTabSelectedListener(mCurrentVpSelectedListener);
-
- final PagerAdapter adapter = viewPager.getAdapter();
- if (adapter != null) {
- // Now we'll populate ourselves from the pager adapter, adding an observer if
- // autoRefresh is enabled
- setPagerAdapter(adapter, autoRefresh);
- }
-
- // Add a listener so that we're notified of any adapter changes
- if (mAdapterChangeListener == null) {
- mAdapterChangeListener = new AdapterChangeListener();
- }
- mAdapterChangeListener.setAutoRefresh(autoRefresh);
- viewPager.addOnAdapterChangeListener(mAdapterChangeListener);
-
- // Now update the scroll position to match the ViewPager's current item
- setScrollPosition(viewPager.getCurrentItem(), 0f, true);
- } else {
- // We've been given a null ViewPager so we need to clear out the internal state,
- // listeners and observers
- mViewPager = null;
- setPagerAdapter(null, false);
- }
-
- mSetupViewPagerImplicitly = implicitSetup;
- }
-
- /**
- * @deprecated Use {@link #setupWithViewPager(ViewPager)} to link a TabLayout with a ViewPager
- * together. When that method is used, the TabLayout will be automatically updated
- * when the {@link PagerAdapter} is changed.
- */
- @Deprecated
- public void setTabsFromPagerAdapter(@Nullable final PagerAdapter adapter) {
- setPagerAdapter(adapter, false);
- }
-
- @Override
- public boolean shouldDelayChildPressedState() {
- // Only delay the pressed state if the tabs can scroll
- return getTabScrollRange() > 0;
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- if (mViewPager == null) {
- // If we don't have a ViewPager already, check if our parent is a ViewPager to
- // setup with it automatically
- final ViewParent vp = getParent();
- if (vp instanceof ViewPager) {
- // If we have a ViewPager parent and we've been added as part of its decor, let's
- // assume that we should automatically setup to display any titles
- setupWithViewPager((ViewPager) vp, true, true);
- }
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- if (mSetupViewPagerImplicitly) {
- // If we've been setup with a ViewPager implicitly, let's clear out any listeners, etc
- setupWithViewPager(null);
- mSetupViewPagerImplicitly = false;
- }
- }
-
- private int getTabScrollRange() {
- return Math.max(0, mTabStrip.getWidth() - getWidth() - getPaddingLeft()
- - getPaddingRight());
- }
-
- void setPagerAdapter(@Nullable final PagerAdapter adapter, final boolean addObserver) {
- if (mPagerAdapter != null && mPagerAdapterObserver != null) {
- // If we already have a PagerAdapter, unregister our observer
- mPagerAdapter.unregisterDataSetObserver(mPagerAdapterObserver);
- }
-
- mPagerAdapter = adapter;
-
- if (addObserver && adapter != null) {
- // Register our observer on the new adapter
- if (mPagerAdapterObserver == null) {
- mPagerAdapterObserver = new PagerAdapterObserver();
- }
- adapter.registerDataSetObserver(mPagerAdapterObserver);
- }
-
- // Finally make sure we reflect the new adapter
- populateFromPagerAdapter();
- }
-
- void populateFromPagerAdapter() {
- removeAllTabs();
-
- if (mPagerAdapter != null) {
- final int adapterCount = mPagerAdapter.getCount();
- for (int i = 0; i < adapterCount; i++) {
- addTab(newTab().setText(mPagerAdapter.getPageTitle(i)), false);
- }
-
- // Make sure we reflect the currently set ViewPager item
- if (mViewPager != null && adapterCount > 0) {
- final int curItem = mViewPager.getCurrentItem();
- if (curItem != getSelectedTabPosition() && curItem < getTabCount()) {
- selectTab(getTabAt(curItem));
- }
- }
- }
- }
-
- private void updateAllTabs() {
- for (int i = 0, z = mTabs.size(); i < z; i++) {
- mTabs.get(i).updateView();
- }
- }
-
- private TabView createTabView(@NonNull final Tab tab) {
- TabView tabView = mTabViewPool != null ? mTabViewPool.acquire() : null;
- if (tabView == null) {
- tabView = new TabView(getContext());
- }
- tabView.setTab(tab);
- tabView.setFocusable(true);
- tabView.setMinimumWidth(getTabMinWidth());
- return tabView;
- }
-
- private void configureTab(Tab tab, int position) {
- tab.setPosition(position);
- mTabs.add(position, tab);
-
- final int count = mTabs.size();
- for (int i = position + 1; i < count; i++) {
- mTabs.get(i).setPosition(i);
- }
- }
-
- private void addTabView(Tab tab) {
- final TabView tabView = tab.mView;
- mTabStrip.addView(tabView, tab.getPosition(), createLayoutParamsForTabs());
- }
-
- @Override
- public void addView(View child) {
- addViewInternal(child);
- }
-
- @Override
- public void addView(View child, int index) {
- addViewInternal(child);
- }
-
- @Override
- public void addView(View child, ViewGroup.LayoutParams params) {
- addViewInternal(child);
- }
-
- @Override
- public void addView(View child, int index, ViewGroup.LayoutParams params) {
- addViewInternal(child);
- }
-
- private void addViewInternal(final View child) {
- if (child instanceof TabItem) {
- addTabFromItemView((TabItem) child);
- } else {
- throw new IllegalArgumentException("Only TabItem instances can be added to TabLayout");
- }
- }
-
- private LinearLayout.LayoutParams createLayoutParamsForTabs() {
- final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
- updateTabViewLayoutParams(lp);
- return lp;
- }
-
- private void updateTabViewLayoutParams(LinearLayout.LayoutParams lp) {
- if (mMode == MODE_FIXED && mTabGravity == GRAVITY_FILL) {
- lp.width = 0;
- lp.weight = 1;
- } else {
- lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
- lp.weight = 0;
- }
- }
-
- int dpToPx(int dps) {
- return Math.round(getResources().getDisplayMetrics().density * dps);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // If we have a MeasureSpec which allows us to decide our height, try and use the default
- // height
- final int idealHeight = dpToPx(getDefaultHeight()) + getPaddingTop() + getPaddingBottom();
- switch (MeasureSpec.getMode(heightMeasureSpec)) {
- case MeasureSpec.AT_MOST:
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(
- Math.min(idealHeight, MeasureSpec.getSize(heightMeasureSpec)),
- MeasureSpec.EXACTLY);
- break;
- case MeasureSpec.UNSPECIFIED:
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(idealHeight, MeasureSpec.EXACTLY);
- break;
- }
-
- final int specWidth = MeasureSpec.getSize(widthMeasureSpec);
- if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
- // If we don't have an unspecified width spec, use the given size to calculate
- // the max tab width
- mTabMaxWidth = mRequestedTabMaxWidth > 0
- ? mRequestedTabMaxWidth
- : specWidth - dpToPx(TAB_MIN_WIDTH_MARGIN);
- }
-
- // Now super measure itself using the (possibly) modified height spec
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- if (getChildCount() == 1) {
- // If we're in fixed mode then we need to make the tab strip is the same width as us
- // so we don't scroll
- final View child = getChildAt(0);
- boolean remeasure = false;
-
- switch (mMode) {
- case MODE_SCROLLABLE:
- // We only need to resize the child if it's smaller than us. This is similar
- // to fillViewport
- remeasure = child.getMeasuredWidth() < getMeasuredWidth();
- break;
- case MODE_FIXED:
- // Resize the child so that it doesn't scroll
- remeasure = child.getMeasuredWidth() != getMeasuredWidth();
- break;
- }
-
- if (remeasure) {
- // Re-measure the child with a widthSpec set to be exactly our measure width
- int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop()
- + getPaddingBottom(), child.getLayoutParams().height);
- int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
- getMeasuredWidth(), MeasureSpec.EXACTLY);
- child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
- }
- }
- }
-
- private void removeTabViewAt(int position) {
- final TabView view = (TabView) mTabStrip.getChildAt(position);
- mTabStrip.removeViewAt(position);
- if (view != null) {
- view.reset();
- mTabViewPool.release(view);
- }
- requestLayout();
- }
-
- private void animateToTab(int newPosition) {
- if (newPosition == Tab.INVALID_POSITION) {
- return;
- }
-
- if (getWindowToken() == null || !ViewCompat.isLaidOut(this)
- || mTabStrip.childrenNeedLayout()) {
- // If we don't have a window token, or we haven't been laid out yet just draw the new
- // position now
- setScrollPosition(newPosition, 0f, true);
- return;
- }
-
- final int startScrollX = getScrollX();
- final int targetScrollX = calculateScrollXForTab(newPosition, 0);
-
- if (startScrollX != targetScrollX) {
- ensureScrollAnimator();
-
- mScrollAnimator.setIntValues(startScrollX, targetScrollX);
- mScrollAnimator.start();
- }
-
- // Now animate the indicator
- mTabStrip.animateIndicatorToPosition(newPosition, ANIMATION_DURATION);
- }
-
- private void ensureScrollAnimator() {
- if (mScrollAnimator == null) {
- mScrollAnimator = new ValueAnimator();
- mScrollAnimator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
- mScrollAnimator.setDuration(ANIMATION_DURATION);
- mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- scrollTo((int) animator.getAnimatedValue(), 0);
- }
- });
- }
- }
-
- void setScrollAnimatorListener(Animator.AnimatorListener listener) {
- ensureScrollAnimator();
- mScrollAnimator.addListener(listener);
- }
-
- private void setSelectedTabView(int position) {
- final int tabCount = mTabStrip.getChildCount();
- if (position < tabCount) {
- for (int i = 0; i < tabCount; i++) {
- final View child = mTabStrip.getChildAt(i);
- child.setSelected(i == position);
- }
- }
- }
-
- void selectTab(Tab tab) {
- selectTab(tab, true);
- }
-
- void selectTab(final Tab tab, boolean updateIndicator) {
- final Tab currentTab = mSelectedTab;
-
- if (currentTab == tab) {
- if (currentTab != null) {
- dispatchTabReselected(tab);
- animateToTab(tab.getPosition());
- }
- } else {
- final int newPosition = tab != null ? tab.getPosition() : Tab.INVALID_POSITION;
- if (updateIndicator) {
- if ((currentTab == null || currentTab.getPosition() == Tab.INVALID_POSITION)
- && newPosition != Tab.INVALID_POSITION) {
- // If we don't currently have a tab, just draw the indicator
- setScrollPosition(newPosition, 0f, true);
- } else {
- animateToTab(newPosition);
- }
- if (newPosition != Tab.INVALID_POSITION) {
- setSelectedTabView(newPosition);
- }
- }
- if (currentTab != null) {
- dispatchTabUnselected(currentTab);
- }
- mSelectedTab = tab;
- if (tab != null) {
- dispatchTabSelected(tab);
- }
- }
- }
-
- private void dispatchTabSelected(@NonNull final Tab tab) {
- for (int i = mSelectedListeners.size() - 1; i >= 0; i--) {
- mSelectedListeners.get(i).onTabSelected(tab);
- }
- }
-
- private void dispatchTabUnselected(@NonNull final Tab tab) {
- for (int i = mSelectedListeners.size() - 1; i >= 0; i--) {
- mSelectedListeners.get(i).onTabUnselected(tab);
- }
- }
-
- private void dispatchTabReselected(@NonNull final Tab tab) {
- for (int i = mSelectedListeners.size() - 1; i >= 0; i--) {
- mSelectedListeners.get(i).onTabReselected(tab);
- }
- }
-
- private int calculateScrollXForTab(int position, float positionOffset) {
- if (mMode == MODE_SCROLLABLE) {
- final View selectedChild = mTabStrip.getChildAt(position);
- final View nextChild = position + 1 < mTabStrip.getChildCount()
- ? mTabStrip.getChildAt(position + 1)
- : null;
- final int selectedWidth = selectedChild != null ? selectedChild.getWidth() : 0;
- final int nextWidth = nextChild != null ? nextChild.getWidth() : 0;
-
- // base scroll amount: places center of tab in center of parent
- int scrollBase = selectedChild.getLeft() + (selectedWidth / 2) - (getWidth() / 2);
- // offset amount: fraction of the distance between centers of tabs
- int scrollOffset = (int) ((selectedWidth + nextWidth) * 0.5f * positionOffset);
-
- return (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_LTR)
- ? scrollBase + scrollOffset
- : scrollBase - scrollOffset;
- }
- return 0;
- }
-
- private void applyModeAndGravity() {
- int paddingStart = 0;
- if (mMode == MODE_SCROLLABLE) {
- // If we're scrollable, or fixed at start, inset using padding
- paddingStart = Math.max(0, mContentInsetStart - mTabPaddingStart);
- }
- ViewCompat.setPaddingRelative(mTabStrip, paddingStart, 0, 0, 0);
-
- switch (mMode) {
- case MODE_FIXED:
- mTabStrip.setGravity(Gravity.CENTER_HORIZONTAL);
- break;
- case MODE_SCROLLABLE:
- mTabStrip.setGravity(GravityCompat.START);
- break;
- }
-
- updateTabViews(true);
- }
-
- void updateTabViews(final boolean requestLayout) {
- for (int i = 0; i < mTabStrip.getChildCount(); i++) {
- View child = mTabStrip.getChildAt(i);
- child.setMinimumWidth(getTabMinWidth());
- updateTabViewLayoutParams((LinearLayout.LayoutParams) child.getLayoutParams());
- if (requestLayout) {
- child.requestLayout();
- }
- }
- }
-
- /**
- * A tab in this layout. Instances can be created via {@link #newTab()}.
- */
- public static final class Tab {
-
- /**
- * An invalid position for a tab.
- *
- * @see #getPosition()
- */
- public static final int INVALID_POSITION = -1;
-
- private Object mTag;
- private Drawable mIcon;
- private CharSequence mText;
- private CharSequence mContentDesc;
- private int mPosition = INVALID_POSITION;
- private View mCustomView;
-
- TabLayout mParent;
- TabView mView;
-
- Tab() {
- // Private constructor
- }
-
- /**
- * @return This Tab's tag object.
- */
- @Nullable
- public Object getTag() {
- return mTag;
- }
-
- /**
- * Give this Tab an arbitrary object to hold for later use.
- *
- * @param tag Object to store
- * @return The current instance for call chaining
- */
- @NonNull
- public Tab setTag(@Nullable Object tag) {
- mTag = tag;
- return this;
- }
-
-
- /**
- * Returns the custom view used for this tab.
- *
- * @see #setCustomView(View)
- * @see #setCustomView(int)
- */
- @Nullable
- public View getCustomView() {
- return mCustomView;
- }
-
- /**
- * Set a custom view to be used for this tab.
- * <p>
- * If the provided view contains a {@link TextView} with an ID of
- * {@link android.R.id#text1} then that will be updated with the value given
- * to {@link #setText(CharSequence)}. Similarly, if this layout contains an
- * {@link ImageView} with ID {@link android.R.id#icon} then it will be updated with
- * the value given to {@link #setIcon(Drawable)}.
- * </p>
- *
- * @param view Custom view to be used as a tab.
- * @return The current instance for call chaining
- */
- @NonNull
- public Tab setCustomView(@Nullable View view) {
- mCustomView = view;
- updateView();
- return this;
- }
-
- /**
- * Set a custom view to be used for this tab.
- * <p>
- * If the inflated layout contains a {@link TextView} with an ID of
- * {@link android.R.id#text1} then that will be updated with the value given
- * to {@link #setText(CharSequence)}. Similarly, if this layout contains an
- * {@link ImageView} with ID {@link android.R.id#icon} then it will be updated with
- * the value given to {@link #setIcon(Drawable)}.
- * </p>
- *
- * @param resId A layout resource to inflate and use as a custom tab view
- * @return The current instance for call chaining
- */
- @NonNull
- public Tab setCustomView(@LayoutRes int resId) {
- final LayoutInflater inflater = LayoutInflater.from(mView.getContext());
- return setCustomView(inflater.inflate(resId, mView, false));
- }
-
- /**
- * Return the icon associated with this tab.
- *
- * @return The tab's icon
- */
- @Nullable
- public Drawable getIcon() {
- return mIcon;
- }
-
- /**
- * Return the current position of this tab in the action bar.
- *
- * @return Current position, or {@link #INVALID_POSITION} if this tab is not currently in
- * the action bar.
- */
- public int getPosition() {
- return mPosition;
- }
-
- void setPosition(int position) {
- mPosition = position;
- }
-
- /**
- * Return the text of this tab.
- *
- * @return The tab's text
- */
- @Nullable
- public CharSequence getText() {
- return mText;
- }
-
- /**
- * Set the icon displayed on this tab.
- *
- * @param icon The drawable to use as an icon
- * @return The current instance for call chaining
- */
- @NonNull
- public Tab setIcon(@Nullable Drawable icon) {
- mIcon = icon;
- updateView();
- return this;
- }
-
- /**
- * Set the icon displayed on this tab.
- *
- * @param resId A resource ID referring to the icon that should be displayed
- * @return The current instance for call chaining
- */
- @NonNull
- public Tab setIcon(@DrawableRes int resId) {
- if (mParent == null) {
- throw new IllegalArgumentException("Tab not attached to a TabLayout");
- }
- return setIcon(AppCompatResources.getDrawable(mParent.getContext(), resId));
- }
-
- /**
- * Set the text displayed on this tab. Text may be truncated if there is not room to display
- * the entire string.
- *
- * @param text The text to display
- * @return The current instance for call chaining
- */
- @NonNull
- public Tab setText(@Nullable CharSequence text) {
- mText = text;
- updateView();
- return this;
- }
-
- /**
- * Set the text displayed on this tab. Text may be truncated if there is not room to display
- * the entire string.
- *
- * @param resId A resource ID referring to the text that should be displayed
- * @return The current instance for call chaining
- */
- @NonNull
- public Tab setText(@StringRes int resId) {
- if (mParent == null) {
- throw new IllegalArgumentException("Tab not attached to a TabLayout");
- }
- return setText(mParent.getResources().getText(resId));
- }
-
- /**
- * Select this tab. Only valid if the tab has been added to the action bar.
- */
- public void select() {
- if (mParent == null) {
- throw new IllegalArgumentException("Tab not attached to a TabLayout");
- }
- mParent.selectTab(this);
- }
-
- /**
- * Returns true if this tab is currently selected.
- */
- public boolean isSelected() {
- if (mParent == null) {
- throw new IllegalArgumentException("Tab not attached to a TabLayout");
- }
- return mParent.getSelectedTabPosition() == mPosition;
- }
-
- /**
- * Set a description of this tab's content for use in accessibility support. If no content
- * description is provided the title will be used.
- *
- * @param resId A resource ID referring to the description text
- * @return The current instance for call chaining
- * @see #setContentDescription(CharSequence)
- * @see #getContentDescription()
- */
- @NonNull
- public Tab setContentDescription(@StringRes int resId) {
- if (mParent == null) {
- throw new IllegalArgumentException("Tab not attached to a TabLayout");
- }
- return setContentDescription(mParent.getResources().getText(resId));
- }
-
- /**
- * Set a description of this tab's content for use in accessibility support. If no content
- * description is provided the title will be used.
- *
- * @param contentDesc Description of this tab's content
- * @return The current instance for call chaining
- * @see #setContentDescription(int)
- * @see #getContentDescription()
- */
- @NonNull
- public Tab setContentDescription(@Nullable CharSequence contentDesc) {
- mContentDesc = contentDesc;
- updateView();
- return this;
- }
-
- /**
- * Gets a brief description of this tab's content for use in accessibility support.
- *
- * @return Description of this tab's content
- * @see #setContentDescription(CharSequence)
- * @see #setContentDescription(int)
- */
- @Nullable
- public CharSequence getContentDescription() {
- return mContentDesc;
- }
-
- void updateView() {
- if (mView != null) {
- mView.update();
- }
- }
-
- void reset() {
- mParent = null;
- mView = null;
- mTag = null;
- mIcon = null;
- mText = null;
- mContentDesc = null;
- mPosition = INVALID_POSITION;
- mCustomView = null;
- }
- }
-
- class TabView extends LinearLayout {
- private Tab mTab;
- private TextView mTextView;
- private ImageView mIconView;
-
- private View mCustomView;
- private TextView mCustomTextView;
- private ImageView mCustomIconView;
-
- private int mDefaultMaxLines = 2;
-
- public TabView(Context context) {
- super(context);
- if (mTabBackgroundResId != 0) {
- ViewCompat.setBackground(
- this, AppCompatResources.getDrawable(context, mTabBackgroundResId));
- }
- ViewCompat.setPaddingRelative(this, mTabPaddingStart, mTabPaddingTop,
- mTabPaddingEnd, mTabPaddingBottom);
- setGravity(Gravity.CENTER);
- setOrientation(VERTICAL);
- setClickable(true);
- ViewCompat.setPointerIcon(this,
- PointerIconCompat.getSystemIcon(getContext(), PointerIconCompat.TYPE_HAND));
- }
-
- @Override
- public boolean performClick() {
- final boolean handled = super.performClick();
-
- if (mTab != null) {
- if (!handled) {
- playSoundEffect(SoundEffectConstants.CLICK);
- }
- mTab.select();
- return true;
- } else {
- return handled;
- }
- }
-
- @Override
- public void setSelected(final boolean selected) {
- final boolean changed = isSelected() != selected;
-
- super.setSelected(selected);
-
- if (changed && selected && Build.VERSION.SDK_INT < 16) {
- // Pre-JB we need to manually send the TYPE_VIEW_SELECTED event
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
- }
-
- // Always dispatch this to the child views, regardless of whether the value has
- // changed
- if (mTextView != null) {
- mTextView.setSelected(selected);
- }
- if (mIconView != null) {
- mIconView.setSelected(selected);
- }
- if (mCustomView != null) {
- mCustomView.setSelected(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);
- // This view masquerades as an action bar tab.
- info.setClassName(ActionBar.Tab.class.getName());
- }
-
- @Override
- public void onMeasure(final int origWidthMeasureSpec, final int origHeightMeasureSpec) {
- final int specWidthSize = MeasureSpec.getSize(origWidthMeasureSpec);
- final int specWidthMode = MeasureSpec.getMode(origWidthMeasureSpec);
- final int maxWidth = getTabMaxWidth();
-
- final int widthMeasureSpec;
- final int heightMeasureSpec = origHeightMeasureSpec;
-
- if (maxWidth > 0 && (specWidthMode == MeasureSpec.UNSPECIFIED
- || specWidthSize > maxWidth)) {
- // If we have a max width and a given spec which is either unspecified or
- // larger than the max width, update the width spec using the same mode
- widthMeasureSpec = MeasureSpec.makeMeasureSpec(mTabMaxWidth, MeasureSpec.AT_MOST);
- } else {
- // Else, use the original width spec
- widthMeasureSpec = origWidthMeasureSpec;
- }
-
- // Now lets measure
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- // We need to switch the text size based on whether the text is spanning 2 lines or not
- if (mTextView != null) {
- final Resources res = getResources();
- float textSize = mTabTextSize;
- int maxLines = mDefaultMaxLines;
-
- if (mIconView != null && mIconView.getVisibility() == VISIBLE) {
- // If the icon view is being displayed, we limit the text to 1 line
- maxLines = 1;
- } else if (mTextView != null && mTextView.getLineCount() > 1) {
- // Otherwise when we have text which wraps we reduce the text size
- textSize = mTabTextMultiLineSize;
- }
-
- final float curTextSize = mTextView.getTextSize();
- final int curLineCount = mTextView.getLineCount();
- final int curMaxLines = TextViewCompat.getMaxLines(mTextView);
-
- if (textSize != curTextSize || (curMaxLines >= 0 && maxLines != curMaxLines)) {
- // We've got a new text size and/or max lines...
- boolean updateTextView = true;
-
- if (mMode == MODE_FIXED && textSize > curTextSize && curLineCount == 1) {
- // If we're in fixed mode, going up in text size and currently have 1 line
- // then it's very easy to get into an infinite recursion.
- // To combat that we check to see if the change in text size
- // will cause a line count change. If so, abort the size change and stick
- // to the smaller size.
- final Layout layout = mTextView.getLayout();
- if (layout == null || approximateLineWidth(layout, 0, textSize)
- > getMeasuredWidth() - getPaddingLeft() - getPaddingRight()) {
- updateTextView = false;
- }
- }
-
- if (updateTextView) {
- mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
- mTextView.setMaxLines(maxLines);
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
- }
- }
-
- void setTab(@Nullable final Tab tab) {
- if (tab != mTab) {
- mTab = tab;
- update();
- }
- }
-
- void reset() {
- setTab(null);
- setSelected(false);
- }
-
- final void update() {
- final Tab tab = mTab;
- final View custom = tab != null ? tab.getCustomView() : null;
- 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);
- }
-
- mCustomTextView = (TextView) custom.findViewById(android.R.id.text1);
- if (mCustomTextView != null) {
- mDefaultMaxLines = TextViewCompat.getMaxLines(mCustomTextView);
- }
- mCustomIconView = (ImageView) custom.findViewById(android.R.id.icon);
- } else {
- // We do not have a custom view. Remove one if it already exists
- if (mCustomView != null) {
- removeView(mCustomView);
- mCustomView = null;
- }
- mCustomTextView = null;
- mCustomIconView = null;
- }
-
- if (mCustomView == null) {
- // If there isn't a custom view, we'll us our own in-built layouts
- if (mIconView == null) {
- ImageView iconView = (ImageView) LayoutInflater.from(getContext())
- .inflate(R.layout.design_layout_tab_icon, this, false);
- addView(iconView, 0);
- mIconView = iconView;
- }
- if (mTextView == null) {
- TextView textView = (TextView) LayoutInflater.from(getContext())
- .inflate(R.layout.design_layout_tab_text, this, false);
- addView(textView);
- mTextView = textView;
- mDefaultMaxLines = TextViewCompat.getMaxLines(mTextView);
- }
- TextViewCompat.setTextAppearance(mTextView, mTabTextAppearance);
- if (mTabTextColors != null) {
- mTextView.setTextColor(mTabTextColors);
- }
- updateTextAndIcon(mTextView, mIconView);
- } else {
- // Else, we'll see if there is a TextView or ImageView present and update them
- if (mCustomTextView != null || mCustomIconView != null) {
- updateTextAndIcon(mCustomTextView, mCustomIconView);
- }
- }
-
- // Finally update our selected state
- setSelected(tab != null && tab.isSelected());
- }
-
- private void updateTextAndIcon(@Nullable final TextView textView,
- @Nullable final ImageView iconView) {
- final Drawable icon = mTab != null ? mTab.getIcon() : null;
- final CharSequence text = mTab != null ? mTab.getText() : null;
- final CharSequence contentDesc = mTab != null ? mTab.getContentDescription() : null;
-
- if (iconView != null) {
- if (icon != null) {
- iconView.setImageDrawable(icon);
- iconView.setVisibility(VISIBLE);
- setVisibility(VISIBLE);
- } else {
- iconView.setVisibility(GONE);
- iconView.setImageDrawable(null);
- }
- iconView.setContentDescription(contentDesc);
- }
-
- final boolean hasText = !TextUtils.isEmpty(text);
- if (textView != null) {
- if (hasText) {
- textView.setText(text);
- textView.setVisibility(VISIBLE);
- setVisibility(VISIBLE);
- } else {
- textView.setVisibility(GONE);
- textView.setText(null);
- }
- textView.setContentDescription(contentDesc);
- }
-
- if (iconView != null) {
- MarginLayoutParams lp = ((MarginLayoutParams) iconView.getLayoutParams());
- int bottomMargin = 0;
- if (hasText && iconView.getVisibility() == VISIBLE) {
- // If we're showing both text and icon, add some margin bottom to the icon
- bottomMargin = dpToPx(DEFAULT_GAP_TEXT_ICON);
- }
- if (bottomMargin != lp.bottomMargin) {
- lp.bottomMargin = bottomMargin;
- iconView.requestLayout();
- }
- }
- TooltipCompat.setTooltipText(this, hasText ? null : contentDesc);
- }
-
- public Tab getTab() {
- return mTab;
- }
-
- /**
- * Approximates a given lines width with the new provided text size.
- */
- private float approximateLineWidth(Layout layout, int line, float textSize) {
- return layout.getLineWidth(line) * (textSize / layout.getPaint().getTextSize());
- }
- }
-
- private class SlidingTabStrip extends LinearLayout {
- private int mSelectedIndicatorHeight;
- private final Paint mSelectedIndicatorPaint;
-
- int mSelectedPosition = -1;
- float mSelectionOffset;
-
- private int mLayoutDirection = -1;
-
- private int mIndicatorLeft = -1;
- private int mIndicatorRight = -1;
-
- private ValueAnimator mIndicatorAnimator;
-
- SlidingTabStrip(Context context) {
- super(context);
- setWillNotDraw(false);
- mSelectedIndicatorPaint = new Paint();
- }
-
- void setSelectedIndicatorColor(int color) {
- if (mSelectedIndicatorPaint.getColor() != color) {
- mSelectedIndicatorPaint.setColor(color);
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
-
- void setSelectedIndicatorHeight(int height) {
- if (mSelectedIndicatorHeight != height) {
- mSelectedIndicatorHeight = height;
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
-
- boolean childrenNeedLayout() {
- for (int i = 0, z = getChildCount(); i < z; i++) {
- final View child = getChildAt(i);
- if (child.getWidth() <= 0) {
- return true;
- }
- }
- return false;
- }
-
- void setIndicatorPositionFromTabPosition(int position, float positionOffset) {
- if (mIndicatorAnimator != null && mIndicatorAnimator.isRunning()) {
- mIndicatorAnimator.cancel();
- }
-
- mSelectedPosition = position;
- mSelectionOffset = positionOffset;
- updateIndicatorPosition();
- }
-
- float getIndicatorPosition() {
- return mSelectedPosition + mSelectionOffset;
- }
-
- @Override
- public void onRtlPropertiesChanged(int layoutDirection) {
- super.onRtlPropertiesChanged(layoutDirection);
-
- // Workaround for a bug before Android M where LinearLayout did not relayout itself when
- // layout direction changed.
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
- //noinspection WrongConstant
- if (mLayoutDirection != layoutDirection) {
- requestLayout();
- mLayoutDirection = layoutDirection;
- }
- }
- }
-
- @Override
- protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY) {
- // HorizontalScrollView will first measure use with UNSPECIFIED, and then with
- // EXACTLY. Ignore the first call since anything we do will be overwritten anyway
- return;
- }
-
- if (mMode == MODE_FIXED && mTabGravity == GRAVITY_CENTER) {
- final int count = getChildCount();
-
- // First we'll find the widest tab
- int largestTabWidth = 0;
- for (int i = 0, z = count; i < z; i++) {
- View child = getChildAt(i);
- if (child.getVisibility() == VISIBLE) {
- largestTabWidth = Math.max(largestTabWidth, child.getMeasuredWidth());
- }
- }
-
- if (largestTabWidth <= 0) {
- // If we don't have a largest child yet, skip until the next measure pass
- return;
- }
-
- final int gutter = dpToPx(FIXED_WRAP_GUTTER_MIN);
- boolean remeasure = false;
-
- if (largestTabWidth * count <= getMeasuredWidth() - gutter * 2) {
- // If the tabs fit within our width minus gutters, we will set all tabs to have
- // the same width
- for (int i = 0; i < count; i++) {
- final LinearLayout.LayoutParams lp =
- (LayoutParams) getChildAt(i).getLayoutParams();
- if (lp.width != largestTabWidth || lp.weight != 0) {
- lp.width = largestTabWidth;
- lp.weight = 0;
- remeasure = true;
- }
- }
- } else {
- // If the tabs will wrap to be larger than the width minus gutters, we need
- // to switch to GRAVITY_FILL
- mTabGravity = GRAVITY_FILL;
- updateTabViews(false);
- remeasure = true;
- }
-
- if (remeasure) {
- // Now re-measure after our changes
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
-
- if (mIndicatorAnimator != null && mIndicatorAnimator.isRunning()) {
- // If we're currently running an animation, lets cancel it and start a
- // new animation with the remaining duration
- mIndicatorAnimator.cancel();
- final long duration = mIndicatorAnimator.getDuration();
- animateIndicatorToPosition(mSelectedPosition,
- Math.round((1f - mIndicatorAnimator.getAnimatedFraction()) * duration));
- } else {
- // If we've been layed out, update the indicator position
- updateIndicatorPosition();
- }
- }
-
- private void updateIndicatorPosition() {
- final View selectedTitle = getChildAt(mSelectedPosition);
- int left, right;
-
- if (selectedTitle != null && selectedTitle.getWidth() > 0) {
- left = selectedTitle.getLeft();
- right = selectedTitle.getRight();
-
- if (mSelectionOffset > 0f && mSelectedPosition < getChildCount() - 1) {
- // Draw the selection partway between the tabs
- View nextTitle = getChildAt(mSelectedPosition + 1);
- left = (int) (mSelectionOffset * nextTitle.getLeft() +
- (1.0f - mSelectionOffset) * left);
- right = (int) (mSelectionOffset * nextTitle.getRight() +
- (1.0f - mSelectionOffset) * right);
- }
- } else {
- left = right = -1;
- }
-
- setIndicatorPosition(left, right);
- }
-
- void setIndicatorPosition(int left, int right) {
- if (left != mIndicatorLeft || right != mIndicatorRight) {
- // If the indicator's left/right has changed, invalidate
- mIndicatorLeft = left;
- mIndicatorRight = right;
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
-
- void animateIndicatorToPosition(final int position, int duration) {
- if (mIndicatorAnimator != null && mIndicatorAnimator.isRunning()) {
- mIndicatorAnimator.cancel();
- }
-
- final boolean isRtl = ViewCompat.getLayoutDirection(this)
- == ViewCompat.LAYOUT_DIRECTION_RTL;
-
- final View targetView = getChildAt(position);
- if (targetView == null) {
- // If we don't have a view, just update the position now and return
- updateIndicatorPosition();
- return;
- }
-
- final int targetLeft = targetView.getLeft();
- final int targetRight = targetView.getRight();
- final int startLeft;
- final int startRight;
-
- if (Math.abs(position - mSelectedPosition) <= 1) {
- // If the views are adjacent, we'll animate from edge-to-edge
- startLeft = mIndicatorLeft;
- startRight = mIndicatorRight;
- } else {
- // Else, we'll just grow from the nearest edge
- final int offset = dpToPx(MOTION_NON_ADJACENT_OFFSET);
- if (position < mSelectedPosition) {
- // We're going end-to-start
- if (isRtl) {
- startLeft = startRight = targetLeft - offset;
- } else {
- startLeft = startRight = targetRight + offset;
- }
- } else {
- // We're going start-to-end
- if (isRtl) {
- startLeft = startRight = targetRight + offset;
- } else {
- startLeft = startRight = targetLeft - offset;
- }
- }
- }
-
- if (startLeft != targetLeft || startRight != targetRight) {
- ValueAnimator animator = mIndicatorAnimator = new ValueAnimator();
- animator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
- animator.setDuration(duration);
- animator.setFloatValues(0, 1);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- final float fraction = animator.getAnimatedFraction();
- setIndicatorPosition(
- AnimationUtils.lerp(startLeft, targetLeft, fraction),
- AnimationUtils.lerp(startRight, targetRight, fraction));
- }
- });
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- mSelectedPosition = position;
- mSelectionOffset = 0f;
- }
- });
- animator.start();
- }
- }
-
- @Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
-
- // Thick colored underline below the current selection
- if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {
- canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
- mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
- }
- }
- }
-
- private static ColorStateList createColorStateList(int defaultColor, int selectedColor) {
- final int[][] states = new int[2][];
- final int[] colors = new int[2];
- int i = 0;
-
- states[i] = SELECTED_STATE_SET;
- colors[i] = selectedColor;
- i++;
-
- // Default enabled state
- states[i] = EMPTY_STATE_SET;
- colors[i] = defaultColor;
- i++;
-
- return new ColorStateList(states, colors);
- }
-
- private int getDefaultHeight() {
- boolean hasIconAndText = false;
- for (int i = 0, count = mTabs.size(); i < count; i++) {
- Tab tab = mTabs.get(i);
- if (tab != null && tab.getIcon() != null && !TextUtils.isEmpty(tab.getText())) {
- hasIconAndText = true;
- break;
- }
- }
- return hasIconAndText ? DEFAULT_HEIGHT_WITH_TEXT_ICON : DEFAULT_HEIGHT;
- }
-
- private int getTabMinWidth() {
- if (mRequestedTabMinWidth != INVALID_WIDTH) {
- // If we have been given a min width, use it
- return mRequestedTabMinWidth;
- }
- // Else, we'll use the default value
- return mMode == MODE_SCROLLABLE ? mScrollableTabMinWidth : 0;
- }
-
- @Override
- public LayoutParams generateLayoutParams(AttributeSet attrs) {
- // We don't care about the layout params of any views added to us, since we don't actually
- // add them. The only view we add is the SlidingTabStrip, which is done manually.
- // We return the default layout params so that we don't blow up if we're given a TabItem
- // without android:layout_* values.
- return generateDefaultLayoutParams();
- }
-
- int getTabMaxWidth() {
- return mTabMaxWidth;
- }
-
- /**
- * A {@link ViewPager.OnPageChangeListener} class which contains the
- * necessary calls back to the provided {@link TabLayout} so that the tab position is
- * kept in sync.
- *
- * <p>This class stores the provided TabLayout weakly, meaning that you can use
- * {@link ViewPager#addOnPageChangeListener(ViewPager.OnPageChangeListener)
- * addOnPageChangeListener(OnPageChangeListener)} without removing the listener and
- * not cause a leak.
- */
- public static class TabLayoutOnPageChangeListener implements ViewPager.OnPageChangeListener {
- private final WeakReference<TabLayout> mTabLayoutRef;
- private int mPreviousScrollState;
- private int mScrollState;
-
- public TabLayoutOnPageChangeListener(TabLayout tabLayout) {
- mTabLayoutRef = new WeakReference<>(tabLayout);
- }
-
- @Override
- public void onPageScrollStateChanged(final int state) {
- mPreviousScrollState = mScrollState;
- mScrollState = state;
- }
-
- @Override
- public void onPageScrolled(final int position, final float positionOffset,
- final int positionOffsetPixels) {
- final TabLayout tabLayout = mTabLayoutRef.get();
- if (tabLayout != null) {
- // Only update the text selection if we're not settling, or we are settling after
- // being dragged
- final boolean updateText = mScrollState != SCROLL_STATE_SETTLING ||
- mPreviousScrollState == SCROLL_STATE_DRAGGING;
- // Update the indicator if we're not settling after being idle. This is caused
- // from a setCurrentItem() call and will be handled by an animation from
- // onPageSelected() instead.
- final boolean updateIndicator = !(mScrollState == SCROLL_STATE_SETTLING
- && mPreviousScrollState == SCROLL_STATE_IDLE);
- tabLayout.setScrollPosition(position, positionOffset, updateText, updateIndicator);
- }
- }
-
- @Override
- public void onPageSelected(final int position) {
- final TabLayout tabLayout = mTabLayoutRef.get();
- if (tabLayout != null && tabLayout.getSelectedTabPosition() != position
- && position < tabLayout.getTabCount()) {
- // Select the tab, only updating the indicator if we're not being dragged/settled
- // (since onPageScrolled will handle that).
- final boolean updateIndicator = mScrollState == SCROLL_STATE_IDLE
- || (mScrollState == SCROLL_STATE_SETTLING
- && mPreviousScrollState == SCROLL_STATE_IDLE);
- tabLayout.selectTab(tabLayout.getTabAt(position), updateIndicator);
- }
- }
-
- void reset() {
- mPreviousScrollState = mScrollState = SCROLL_STATE_IDLE;
- }
- }
-
- /**
- * A {@link TabLayout.OnTabSelectedListener} class which contains the necessary calls back
- * to the provided {@link ViewPager} so that the tab position is kept in sync.
- */
- public static class ViewPagerOnTabSelectedListener implements TabLayout.OnTabSelectedListener {
- private final ViewPager mViewPager;
-
- public ViewPagerOnTabSelectedListener(ViewPager viewPager) {
- mViewPager = viewPager;
- }
-
- @Override
- public void onTabSelected(TabLayout.Tab tab) {
- mViewPager.setCurrentItem(tab.getPosition());
- }
-
- @Override
- public void onTabUnselected(TabLayout.Tab tab) {
- // No-op
- }
-
- @Override
- public void onTabReselected(TabLayout.Tab tab) {
- // No-op
- }
- }
-
- private class PagerAdapterObserver extends DataSetObserver {
- PagerAdapterObserver() {
- }
-
- @Override
- public void onChanged() {
- populateFromPagerAdapter();
- }
-
- @Override
- public void onInvalidated() {
- populateFromPagerAdapter();
- }
- }
-
- private class AdapterChangeListener implements ViewPager.OnAdapterChangeListener {
- private boolean mAutoRefresh;
-
- AdapterChangeListener() {
- }
-
- @Override
- public void onAdapterChanged(@NonNull ViewPager viewPager,
- @Nullable PagerAdapter oldAdapter, @Nullable PagerAdapter newAdapter) {
- if (mViewPager == viewPager) {
- setPagerAdapter(newAdapter, mAutoRefresh);
- }
- }
-
- void setAutoRefresh(boolean autoRefresh) {
- mAutoRefresh = autoRefresh;
- }
- }
-}
diff --git a/android/support/design/widget/TextInputEditText.java b/android/support/design/widget/TextInputEditText.java
deleted file mode 100644
index ee6c32cd..00000000
--- a/android/support/design/widget/TextInputEditText.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2016 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.design.widget;
-
-import android.content.Context;
-import android.support.v7.widget.AppCompatEditText;
-import android.support.v7.widget.WithHint;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewParent;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-
-/**
- * A special sub-class of {@link android.widget.EditText} designed for use as a child of
- * {@link TextInputLayout}.
- *
- * <p>Using this class allows us to display a hint in the IME when in 'extract' mode.</p>
- */
-public class TextInputEditText extends AppCompatEditText {
-
- public TextInputEditText(Context context) {
- super(context);
- }
-
- public TextInputEditText(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public TextInputEditText(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- @Override
- public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
- final InputConnection ic = super.onCreateInputConnection(outAttrs);
- if (ic != null && outAttrs.hintText == null) {
- // If we don't have a hint and our parent implements WithHint, use its hint for the
- // EditorInfo. This allows us to display a hint in 'extract mode'.
- ViewParent parent = getParent();
- while (parent instanceof View) {
- if (parent instanceof WithHint) {
- outAttrs.hintText = ((WithHint) parent).getHint();
- break;
- }
- parent = parent.getParent();
- }
- }
- return ic;
- }
-}
diff --git a/android/support/design/widget/TextInputLayout.java b/android/support/design/widget/TextInputLayout.java
deleted file mode 100644
index 0540678e..00000000
--- a/android/support/design/widget/TextInputLayout.java
+++ /dev/null
@@ -1,1530 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.DrawableContainer;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.StringRes;
-import android.support.annotation.StyleRes;
-import android.support.annotation.VisibleForTesting;
-import android.support.design.R;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.view.AbsSavedState;
-import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.widget.Space;
-import android.support.v4.widget.TextViewCompat;
-import android.support.v4.widget.ViewGroupUtils;
-import android.support.v7.content.res.AppCompatResources;
-import android.support.v7.widget.AppCompatDrawableManager;
-import android.support.v7.widget.AppCompatTextView;
-import android.support.v7.widget.TintTypedArray;
-import android.support.v7.widget.WithHint;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.text.method.PasswordTransformationMethod;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStructure;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AccelerateInterpolator;
-import android.widget.EditText;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-/**
- * Layout which wraps an {@link android.widget.EditText} (or descendant) to show a floating label
- * when the hint is hidden due to the user inputting text.
- *
- * <p>Also supports showing an error via {@link #setErrorEnabled(boolean)} and
- * {@link #setError(CharSequence)}, and a character counter via
- * {@link #setCounterEnabled(boolean)}.</p>
- *
- * <p>Password visibility toggling is also supported via the
- * {@link #setPasswordVisibilityToggleEnabled(boolean)} API and related attribute.
- * If enabled, a button is displayed to toggle between the password being displayed as plain-text
- * or disguised, when your EditText is set to display a password.</p>
- *
- * <p><strong>Note:</strong> When using the password toggle functionality, the 'end' compound
- * drawable of the EditText will be overridden while the toggle is enabled. To ensure that any
- * existing drawables are restored correctly, you should set those compound drawables relatively
- * (start/end), opposed to absolutely (left/right).</p>
- *
- * The {@link TextInputEditText} class is provided to be used as a child of this layout. Using
- * TextInputEditText allows TextInputLayout greater control over the visual aspects of any
- * text input. An example usage is as so:
- *
- * <pre>
- * &lt;android.support.design.widget.TextInputLayout
- * android:layout_width=&quot;match_parent&quot;
- * android:layout_height=&quot;wrap_content&quot;&gt;
- *
- * &lt;android.support.design.widget.TextInputEditText
- * android:layout_width=&quot;match_parent&quot;
- * android:layout_height=&quot;wrap_content&quot;
- * android:hint=&quot;@string/form_username&quot;/&gt;
- *
- * &lt;/android.support.design.widget.TextInputLayout&gt;
- * </pre>
- *
- * <p><strong>Note:</strong> The actual view hierarchy present under TextInputLayout is
- * <strong>NOT</strong> guaranteed to match the view hierarchy as written in XML. As a result,
- * calls to getParent() on children of the TextInputLayout -- such as an TextInputEditText --
- * may not return the TextInputLayout itself, but rather an intermediate View. If you need
- * to access a View directly, set an {@code android:id} and use {@link View#findViewById(int)}.
- */
-public class TextInputLayout extends LinearLayout implements WithHint {
-
- private static final int ANIMATION_DURATION = 200;
- private static final int INVALID_MAX_LENGTH = -1;
-
- private static final String LOG_TAG = "TextInputLayout";
-
- private final FrameLayout mInputFrame;
- EditText mEditText;
- private CharSequence mOriginalHint;
-
- private boolean mHintEnabled;
- private CharSequence mHint;
-
- private Paint mTmpPaint;
- private final Rect mTmpRect = new Rect();
-
- private LinearLayout mIndicatorArea;
- private int mIndicatorsAdded;
-
- private Typeface mTypeface;
-
- private boolean mErrorEnabled;
- TextView mErrorView;
- private int mErrorTextAppearance;
- private boolean mErrorShown;
- private CharSequence mError;
-
- boolean mCounterEnabled;
- private TextView mCounterView;
- private int mCounterMaxLength;
- private int mCounterTextAppearance;
- private int mCounterOverflowTextAppearance;
- private boolean mCounterOverflowed;
-
- private boolean mPasswordToggleEnabled;
- private Drawable mPasswordToggleDrawable;
- private CharSequence mPasswordToggleContentDesc;
- private CheckableImageButton mPasswordToggleView;
- private boolean mPasswordToggledVisible;
- private Drawable mPasswordToggleDummyDrawable;
- private Drawable mOriginalEditTextEndDrawable;
-
- private ColorStateList mPasswordToggleTintList;
- private boolean mHasPasswordToggleTintList;
- private PorterDuff.Mode mPasswordToggleTintMode;
- private boolean mHasPasswordToggleTintMode;
-
- private ColorStateList mDefaultTextColor;
- private ColorStateList mFocusedTextColor;
-
- // Only used for testing
- private boolean mHintExpanded;
-
- final CollapsingTextHelper mCollapsingTextHelper = new CollapsingTextHelper(this);
-
- private boolean mHintAnimationEnabled;
- private ValueAnimator mAnimator;
-
- private boolean mHasReconstructedEditTextBackground;
- private boolean mInDrawableStateChanged;
-
- private boolean mRestoringSavedState;
-
- public TextInputLayout(Context context) {
- this(context, null);
- }
-
- public TextInputLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- // Can't call through to super(Context, AttributeSet, int) since it doesn't exist on API 10
- super(context, attrs);
-
- ThemeUtils.checkAppCompatTheme(context);
-
- setOrientation(VERTICAL);
- setWillNotDraw(false);
- setAddStatesFromChildren(true);
-
- mInputFrame = new FrameLayout(context);
- mInputFrame.setAddStatesFromChildren(true);
- addView(mInputFrame);
-
- mCollapsingTextHelper.setTextSizeInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
- mCollapsingTextHelper.setPositionInterpolator(new AccelerateInterpolator());
- mCollapsingTextHelper.setCollapsedTextGravity(Gravity.TOP | GravityCompat.START);
-
- final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
- R.styleable.TextInputLayout, defStyleAttr, R.style.Widget_Design_TextInputLayout);
- mHintEnabled = a.getBoolean(R.styleable.TextInputLayout_hintEnabled, true);
- setHint(a.getText(R.styleable.TextInputLayout_android_hint));
- mHintAnimationEnabled = a.getBoolean(
- R.styleable.TextInputLayout_hintAnimationEnabled, true);
-
- if (a.hasValue(R.styleable.TextInputLayout_android_textColorHint)) {
- mDefaultTextColor = mFocusedTextColor =
- a.getColorStateList(R.styleable.TextInputLayout_android_textColorHint);
- }
-
- final int hintAppearance = a.getResourceId(
- R.styleable.TextInputLayout_hintTextAppearance, -1);
- if (hintAppearance != -1) {
- setHintTextAppearance(
- a.getResourceId(R.styleable.TextInputLayout_hintTextAppearance, 0));
- }
-
- mErrorTextAppearance = a.getResourceId(R.styleable.TextInputLayout_errorTextAppearance, 0);
- final boolean errorEnabled = a.getBoolean(R.styleable.TextInputLayout_errorEnabled, false);
-
- final boolean counterEnabled = a.getBoolean(
- R.styleable.TextInputLayout_counterEnabled, false);
- setCounterMaxLength(
- a.getInt(R.styleable.TextInputLayout_counterMaxLength, INVALID_MAX_LENGTH));
- mCounterTextAppearance = a.getResourceId(
- R.styleable.TextInputLayout_counterTextAppearance, 0);
- mCounterOverflowTextAppearance = a.getResourceId(
- R.styleable.TextInputLayout_counterOverflowTextAppearance, 0);
-
- mPasswordToggleEnabled = a.getBoolean(
- R.styleable.TextInputLayout_passwordToggleEnabled, false);
- mPasswordToggleDrawable = a.getDrawable(R.styleable.TextInputLayout_passwordToggleDrawable);
- mPasswordToggleContentDesc = a.getText(
- R.styleable.TextInputLayout_passwordToggleContentDescription);
- if (a.hasValue(R.styleable.TextInputLayout_passwordToggleTint)) {
- mHasPasswordToggleTintList = true;
- mPasswordToggleTintList = a.getColorStateList(
- R.styleable.TextInputLayout_passwordToggleTint);
- }
- if (a.hasValue(R.styleable.TextInputLayout_passwordToggleTintMode)) {
- mHasPasswordToggleTintMode = true;
- mPasswordToggleTintMode = ViewUtils.parseTintMode(
- a.getInt(R.styleable.TextInputLayout_passwordToggleTintMode, -1), null);
- }
-
- a.recycle();
-
- setErrorEnabled(errorEnabled);
- setCounterEnabled(counterEnabled);
- applyPasswordToggleTint();
-
- if (ViewCompat.getImportantForAccessibility(this)
- == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
- // Make sure we're important for accessibility if we haven't been explicitly not
- ViewCompat.setImportantForAccessibility(this,
- ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
-
- ViewCompat.setAccessibilityDelegate(this, new TextInputAccessibilityDelegate());
- }
-
- @Override
- public void addView(View child, int index, final ViewGroup.LayoutParams params) {
- if (child instanceof EditText) {
- // Make sure that the EditText is vertically at the bottom, so that it sits on the
- // EditText's underline
- FrameLayout.LayoutParams flp = new FrameLayout.LayoutParams(params);
- flp.gravity = Gravity.CENTER_VERTICAL | (flp.gravity & ~Gravity.VERTICAL_GRAVITY_MASK);
- mInputFrame.addView(child, flp);
-
- // Now use the EditText's LayoutParams as our own and update them to make enough space
- // for the label
- mInputFrame.setLayoutParams(params);
- updateInputLayoutMargins();
-
- setEditText((EditText) child);
- } else {
- // Carry on adding the View...
- super.addView(child, index, params);
- }
- }
-
- /**
- * Set the typeface to use for the hint and any label views (such as counter and error views).
- *
- * @param typeface typeface to use, or {@code null} to use the default.
- */
- public void setTypeface(@Nullable Typeface typeface) {
- if ((mTypeface != null && !mTypeface.equals(typeface))
- || (mTypeface == null && typeface != null)) {
- mTypeface = typeface;
-
- mCollapsingTextHelper.setTypefaces(typeface);
- if (mCounterView != null) {
- mCounterView.setTypeface(typeface);
- }
- if (mErrorView != null) {
- mErrorView.setTypeface(typeface);
- }
- }
- }
-
- /**
- * Returns the typeface used for the hint and any label views (such as counter and error views).
- */
- @NonNull
- public Typeface getTypeface() {
- return mTypeface;
- }
-
- @Override
- public void dispatchProvideAutofillStructure(ViewStructure structure, int flags) {
- if (mOriginalHint == null || mEditText == null) {
- super.dispatchProvideAutofillStructure(structure, flags);
- return;
- }
-
- // Temporarily sets child's hint to its original value so it is properly set in the
- // child's ViewStructure.
- final CharSequence hint = mEditText.getHint();
- mEditText.setHint(mOriginalHint);
- try {
- super.dispatchProvideAutofillStructure(structure, flags);
- } finally {
- mEditText.setHint(hint);
- }
- }
-
- private void setEditText(EditText editText) {
- // If we already have an EditText, throw an exception
- if (mEditText != null) {
- throw new IllegalArgumentException("We already have an EditText, can only have one");
- }
-
- if (!(editText instanceof TextInputEditText)) {
- Log.i(LOG_TAG, "EditText added is not a TextInputEditText. Please switch to using that"
- + " class instead.");
- }
-
- mEditText = editText;
-
- final boolean hasPasswordTransformation = hasPasswordTransformation();
-
- // Use the EditText's typeface, and it's text size for our expanded text
- if (!hasPasswordTransformation) {
- // We don't want a monospace font just because we have a password field
- mCollapsingTextHelper.setTypefaces(mEditText.getTypeface());
- }
- mCollapsingTextHelper.setExpandedTextSize(mEditText.getTextSize());
-
- final int editTextGravity = mEditText.getGravity();
- mCollapsingTextHelper.setCollapsedTextGravity(
- Gravity.TOP | (editTextGravity & ~Gravity.VERTICAL_GRAVITY_MASK));
- mCollapsingTextHelper.setExpandedTextGravity(editTextGravity);
-
- // Add a TextWatcher so that we know when the text input has changed
- mEditText.addTextChangedListener(new TextWatcher() {
- @Override
- public void afterTextChanged(Editable s) {
- updateLabelState(!mRestoringSavedState);
- if (mCounterEnabled) {
- updateCounter(s.length());
- }
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {}
- });
-
- // Use the EditText's hint colors if we don't have one set
- if (mDefaultTextColor == null) {
- mDefaultTextColor = mEditText.getHintTextColors();
- }
-
- // If we do not have a valid hint, try and retrieve it from the EditText, if enabled
- if (mHintEnabled && TextUtils.isEmpty(mHint)) {
- // Save the hint so it can be restored on dispatchProvideAutofillStructure();
- mOriginalHint = mEditText.getHint();
- setHint(mOriginalHint);
- // Clear the EditText's hint as we will display it ourselves
- mEditText.setHint(null);
- }
-
- if (mCounterView != null) {
- updateCounter(mEditText.getText().length());
- }
-
- if (mIndicatorArea != null) {
- adjustIndicatorPadding();
- }
-
- updatePasswordToggleView();
-
- // Update the label visibility with no animation, but force a state change
- updateLabelState(false, true);
- }
-
- private void updateInputLayoutMargins() {
- // Create/update the LayoutParams so that we can add enough top margin
- // to the EditText so make room for the label
- final LayoutParams lp = (LayoutParams) mInputFrame.getLayoutParams();
- final int newTopMargin;
-
- if (mHintEnabled) {
- if (mTmpPaint == null) {
- mTmpPaint = new Paint();
- }
- mTmpPaint.setTypeface(mCollapsingTextHelper.getCollapsedTypeface());
- mTmpPaint.setTextSize(mCollapsingTextHelper.getCollapsedTextSize());
- newTopMargin = (int) -mTmpPaint.ascent();
- } else {
- newTopMargin = 0;
- }
-
- if (newTopMargin != lp.topMargin) {
- lp.topMargin = newTopMargin;
- mInputFrame.requestLayout();
- }
- }
-
- void updateLabelState(boolean animate) {
- updateLabelState(animate, false);
- }
-
- void updateLabelState(final boolean animate, final boolean force) {
- final boolean isEnabled = isEnabled();
- final boolean hasText = mEditText != null && !TextUtils.isEmpty(mEditText.getText());
- final boolean isFocused = arrayContains(getDrawableState(), android.R.attr.state_focused);
- final boolean isErrorShowing = !TextUtils.isEmpty(getError());
-
- if (mDefaultTextColor != null) {
- mCollapsingTextHelper.setExpandedTextColor(mDefaultTextColor);
- }
-
- if (isEnabled && mCounterOverflowed && mCounterView != null) {
- mCollapsingTextHelper.setCollapsedTextColor(mCounterView.getTextColors());
- } else if (isEnabled && isFocused && mFocusedTextColor != null) {
- mCollapsingTextHelper.setCollapsedTextColor(mFocusedTextColor);
- } else if (mDefaultTextColor != null) {
- mCollapsingTextHelper.setCollapsedTextColor(mDefaultTextColor);
- }
-
- if (hasText || (isEnabled() && (isFocused || isErrorShowing))) {
- // We should be showing the label so do so if it isn't already
- if (force || mHintExpanded) {
- collapseHint(animate);
- }
- } else {
- // We should not be showing the label so hide it
- if (force || !mHintExpanded) {
- expandHint(animate);
- }
- }
- }
-
- /**
- * Returns the {@link android.widget.EditText} used for text input.
- */
- @Nullable
- public EditText getEditText() {
- return mEditText;
- }
-
- /**
- * Set the hint to be displayed in the floating label, if enabled.
- *
- * @see #setHintEnabled(boolean)
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_android_hint
- */
- public void setHint(@Nullable CharSequence hint) {
- if (mHintEnabled) {
- setHintInternal(hint);
- sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
- }
- }
-
- private void setHintInternal(CharSequence hint) {
- mHint = hint;
- mCollapsingTextHelper.setText(hint);
- }
-
- /**
- * Returns the hint which is displayed in the floating label, if enabled.
- *
- * @return the hint, or null if there isn't one set, or the hint is not enabled.
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_android_hint
- */
- @Override
- @Nullable
- public CharSequence getHint() {
- return mHintEnabled ? mHint : null;
- }
-
- /**
- * Sets whether the floating label functionality is enabled or not in this layout.
- *
- * <p>If enabled, any non-empty hint in the child EditText will be moved into the floating
- * hint, and its existing hint will be cleared. If disabled, then any non-empty floating hint
- * in this layout will be moved into the EditText, and this layout's hint will be cleared.</p>
- *
- * @see #setHint(CharSequence)
- * @see #isHintEnabled()
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_hintEnabled
- */
- public void setHintEnabled(boolean enabled) {
- if (enabled != mHintEnabled) {
- mHintEnabled = enabled;
-
- final CharSequence editTextHint = mEditText.getHint();
- if (!mHintEnabled) {
- if (!TextUtils.isEmpty(mHint) && TextUtils.isEmpty(editTextHint)) {
- // If the hint is disabled, but we have a hint set, and the EditText doesn't,
- // pass it through...
- mEditText.setHint(mHint);
- }
- // Now clear out any set hint
- setHintInternal(null);
- } else {
- if (!TextUtils.isEmpty(editTextHint)) {
- // If the hint is now enabled and the EditText has one set, we'll use it if
- // we don't already have one, and clear the EditText's
- if (TextUtils.isEmpty(mHint)) {
- setHint(editTextHint);
- }
- mEditText.setHint(null);
- }
- }
-
- // Now update the EditText top margin
- if (mEditText != null) {
- updateInputLayoutMargins();
- }
- }
- }
-
- /**
- * Returns whether the floating label functionality is enabled or not in this layout.
- *
- * @see #setHintEnabled(boolean)
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_hintEnabled
- */
- public boolean isHintEnabled() {
- return mHintEnabled;
- }
-
- /**
- * Sets the hint text color, size, style from the specified TextAppearance resource.
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_hintTextAppearance
- */
- public void setHintTextAppearance(@StyleRes int resId) {
- mCollapsingTextHelper.setCollapsedTextAppearance(resId);
- mFocusedTextColor = mCollapsingTextHelper.getCollapsedTextColor();
-
- if (mEditText != null) {
- updateLabelState(false);
- // Text size might have changed so update the top margin
- updateInputLayoutMargins();
- }
- }
-
- private void addIndicator(TextView indicator, int index) {
- if (mIndicatorArea == null) {
- mIndicatorArea = new LinearLayout(getContext());
- mIndicatorArea.setOrientation(LinearLayout.HORIZONTAL);
- addView(mIndicatorArea, LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.WRAP_CONTENT);
-
- // Add a flexible spacer in the middle so that the left/right views stay pinned
- final Space spacer = new Space(getContext());
- final LinearLayout.LayoutParams spacerLp = new LinearLayout.LayoutParams(0, 0, 1f);
- mIndicatorArea.addView(spacer, spacerLp);
-
- if (mEditText != null) {
- adjustIndicatorPadding();
- }
- }
- mIndicatorArea.setVisibility(View.VISIBLE);
- mIndicatorArea.addView(indicator, index);
- mIndicatorsAdded++;
- }
-
- private void adjustIndicatorPadding() {
- // Add padding to the error and character counter so that they match the EditText
- ViewCompat.setPaddingRelative(mIndicatorArea, ViewCompat.getPaddingStart(mEditText),
- 0, ViewCompat.getPaddingEnd(mEditText), mEditText.getPaddingBottom());
- }
-
- private void removeIndicator(TextView indicator) {
- if (mIndicatorArea != null) {
- mIndicatorArea.removeView(indicator);
- if (--mIndicatorsAdded == 0) {
- mIndicatorArea.setVisibility(View.GONE);
- }
- }
- }
-
- /**
- * Whether the error functionality is enabled or not in this layout. Enabling this
- * functionality before setting an error message via {@link #setError(CharSequence)}, will mean
- * that this layout will not change size when an error is displayed.
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_errorEnabled
- */
- public void setErrorEnabled(boolean enabled) {
- if (mErrorEnabled != enabled) {
- if (mErrorView != null) {
- mErrorView.animate().cancel();
- }
-
- if (enabled) {
- mErrorView = new AppCompatTextView(getContext());
- mErrorView.setId(R.id.textinput_error);
- if (mTypeface != null) {
- mErrorView.setTypeface(mTypeface);
- }
- boolean useDefaultColor = false;
- try {
- TextViewCompat.setTextAppearance(mErrorView, mErrorTextAppearance);
-
- if (Build.VERSION.SDK_INT >= 23
- && mErrorView.getTextColors().getDefaultColor() == Color.MAGENTA) {
- // Caused by our theme not extending from Theme.Design*. On API 23 and
- // above, unresolved theme attrs result in MAGENTA rather than an exception.
- // Flag so that we use a decent default
- useDefaultColor = true;
- }
- } catch (Exception e) {
- // Caused by our theme not extending from Theme.Design*. Flag so that we use
- // a decent default
- useDefaultColor = true;
- }
- if (useDefaultColor) {
- // Probably caused by our theme not extending from Theme.Design*. Instead
- // we manually set something appropriate
- TextViewCompat.setTextAppearance(mErrorView,
- android.support.v7.appcompat.R.style.TextAppearance_AppCompat_Caption);
- mErrorView.setTextColor(ContextCompat.getColor(getContext(),
- android.support.v7.appcompat.R.color.error_color_material));
- }
- mErrorView.setVisibility(INVISIBLE);
- ViewCompat.setAccessibilityLiveRegion(mErrorView,
- ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
- addIndicator(mErrorView, 0);
- } else {
- mErrorShown = false;
- updateEditTextBackground();
- removeIndicator(mErrorView);
- mErrorView = null;
- }
- mErrorEnabled = enabled;
- }
- }
-
- /**
- * Sets the text color and size for the error message from the specified
- * TextAppearance resource.
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_errorTextAppearance
- */
- public void setErrorTextAppearance(@StyleRes int resId) {
- mErrorTextAppearance = resId;
- if (mErrorView != null) {
- TextViewCompat.setTextAppearance(mErrorView, resId);
- }
- }
-
- /**
- * Returns whether the error functionality is enabled or not in this layout.
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_errorEnabled
- *
- * @see #setErrorEnabled(boolean)
- */
- public boolean isErrorEnabled() {
- return mErrorEnabled;
- }
-
- /**
- * Sets an error message that will be displayed below our {@link EditText}. If the
- * {@code error} is {@code null}, the error message will be cleared.
- * <p>
- * If the error functionality has not been enabled via {@link #setErrorEnabled(boolean)}, then
- * it will be automatically enabled if {@code error} is not empty.
- *
- * @param error Error message to display, or null to clear
- *
- * @see #getError()
- */
- public void setError(@Nullable final CharSequence error) {
- // Only animate if we're enabled, laid out, and we have a different error message
- setError(error, ViewCompat.isLaidOut(this) && isEnabled()
- && (mErrorView == null || !TextUtils.equals(mErrorView.getText(), error)));
- }
-
- private void setError(@Nullable final CharSequence error, final boolean animate) {
- mError = error;
-
- if (!mErrorEnabled) {
- if (TextUtils.isEmpty(error)) {
- // If error isn't enabled, and the error is empty, just return
- return;
- }
- // Else, we'll assume that they want to enable the error functionality
- setErrorEnabled(true);
- }
-
- mErrorShown = !TextUtils.isEmpty(error);
-
- // Cancel any on-going animation
- mErrorView.animate().cancel();
-
- if (mErrorShown) {
- mErrorView.setText(error);
- mErrorView.setVisibility(VISIBLE);
-
- if (animate) {
- if (mErrorView.getAlpha() == 1f) {
- // If it's currently 100% show, we'll animate it from 0
- mErrorView.setAlpha(0f);
- }
- mErrorView.animate()
- .alpha(1f)
- .setDuration(ANIMATION_DURATION)
- .setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- mErrorView.setVisibility(VISIBLE);
- }
- }).start();
- } else {
- // Set alpha to 1f, just in case
- mErrorView.setAlpha(1f);
- }
- } else {
- if (mErrorView.getVisibility() == VISIBLE) {
- if (animate) {
- mErrorView.animate()
- .alpha(0f)
- .setDuration(ANIMATION_DURATION)
- .setInterpolator(AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- mErrorView.setText(error);
- mErrorView.setVisibility(INVISIBLE);
- }
- }).start();
- } else {
- mErrorView.setText(error);
- mErrorView.setVisibility(INVISIBLE);
- }
- }
- }
-
- updateEditTextBackground();
- updateLabelState(animate);
- }
-
- /**
- * Whether the character counter functionality is enabled or not in this layout.
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_counterEnabled
- */
- public void setCounterEnabled(boolean enabled) {
- if (mCounterEnabled != enabled) {
- if (enabled) {
- mCounterView = new AppCompatTextView(getContext());
- mCounterView.setId(R.id.textinput_counter);
- if (mTypeface != null) {
- mCounterView.setTypeface(mTypeface);
- }
- mCounterView.setMaxLines(1);
- try {
- TextViewCompat.setTextAppearance(mCounterView, mCounterTextAppearance);
- } catch (Exception e) {
- // Probably caused by our theme not extending from Theme.Design*. Instead
- // we manually set something appropriate
- TextViewCompat.setTextAppearance(mCounterView,
- android.support.v7.appcompat.R.style.TextAppearance_AppCompat_Caption);
- mCounterView.setTextColor(ContextCompat.getColor(getContext(),
- android.support.v7.appcompat.R.color.error_color_material));
- }
- addIndicator(mCounterView, -1);
- if (mEditText == null) {
- updateCounter(0);
- } else {
- updateCounter(mEditText.getText().length());
- }
- } else {
- removeIndicator(mCounterView);
- mCounterView = null;
- }
- mCounterEnabled = enabled;
- }
- }
-
- /**
- * Returns whether the character counter functionality is enabled or not in this layout.
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_counterEnabled
- *
- * @see #setCounterEnabled(boolean)
- */
- public boolean isCounterEnabled() {
- return mCounterEnabled;
- }
-
- /**
- * Sets the max length to display at the character counter.
- *
- * @param maxLength maxLength to display. Any value less than or equal to 0 will not be shown.
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_counterMaxLength
- */
- public void setCounterMaxLength(int maxLength) {
- if (mCounterMaxLength != maxLength) {
- if (maxLength > 0) {
- mCounterMaxLength = maxLength;
- } else {
- mCounterMaxLength = INVALID_MAX_LENGTH;
- }
- if (mCounterEnabled) {
- updateCounter(mEditText == null ? 0 : mEditText.getText().length());
- }
- }
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- // Since we're set to addStatesFromChildren, we need to make sure that we set all
- // children to enabled/disabled otherwise any enabled children will wipe out our disabled
- // drawable state
- recursiveSetEnabled(this, enabled);
- super.setEnabled(enabled);
- }
-
- private static void recursiveSetEnabled(final ViewGroup vg, final boolean enabled) {
- for (int i = 0, count = vg.getChildCount(); i < count; i++) {
- final View child = vg.getChildAt(i);
- child.setEnabled(enabled);
- if (child instanceof ViewGroup) {
- recursiveSetEnabled((ViewGroup) child, enabled);
- }
- }
- }
-
- /**
- * Returns the max length shown at the character counter.
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_counterMaxLength
- */
- public int getCounterMaxLength() {
- return mCounterMaxLength;
- }
-
- void updateCounter(int length) {
- boolean wasCounterOverflowed = mCounterOverflowed;
- if (mCounterMaxLength == INVALID_MAX_LENGTH) {
- mCounterView.setText(String.valueOf(length));
- mCounterOverflowed = false;
- } else {
- mCounterOverflowed = length > mCounterMaxLength;
- if (wasCounterOverflowed != mCounterOverflowed) {
- TextViewCompat.setTextAppearance(mCounterView, mCounterOverflowed
- ? mCounterOverflowTextAppearance : mCounterTextAppearance);
- }
- mCounterView.setText(getContext().getString(R.string.character_counter_pattern,
- length, mCounterMaxLength));
- }
- if (mEditText != null && wasCounterOverflowed != mCounterOverflowed) {
- updateLabelState(false);
- updateEditTextBackground();
- }
- }
-
- private void updateEditTextBackground() {
- if (mEditText == null) {
- return;
- }
-
- Drawable editTextBackground = mEditText.getBackground();
- if (editTextBackground == null) {
- return;
- }
-
- ensureBackgroundDrawableStateWorkaround();
-
- if (android.support.v7.widget.DrawableUtils.canSafelyMutateDrawable(editTextBackground)) {
- editTextBackground = editTextBackground.mutate();
- }
-
- if (mErrorShown && mErrorView != null) {
- // Set a color filter of the error color
- editTextBackground.setColorFilter(
- AppCompatDrawableManager.getPorterDuffColorFilter(
- mErrorView.getCurrentTextColor(), PorterDuff.Mode.SRC_IN));
- } else if (mCounterOverflowed && mCounterView != null) {
- // Set a color filter of the counter color
- editTextBackground.setColorFilter(
- AppCompatDrawableManager.getPorterDuffColorFilter(
- mCounterView.getCurrentTextColor(), PorterDuff.Mode.SRC_IN));
- } else {
- // Else reset the color filter and refresh the drawable state so that the
- // normal tint is used
- DrawableCompat.clearColorFilter(editTextBackground);
- mEditText.refreshDrawableState();
- }
- }
-
- private void ensureBackgroundDrawableStateWorkaround() {
- final int sdk = Build.VERSION.SDK_INT;
- if (sdk != 21 && sdk != 22) {
- // The workaround is only required on API 21-22
- return;
- }
- final Drawable bg = mEditText.getBackground();
- if (bg == null) {
- return;
- }
-
- if (!mHasReconstructedEditTextBackground) {
- // This is gross. There is an issue in the platform which affects container Drawables
- // where the first drawable retrieved from resources will propagate any changes
- // (like color filter) to all instances from the cache. We'll try to workaround it...
-
- final Drawable newBg = bg.getConstantState().newDrawable();
-
- if (bg instanceof DrawableContainer) {
- // If we have a Drawable container, we can try and set it's constant state via
- // reflection from the new Drawable
- mHasReconstructedEditTextBackground =
- DrawableUtils.setContainerConstantState(
- (DrawableContainer) bg, newBg.getConstantState());
- }
-
- if (!mHasReconstructedEditTextBackground) {
- // If we reach here then we just need to set a brand new instance of the Drawable
- // as the background. This has the unfortunate side-effect of wiping out any
- // user set padding, but I'd hope that use of custom padding on an EditText
- // is limited.
- ViewCompat.setBackground(mEditText, newBg);
- mHasReconstructedEditTextBackground = true;
- }
- }
- }
-
- static class SavedState extends AbsSavedState {
- CharSequence error;
- boolean isPasswordToggledVisible;
-
- SavedState(Parcelable superState) {
- super(superState);
- }
-
- SavedState(Parcel source, ClassLoader loader) {
- super(source, loader);
- error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
- isPasswordToggledVisible = (source.readInt() == 1);
-
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- TextUtils.writeToParcel(error, dest, flags);
- dest.writeInt(isPasswordToggledVisible ? 1 : 0);
- }
-
- @Override
- public String toString() {
- return "TextInputLayout.SavedState{"
- + Integer.toHexString(System.identityHashCode(this))
- + " error=" + error + "}";
- }
-
- public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
- @Override
- public SavedState createFromParcel(Parcel in, ClassLoader loader) {
- return new SavedState(in, loader);
- }
-
- @Override
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in, null);
- }
-
- @Override
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- Parcelable superState = super.onSaveInstanceState();
- SavedState ss = new SavedState(superState);
- if (mErrorShown) {
- ss.error = getError();
- }
- ss.isPasswordToggledVisible = mPasswordToggledVisible;
- return ss;
- }
-
- @Override
- protected void onRestoreInstanceState(Parcelable state) {
- if (!(state instanceof SavedState)) {
- super.onRestoreInstanceState(state);
- return;
- }
- SavedState ss = (SavedState) state;
- super.onRestoreInstanceState(ss.getSuperState());
- setError(ss.error);
- if (ss.isPasswordToggledVisible) {
- passwordVisibilityToggleRequested(true);
- }
- requestLayout();
- }
-
- @Override
- protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
- mRestoringSavedState = true;
- super.dispatchRestoreInstanceState(container);
- mRestoringSavedState = false;
- }
-
- /**
- * Returns the error message that was set to be displayed with
- * {@link #setError(CharSequence)}, or <code>null</code> if no error was set
- * or if error displaying is not enabled.
- *
- * @see #setError(CharSequence)
- */
- @Nullable
- public CharSequence getError() {
- return mErrorEnabled ? mError : null;
- }
-
- /**
- * Returns whether any hint state changes, due to being focused or non-empty text, are
- * animated.
- *
- * @see #setHintAnimationEnabled(boolean)
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_hintAnimationEnabled
- */
- public boolean isHintAnimationEnabled() {
- return mHintAnimationEnabled;
- }
-
- /**
- * Set whether any hint state changes, due to being focused or non-empty text, are
- * animated.
- *
- * @see #isHintAnimationEnabled()
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_hintAnimationEnabled
- */
- public void setHintAnimationEnabled(boolean enabled) {
- mHintAnimationEnabled = enabled;
- }
-
- @Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
-
- if (mHintEnabled) {
- mCollapsingTextHelper.draw(canvas);
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- updatePasswordToggleView();
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- private void updatePasswordToggleView() {
- if (mEditText == null) {
- // If there is no EditText, there is nothing to update
- return;
- }
-
- if (shouldShowPasswordIcon()) {
- if (mPasswordToggleView == null) {
- mPasswordToggleView = (CheckableImageButton) LayoutInflater.from(getContext())
- .inflate(R.layout.design_text_input_password_icon, mInputFrame, false);
- mPasswordToggleView.setImageDrawable(mPasswordToggleDrawable);
- mPasswordToggleView.setContentDescription(mPasswordToggleContentDesc);
- mInputFrame.addView(mPasswordToggleView);
-
- mPasswordToggleView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- passwordVisibilityToggleRequested(false);
- }
- });
- }
-
- if (mEditText != null && ViewCompat.getMinimumHeight(mEditText) <= 0) {
- // We should make sure that the EditText has the same min-height as the password
- // toggle view. This ensure focus works properly, and there is no visual jump
- // if the password toggle is enabled/disabled.
- mEditText.setMinimumHeight(ViewCompat.getMinimumHeight(mPasswordToggleView));
- }
-
- mPasswordToggleView.setVisibility(VISIBLE);
- mPasswordToggleView.setChecked(mPasswordToggledVisible);
-
- // We need to add a dummy drawable as the end compound drawable so that the text is
- // indented and doesn't display below the toggle view
- if (mPasswordToggleDummyDrawable == null) {
- mPasswordToggleDummyDrawable = new ColorDrawable();
- }
- mPasswordToggleDummyDrawable.setBounds(0, 0, mPasswordToggleView.getMeasuredWidth(), 1);
-
- final Drawable[] compounds = TextViewCompat.getCompoundDrawablesRelative(mEditText);
- // Store the user defined end compound drawable so that we can restore it later
- if (compounds[2] != mPasswordToggleDummyDrawable) {
- mOriginalEditTextEndDrawable = compounds[2];
- }
- TextViewCompat.setCompoundDrawablesRelative(mEditText, compounds[0], compounds[1],
- mPasswordToggleDummyDrawable, compounds[3]);
-
- // Copy over the EditText's padding so that we match
- mPasswordToggleView.setPadding(mEditText.getPaddingLeft(),
- mEditText.getPaddingTop(), mEditText.getPaddingRight(),
- mEditText.getPaddingBottom());
- } else {
- if (mPasswordToggleView != null && mPasswordToggleView.getVisibility() == VISIBLE) {
- mPasswordToggleView.setVisibility(View.GONE);
- }
-
- if (mPasswordToggleDummyDrawable != null) {
- // Make sure that we remove the dummy end compound drawable if it exists, and then
- // clear it
- final Drawable[] compounds = TextViewCompat.getCompoundDrawablesRelative(mEditText);
- if (compounds[2] == mPasswordToggleDummyDrawable) {
- TextViewCompat.setCompoundDrawablesRelative(mEditText, compounds[0],
- compounds[1], mOriginalEditTextEndDrawable, compounds[3]);
- mPasswordToggleDummyDrawable = null;
- }
- }
- }
- }
-
- /**
- * Set the icon to use for the password visibility toggle button.
- *
- * <p>If you use an icon you should also set a description for its action
- * using {@link #setPasswordVisibilityToggleContentDescription(CharSequence)}.
- * This is used for accessibility.</p>
- *
- * @param resId resource id of the drawable to set, or 0 to clear the icon
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleDrawable
- */
- public void setPasswordVisibilityToggleDrawable(@DrawableRes int resId) {
- setPasswordVisibilityToggleDrawable(resId != 0
- ? AppCompatResources.getDrawable(getContext(), resId)
- : null);
- }
-
- /**
- * Set the icon to use for the password visibility toggle button.
- *
- * <p>If you use an icon you should also set a description for its action
- * using {@link #setPasswordVisibilityToggleContentDescription(CharSequence)}.
- * This is used for accessibility.</p>
- *
- * @param icon Drawable to set, may be null to clear the icon
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleDrawable
- */
- public void setPasswordVisibilityToggleDrawable(@Nullable Drawable icon) {
- mPasswordToggleDrawable = icon;
- if (mPasswordToggleView != null) {
- mPasswordToggleView.setImageDrawable(icon);
- }
- }
-
- /**
- * Set a content description for the navigation button if one is present.
- *
- * <p>The content description will be read via screen readers or other accessibility
- * systems to explain the action of the password visibility toggle.</p>
- *
- * @param resId Resource ID of a content description string to set,
- * or 0 to clear the description
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleContentDescription
- */
- public void setPasswordVisibilityToggleContentDescription(@StringRes int resId) {
- setPasswordVisibilityToggleContentDescription(
- resId != 0 ? getResources().getText(resId) : null);
- }
-
- /**
- * Set a content description for the navigation button if one is present.
- *
- * <p>The content description will be read via screen readers or other accessibility
- * systems to explain the action of the password visibility toggle.</p>
- *
- * @param description Content description to set, or null to clear the content description
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleContentDescription
- */
- public void setPasswordVisibilityToggleContentDescription(@Nullable CharSequence description) {
- mPasswordToggleContentDesc = description;
- if (mPasswordToggleView != null) {
- mPasswordToggleView.setContentDescription(description);
- }
- }
-
- /**
- * Returns the icon currently used for the password visibility toggle button.
- *
- * @see #setPasswordVisibilityToggleDrawable(Drawable)
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleDrawable
- */
- @Nullable
- public Drawable getPasswordVisibilityToggleDrawable() {
- return mPasswordToggleDrawable;
- }
-
- /**
- * Returns the currently configured content description for the password visibility
- * toggle button.
- *
- * <p>This will be used to describe the navigation action to users through mechanisms
- * such as screen readers.</p>
- */
- @Nullable
- public CharSequence getPasswordVisibilityToggleContentDescription() {
- return mPasswordToggleContentDesc;
- }
-
- /**
- * Returns whether the password visibility toggle functionality is currently enabled.
- *
- * @see #setPasswordVisibilityToggleEnabled(boolean)
- */
- public boolean isPasswordVisibilityToggleEnabled() {
- return mPasswordToggleEnabled;
- }
-
- /**
- * Returns whether the password visibility toggle functionality is enabled or not.
- *
- * <p>When enabled, a button is placed at the end of the EditText which enables the user
- * to switch between the field's input being visibly disguised or not.</p>
- *
- * @param enabled true to enable the functionality
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleEnabled
- */
- public void setPasswordVisibilityToggleEnabled(final boolean enabled) {
- if (mPasswordToggleEnabled != enabled) {
- mPasswordToggleEnabled = enabled;
-
- if (!enabled && mPasswordToggledVisible && mEditText != null) {
- // If the toggle is no longer enabled, but we remove the PasswordTransformation
- // to make the password visible, add it back
- mEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
- }
-
- // Reset the visibility tracking flag
- mPasswordToggledVisible = false;
-
- updatePasswordToggleView();
- }
- }
-
- /**
- * Applies a tint to the the password visibility toggle drawable. Does not modify the current
- * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
- *
- * <p>Subsequent calls to {@link #setPasswordVisibilityToggleDrawable(Drawable)} will
- * automatically mutate the drawable and apply the specified tint and tint mode using
- * {@link DrawableCompat#setTintList(Drawable, ColorStateList)}.</p>
- *
- * @param tintList the tint to apply, may be null to clear tint
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleTint
- */
- public void setPasswordVisibilityToggleTintList(@Nullable ColorStateList tintList) {
- mPasswordToggleTintList = tintList;
- mHasPasswordToggleTintList = true;
- applyPasswordToggleTint();
- }
-
- /**
- * Specifies the blending mode used to apply the tint specified by
- * {@link #setPasswordVisibilityToggleTintList(ColorStateList)} to the password
- * visibility toggle drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.</p>
- *
- * @param mode the blending mode used to apply the tint, may be null to clear tint
- *
- * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleTintMode
- */
- public void setPasswordVisibilityToggleTintMode(@Nullable PorterDuff.Mode mode) {
- mPasswordToggleTintMode = mode;
- mHasPasswordToggleTintMode = true;
- applyPasswordToggleTint();
- }
-
- private void passwordVisibilityToggleRequested(boolean shouldSkipAnimations) {
- if (mPasswordToggleEnabled) {
- // Store the current cursor position
- final int selection = mEditText.getSelectionEnd();
-
- if (hasPasswordTransformation()) {
- mEditText.setTransformationMethod(null);
- mPasswordToggledVisible = true;
- } else {
- mEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
- mPasswordToggledVisible = false;
- }
-
- mPasswordToggleView.setChecked(mPasswordToggledVisible);
- if (shouldSkipAnimations) {
- mPasswordToggleView.jumpDrawablesToCurrentState();
- }
-
- // And restore the cursor position
- mEditText.setSelection(selection);
- }
- }
-
- private boolean hasPasswordTransformation() {
- return mEditText != null
- && mEditText.getTransformationMethod() instanceof PasswordTransformationMethod;
- }
-
- private boolean shouldShowPasswordIcon() {
- return mPasswordToggleEnabled && (hasPasswordTransformation() || mPasswordToggledVisible);
- }
-
- private void applyPasswordToggleTint() {
- if (mPasswordToggleDrawable != null
- && (mHasPasswordToggleTintList || mHasPasswordToggleTintMode)) {
- mPasswordToggleDrawable = DrawableCompat.wrap(mPasswordToggleDrawable).mutate();
-
- if (mHasPasswordToggleTintList) {
- DrawableCompat.setTintList(mPasswordToggleDrawable, mPasswordToggleTintList);
- }
- if (mHasPasswordToggleTintMode) {
- DrawableCompat.setTintMode(mPasswordToggleDrawable, mPasswordToggleTintMode);
- }
-
- if (mPasswordToggleView != null
- && mPasswordToggleView.getDrawable() != mPasswordToggleDrawable) {
- mPasswordToggleView.setImageDrawable(mPasswordToggleDrawable);
- }
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
-
- if (mHintEnabled && mEditText != null) {
- final Rect rect = mTmpRect;
- ViewGroupUtils.getDescendantRect(this, mEditText, rect);
-
- final int l = rect.left + mEditText.getCompoundPaddingLeft();
- final int r = rect.right - mEditText.getCompoundPaddingRight();
-
- mCollapsingTextHelper.setExpandedBounds(
- l, rect.top + mEditText.getCompoundPaddingTop(),
- r, rect.bottom - mEditText.getCompoundPaddingBottom());
-
- // Set the collapsed bounds to be the the full height (minus padding) to match the
- // EditText's editable area
- mCollapsingTextHelper.setCollapsedBounds(l, getPaddingTop(),
- r, bottom - top - getPaddingBottom());
-
- mCollapsingTextHelper.recalculate();
- }
- }
-
- private void collapseHint(boolean animate) {
- if (mAnimator != null && mAnimator.isRunning()) {
- mAnimator.cancel();
- }
- if (animate && mHintAnimationEnabled) {
- animateToExpansionFraction(1f);
- } else {
- mCollapsingTextHelper.setExpansionFraction(1f);
- }
- mHintExpanded = false;
- }
-
- @Override
- protected void drawableStateChanged() {
- if (mInDrawableStateChanged) {
- // Some of the calls below will update the drawable state of child views. Since we're
- // using addStatesFromChildren we can get into infinite recursion, hence we'll just
- // exit in this instance
- return;
- }
-
- mInDrawableStateChanged = true;
-
- super.drawableStateChanged();
-
- final int[] state = getDrawableState();
- boolean changed = false;
-
- // Drawable state has changed so see if we need to update the label
- updateLabelState(ViewCompat.isLaidOut(this) && isEnabled());
-
- updateEditTextBackground();
-
- if (mCollapsingTextHelper != null) {
- changed |= mCollapsingTextHelper.setState(state);
- }
-
- if (changed) {
- invalidate();
- }
-
- mInDrawableStateChanged = false;
- }
-
- private void expandHint(boolean animate) {
- if (mAnimator != null && mAnimator.isRunning()) {
- mAnimator.cancel();
- }
- if (animate && mHintAnimationEnabled) {
- animateToExpansionFraction(0f);
- } else {
- mCollapsingTextHelper.setExpansionFraction(0f);
- }
- mHintExpanded = true;
- }
-
- @VisibleForTesting
- void animateToExpansionFraction(final float target) {
- if (mCollapsingTextHelper.getExpansionFraction() == target) {
- return;
- }
- if (mAnimator == null) {
- mAnimator = new ValueAnimator();
- mAnimator.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR);
- mAnimator.setDuration(ANIMATION_DURATION);
- mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- mCollapsingTextHelper.setExpansionFraction((float) animator.getAnimatedValue());
- }
- });
- }
- mAnimator.setFloatValues(mCollapsingTextHelper.getExpansionFraction(), target);
- mAnimator.start();
- }
-
- @VisibleForTesting
- final boolean isHintExpanded() {
- return mHintExpanded;
- }
-
- private class TextInputAccessibilityDelegate extends AccessibilityDelegateCompat {
- TextInputAccessibilityDelegate() {
- }
-
- @Override
- public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(host, event);
- event.setClassName(TextInputLayout.class.getSimpleName());
- }
-
- @Override
- public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
- super.onPopulateAccessibilityEvent(host, event);
-
- final CharSequence text = mCollapsingTextHelper.getText();
- if (!TextUtils.isEmpty(text)) {
- event.getText().add(text);
- }
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- info.setClassName(TextInputLayout.class.getSimpleName());
-
- final CharSequence text = mCollapsingTextHelper.getText();
- if (!TextUtils.isEmpty(text)) {
- info.setText(text);
- }
- if (mEditText != null) {
- info.setLabelFor(mEditText);
- }
- final CharSequence error = mErrorView != null ? mErrorView.getText() : null;
- if (!TextUtils.isEmpty(error)) {
- info.setContentInvalid(true);
- info.setError(error);
- }
- }
- }
-
- private static boolean arrayContains(int[] array, int value) {
- for (int v : array) {
- if (v == value) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/android/support/design/widget/ThemeUtils.java b/android/support/design/widget/ThemeUtils.java
deleted file mode 100644
index 821dcb64..00000000
--- a/android/support/design/widget/ThemeUtils.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-
-class ThemeUtils {
-
- private static final int[] APPCOMPAT_CHECK_ATTRS = {
- android.support.v7.appcompat.R.attr.colorPrimary
- };
-
- static void checkAppCompatTheme(Context context) {
- TypedArray a = context.obtainStyledAttributes(APPCOMPAT_CHECK_ATTRS);
- final boolean failed = !a.hasValue(0);
- a.recycle();
- if (failed) {
- throw new IllegalArgumentException("You need to use a Theme.AppCompat theme "
- + "(or descendant) with the design library.");
- }
- }
-}
diff --git a/android/support/design/widget/ViewOffsetBehavior.java b/android/support/design/widget/ViewOffsetBehavior.java
deleted file mode 100644
index 541de696..00000000
--- a/android/support/design/widget/ViewOffsetBehavior.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-
-/**
- * Behavior will automatically sets up a {@link ViewOffsetHelper} on a {@link View}.
- */
-class ViewOffsetBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {
-
- private ViewOffsetHelper mViewOffsetHelper;
-
- private int mTempTopBottomOffset = 0;
- private int mTempLeftRightOffset = 0;
-
- public ViewOffsetBehavior() {}
-
- public ViewOffsetBehavior(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
- // First let lay the child out
- layoutChild(parent, child, layoutDirection);
-
- if (mViewOffsetHelper == null) {
- mViewOffsetHelper = new ViewOffsetHelper(child);
- }
- mViewOffsetHelper.onViewLayout();
-
- if (mTempTopBottomOffset != 0) {
- mViewOffsetHelper.setTopAndBottomOffset(mTempTopBottomOffset);
- mTempTopBottomOffset = 0;
- }
- if (mTempLeftRightOffset != 0) {
- mViewOffsetHelper.setLeftAndRightOffset(mTempLeftRightOffset);
- mTempLeftRightOffset = 0;
- }
-
- return true;
- }
-
- protected void layoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
- // Let the parent lay it out by default
- parent.onLayoutChild(child, layoutDirection);
- }
-
- public boolean setTopAndBottomOffset(int offset) {
- if (mViewOffsetHelper != null) {
- return mViewOffsetHelper.setTopAndBottomOffset(offset);
- } else {
- mTempTopBottomOffset = offset;
- }
- return false;
- }
-
- public boolean setLeftAndRightOffset(int offset) {
- if (mViewOffsetHelper != null) {
- return mViewOffsetHelper.setLeftAndRightOffset(offset);
- } else {
- mTempLeftRightOffset = offset;
- }
- return false;
- }
-
- public int getTopAndBottomOffset() {
- return mViewOffsetHelper != null ? mViewOffsetHelper.getTopAndBottomOffset() : 0;
- }
-
- public int getLeftAndRightOffset() {
- return mViewOffsetHelper != null ? mViewOffsetHelper.getLeftAndRightOffset() : 0;
- }
-} \ No newline at end of file
diff --git a/android/support/design/widget/ViewOffsetHelper.java b/android/support/design/widget/ViewOffsetHelper.java
deleted file mode 100644
index 088430af..00000000
--- a/android/support/design/widget/ViewOffsetHelper.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.support.v4.view.ViewCompat;
-import android.view.View;
-
-/**
- * Utility helper for moving a {@link android.view.View} around using
- * {@link android.view.View#offsetLeftAndRight(int)} and
- * {@link android.view.View#offsetTopAndBottom(int)}.
- * <p>
- * Also the setting of absolute offsets (similar to translationX/Y), rather than additive
- * offsets.
- */
-class ViewOffsetHelper {
-
- private final View mView;
-
- private int mLayoutTop;
- private int mLayoutLeft;
- private int mOffsetTop;
- private int mOffsetLeft;
-
- public ViewOffsetHelper(View view) {
- mView = view;
- }
-
- public void onViewLayout() {
- // Now grab the intended top
- mLayoutTop = mView.getTop();
- mLayoutLeft = mView.getLeft();
-
- // And offset it as needed
- updateOffsets();
- }
-
- private void updateOffsets() {
- ViewCompat.offsetTopAndBottom(mView, mOffsetTop - (mView.getTop() - mLayoutTop));
- ViewCompat.offsetLeftAndRight(mView, mOffsetLeft - (mView.getLeft() - mLayoutLeft));
- }
-
- /**
- * Set the top and bottom offset for this {@link ViewOffsetHelper}'s view.
- *
- * @param offset the offset in px.
- * @return true if the offset has changed
- */
- public boolean setTopAndBottomOffset(int offset) {
- if (mOffsetTop != offset) {
- mOffsetTop = offset;
- updateOffsets();
- return true;
- }
- return false;
- }
-
- /**
- * Set the left and right offset for this {@link ViewOffsetHelper}'s view.
- *
- * @param offset the offset in px.
- * @return true if the offset has changed
- */
- public boolean setLeftAndRightOffset(int offset) {
- if (mOffsetLeft != offset) {
- mOffsetLeft = offset;
- updateOffsets();
- return true;
- }
- return false;
- }
-
- public int getTopAndBottomOffset() {
- return mOffsetTop;
- }
-
- public int getLeftAndRightOffset() {
- return mOffsetLeft;
- }
-
- public int getLayoutTop() {
- return mLayoutTop;
- }
-
- public int getLayoutLeft() {
- return mLayoutLeft;
- }
-} \ No newline at end of file
diff --git a/android/support/design/widget/ViewUtils.java b/android/support/design/widget/ViewUtils.java
deleted file mode 100644
index c09eac16..00000000
--- a/android/support/design/widget/ViewUtils.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.graphics.PorterDuff;
-
-class ViewUtils {
- static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {
- switch (value) {
- case 3:
- return PorterDuff.Mode.SRC_OVER;
- case 5:
- return PorterDuff.Mode.SRC_IN;
- case 9:
- return PorterDuff.Mode.SRC_ATOP;
- case 14:
- return PorterDuff.Mode.MULTIPLY;
- case 15:
- return PorterDuff.Mode.SCREEN;
- default:
- return defaultMode;
- }
- }
-
-}
diff --git a/android/support/design/widget/ViewUtilsLollipop.java b/android/support/design/widget/ViewUtilsLollipop.java
deleted file mode 100644
index 5927e9b8..00000000
--- a/android/support/design/widget/ViewUtilsLollipop.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.animation.AnimatorInflater;
-import android.animation.ObjectAnimator;
-import android.animation.StateListAnimator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.annotation.RequiresApi;
-import android.support.design.R;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-
-@RequiresApi(21)
-class ViewUtilsLollipop {
-
- private static final int[] STATE_LIST_ANIM_ATTRS = new int[] {android.R.attr.stateListAnimator};
-
- static void setBoundsViewOutlineProvider(View view) {
- view.setOutlineProvider(ViewOutlineProvider.BOUNDS);
- }
-
- static void setStateListAnimatorFromAttrs(View view, AttributeSet attrs,
- int defStyleAttr, int defStyleRes) {
- final Context context = view.getContext();
- final TypedArray a = context.obtainStyledAttributes(attrs, STATE_LIST_ANIM_ATTRS,
- defStyleAttr, defStyleRes);
- try {
- if (a.hasValue(0)) {
- StateListAnimator sla = AnimatorInflater.loadStateListAnimator(context,
- a.getResourceId(0, 0));
- view.setStateListAnimator(sla);
- }
- } finally {
- a.recycle();
- }
- }
-
- /**
- * Creates and sets a {@link StateListAnimator} with a custom elevation value
- */
- static void setDefaultAppBarLayoutStateListAnimator(final View view, final float elevation) {
- final int dur = view.getResources().getInteger(R.integer.app_bar_elevation_anim_duration);
-
- final StateListAnimator sla = new StateListAnimator();
-
- // Enabled and collapsible, but not collapsed means not elevated
- sla.addState(new int[]{android.R.attr.enabled, R.attr.state_collapsible,
- -R.attr.state_collapsed},
- ObjectAnimator.ofFloat(view, "elevation", 0f).setDuration(dur));
-
- // Default enabled state
- sla.addState(new int[]{android.R.attr.enabled},
- ObjectAnimator.ofFloat(view, "elevation", elevation).setDuration(dur));
-
- // Disabled state
- sla.addState(new int[0],
- ObjectAnimator.ofFloat(view, "elevation", 0).setDuration(0));
-
- view.setStateListAnimator(sla);
- }
-
-}
diff --git a/android/support/design/widget/VisibilityAwareImageButton.java b/android/support/design/widget/VisibilityAwareImageButton.java
deleted file mode 100644
index d7a0b13f..00000000
--- a/android/support/design/widget/VisibilityAwareImageButton.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.design.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.ImageButton;
-
-class VisibilityAwareImageButton extends ImageButton {
-
- private int mUserSetVisibility;
-
- public VisibilityAwareImageButton(Context context) {
- this(context, null);
- }
-
- public VisibilityAwareImageButton(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public VisibilityAwareImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- mUserSetVisibility = getVisibility();
- }
-
- @Override
- public void setVisibility(int visibility) {
- internalSetVisibility(visibility, true);
- }
-
- final void internalSetVisibility(int visibility, boolean fromUser) {
- super.setVisibility(visibility);
- if (fromUser) {
- mUserSetVisibility = visibility;
- }
- }
-
- final int getUserSetVisibility() {
- return mUserSetVisibility;
- }
-}
diff --git a/android/support/graphics/drawable/AndroidResources.java b/android/support/graphics/drawable/AndroidResources.java
index 31370a2b..804c6239 100644
--- a/android/support/graphics/drawable/AndroidResources.java
+++ b/android/support/graphics/drawable/AndroidResources.java
@@ -89,8 +89,7 @@ class AndroidResources {
public static final int[] STYLEABLE_ANIMATOR = {
0x01010141, 0x01010198, 0x010101be, 0x010101bf,
- 0x010101c0, 0x010102de, 0x010102df, 0x010102e0,
- 0x0111009c
+ 0x010101c0, 0x010102de, 0x010102df, 0x010102e0
};
public static final int STYLEABLE_ANIMATOR_INTERPOLATOR = 0;
@@ -101,7 +100,6 @@ class AndroidResources {
public static final int STYLEABLE_ANIMATOR_VALUE_FROM = 5;
public static final int STYLEABLE_ANIMATOR_VALUE_TO = 6;
public static final int STYLEABLE_ANIMATOR_VALUE_TYPE = 7;
- public static final int STYLEABLE_ANIMATOR_REMOVE_BEFORE_M_RELEASE = 8;
public static final int[] STYLEABLE_ANIMATOR_SET = {
0x010102e2
};
diff --git a/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java b/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
index cff61bcc..bc521cc7 100644
--- a/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
+++ b/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
@@ -118,6 +118,9 @@ import java.util.List;
* <td>trimPathStart</td>
* </tr>
* <tr>
+ * <td>trimPathEnd</td>
+ * </tr>
+ * <tr>
* <td>trimPathOffset</td>
* </tr>
* </table>
diff --git a/android/support/graphics/drawable/AnimatorInflaterCompat.java b/android/support/graphics/drawable/AnimatorInflaterCompat.java
index cfededb4..da522f6e 100644
--- a/android/support/graphics/drawable/AnimatorInflaterCompat.java
+++ b/android/support/graphics/drawable/AnimatorInflaterCompat.java
@@ -463,7 +463,6 @@ public class AnimatorInflaterCompat {
// the previously sampled contours' total length.
for (int i = 0; i < numPoints; ++i) {
pathMeasure.getPosTan(currentDistance, position, null);
- pathMeasure.getPosTan(currentDistance, position, null);
mX[i] = position[0];
mY[i] = position[1];
diff --git a/android/support/media/ExifInterface.java b/android/support/media/ExifInterface.java
index eea69ab1..6b437a69 100644
--- a/android/support/media/ExifInterface.java
+++ b/android/support/media/ExifInterface.java
@@ -23,6 +23,7 @@ import android.location.Location;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
import android.util.Log;
import android.util.Pair;
@@ -3550,6 +3551,7 @@ public class ExifInterface {
// Indices of Exif Ifd tag groups
/** @hide */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
@Retention(RetentionPolicy.SOURCE)
@IntDef({IFD_TYPE_PRIMARY, IFD_TYPE_EXIF, IFD_TYPE_GPS, IFD_TYPE_INTEROPERABILITY,
IFD_TYPE_THUMBNAIL, IFD_TYPE_PREVIEW, IFD_TYPE_ORF_MAKER_NOTE,
@@ -4567,6 +4569,7 @@ public class ExifInterface {
* @param timeStamp number of milliseconds since Jan. 1, 1970, midnight local time.
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
public void setDateTime(long timeStamp) {
long sub = timeStamp % 1000;
setAttribute(TAG_DATETIME, sFormatter.format(new Date(timeStamp)));
@@ -4578,6 +4581,7 @@ public class ExifInterface {
* Returns -1 if the date time information if not available.
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
public long getDateTime() {
String dateTimeString = getAttribute(TAG_DATETIME);
if (dateTimeString == null
@@ -4614,6 +4618,7 @@ public class ExifInterface {
* Returns -1 if the date time information if not available.
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
public long getGpsDateTime() {
String date = getAttribute(TAG_GPS_DATESTAMP);
String time = getAttribute(TAG_GPS_TIMESTAMP);
diff --git a/android/support/media/tv/BasePreviewProgram.java b/android/support/media/tv/BasePreviewProgram.java
index eeaa5ea1..816b1a13 100644
--- a/android/support/media/tv/BasePreviewProgram.java
+++ b/android/support/media/tv/BasePreviewProgram.java
@@ -15,6 +15,7 @@
*/
package android.support.media.tv;
+import static android.support.annotation.RestrictTo.Scope.LIBRARY;
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.content.ContentValues;
@@ -39,6 +40,7 @@ import java.util.TimeZone;
*
* @hide
*/
+@RestrictTo(LIBRARY)
public abstract class BasePreviewProgram extends BaseProgram {
/**
* @hide
diff --git a/android/support/media/tv/BaseProgram.java b/android/support/media/tv/BaseProgram.java
index 23b5cf9c..4c7882dd 100644
--- a/android/support/media/tv/BaseProgram.java
+++ b/android/support/media/tv/BaseProgram.java
@@ -15,6 +15,7 @@
*/
package android.support.media.tv;
+import static android.support.annotation.RestrictTo.Scope.LIBRARY;
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.content.ContentValues;
@@ -37,6 +38,7 @@ import java.lang.annotation.RetentionPolicy;
* {@link TvContractCompat}.
* @hide
*/
+@RestrictTo(LIBRARY)
public abstract class BaseProgram {
/**
* @hide
diff --git a/android/support/media/tv/TvContractCompat.java b/android/support/media/tv/TvContractCompat.java
index de4fd04f..bd03bf1b 100644
--- a/android/support/media/tv/TvContractCompat.java
+++ b/android/support/media/tv/TvContractCompat.java
@@ -2422,6 +2422,7 @@ public final class TvContractCompat {
/** Canonical genres for TV programs. */
public static final class Genres {
/** @hide */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
@StringDef({
FAMILY_KIDS,
SPORTS,
diff --git a/android/support/multidex/MultiDex.java b/android/support/multidex/MultiDex.java
index ab7f668b..2b681db0 100644
--- a/android/support/multidex/MultiDex.java
+++ b/android/support/multidex/MultiDex.java
@@ -28,6 +28,7 @@ import dalvik.system.DexFile;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -114,7 +115,8 @@ public final class MultiDex {
new File(applicationInfo.sourceDir),
new File(applicationInfo.dataDir),
CODE_CACHE_SECONDARY_FOLDER_NAME,
- NO_KEY_PREFIX);
+ NO_KEY_PREFIX,
+ true);
} catch (Exception e) {
Log.e(TAG, "MultiDex installation failure", e);
@@ -171,13 +173,15 @@ public final class MultiDex {
new File(instrumentationInfo.sourceDir),
dataDir,
instrumentationPrefix + CODE_CACHE_SECONDARY_FOLDER_NAME,
- instrumentationPrefix);
+ instrumentationPrefix,
+ false);
doInstallation(targetContext,
new File(applicationInfo.sourceDir),
dataDir,
CODE_CACHE_SECONDARY_FOLDER_NAME,
- NO_KEY_PREFIX);
+ NO_KEY_PREFIX,
+ false);
} catch (Exception e) {
Log.e(TAG, "MultiDex installation failure", e);
throw new RuntimeException("MultiDex installation failed (" + e.getMessage() + ").");
@@ -192,11 +196,15 @@ public final class MultiDex {
* @param dataDir data directory to use for code cache simulation.
* @param secondaryFolderName name of the folder for storing extractions.
* @param prefsKeyPrefix prefix of all stored preference keys.
+ * @param reinstallOnPatchRecoverableException if set to true, will attempt a clean extraction
+ * if a possibly recoverable exception occurs during classloader patching.
*/
private static void doInstallation(Context mainContext, File sourceApk, File dataDir,
- String secondaryFolderName, String prefsKeyPrefix) throws IOException,
+ String secondaryFolderName, String prefsKeyPrefix,
+ boolean reinstallOnPatchRecoverableException) throws IOException,
IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
- InvocationTargetException, NoSuchMethodException {
+ InvocationTargetException, NoSuchMethodException, SecurityException,
+ ClassNotFoundException, InstantiationException {
synchronized (installedApk) {
if (installedApk.contains(sourceApk)) {
return;
@@ -245,9 +253,38 @@ public final class MultiDex {
}
File dexDir = getDexDir(mainContext, dataDir, secondaryFolderName);
- List<? extends File> files =
- MultiDexExtractor.load(mainContext, sourceApk, dexDir, prefsKeyPrefix, false);
- installSecondaryDexes(loader, dexDir, files);
+ // MultiDexExtractor is taking the file lock and keeping it until it is closed.
+ // Keep it open during installSecondaryDexes and through forced extraction to ensure no
+ // extraction or optimizing dexopt is running in parallel.
+ MultiDexExtractor extractor = new MultiDexExtractor(sourceApk, dexDir);
+ IOException closeException = null;
+ try {
+ List<? extends File> files =
+ extractor.load(mainContext, prefsKeyPrefix, false);
+ try {
+ installSecondaryDexes(loader, dexDir, files);
+ // Some IOException causes may be fixed by a clean extraction.
+ } catch (IOException e) {
+ if (!reinstallOnPatchRecoverableException) {
+ throw e;
+ }
+ Log.w(TAG, "Failed to install extracted secondary dex files, retrying with "
+ + "forced extraction", e);
+ files = extractor.load(mainContext, prefsKeyPrefix, true);
+ installSecondaryDexes(loader, dexDir, files);
+ }
+ } finally {
+ try {
+ extractor.close();
+ } catch (IOException e) {
+ // Delay throw of close exception to ensure we don't override some exception
+ // thrown during the try block.
+ closeException = e;
+ }
+ }
+ if (closeException != null) {
+ throw closeException;
+ }
}
}
@@ -305,12 +342,13 @@ public final class MultiDex {
private static void installSecondaryDexes(ClassLoader loader, File dexDir,
List<? extends File> files)
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
- InvocationTargetException, NoSuchMethodException, IOException {
+ InvocationTargetException, NoSuchMethodException, IOException, SecurityException,
+ ClassNotFoundException, InstantiationException {
if (!files.isEmpty()) {
if (Build.VERSION.SDK_INT >= 19) {
V19.install(loader, files, dexDir);
} else if (Build.VERSION.SDK_INT >= 14) {
- V14.install(loader, files, dexDir);
+ V14.install(loader, files);
} else {
V4.install(loader, files);
}
@@ -460,11 +498,12 @@ public final class MultiDex {
*/
private static final class V19 {
- private static void install(ClassLoader loader,
+ static void install(ClassLoader loader,
List<? extends File> additionalClassPathEntries,
File optimizedDirectory)
throws IllegalArgumentException, IllegalAccessException,
- NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
+ NoSuchFieldException, InvocationTargetException, NoSuchMethodException,
+ IOException {
/* The patched class loader is expected to be a descendant of
* dalvik.system.BaseDexClassLoader. We modify its
* dalvik.system.DexPathList pathList field to append additional DEX
@@ -500,6 +539,10 @@ public final class MultiDex {
}
suppressedExceptionsField.set(dexPathList, dexElementsSuppressedExceptions);
+
+ IOException exception = new IOException("I/O exception during makeDexElement");
+ exception.initCause(suppressedExceptions.get(0));
+ throw exception;
}
}
@@ -526,11 +569,16 @@ public final class MultiDex {
*/
private static final class V14 {
- private static void install(ClassLoader loader,
- List<? extends File> additionalClassPathEntries,
- File optimizedDirectory)
- throws IllegalArgumentException, IllegalAccessException,
- NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
+ private static final int EXTRACTED_SUFFIX_LENGTH =
+ MultiDexExtractor.EXTRACTED_SUFFIX.length();
+
+ private final Constructor<?> elementConstructor;
+
+ static void install(ClassLoader loader,
+ List<? extends File> additionalClassPathEntries)
+ throws IOException, SecurityException, IllegalArgumentException,
+ ClassNotFoundException, NoSuchMethodException, InstantiationException,
+ IllegalAccessException, InvocationTargetException, NoSuchFieldException {
/* The patched class loader is expected to be a descendant of
* dalvik.system.BaseDexClassLoader. We modify its
* dalvik.system.DexPathList pathList field to append additional DEX
@@ -538,22 +586,52 @@ public final class MultiDex {
*/
Field pathListField = findField(loader, "pathList");
Object dexPathList = pathListField.get(loader);
- expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList,
- new ArrayList<File>(additionalClassPathEntries), optimizedDirectory));
+ expandFieldArray(dexPathList, "dexElements",
+ new V14().makeDexElements(additionalClassPathEntries));
+ }
+
+ private V14() throws ClassNotFoundException, SecurityException, NoSuchMethodException {
+ Class<?> elementClass = Class.forName("dalvik.system.DexPathList$Element");
+ elementConstructor =
+ elementClass.getConstructor(File.class, ZipFile.class, DexFile.class);
+ elementConstructor.setAccessible(true);
}
/**
- * A wrapper around
- * {@code private static final dalvik.system.DexPathList#makeDexElements}.
+ * An emulation of {@code private static final dalvik.system.DexPathList#makeDexElements}
+ * accepting only extracted secondary dex files.
+ * OS version is catching IOException and just logging some of them, this version is letting
+ * them through.
*/
- private static Object[] makeDexElements(
- Object dexPathList, ArrayList<File> files, File optimizedDirectory)
- throws IllegalAccessException, InvocationTargetException,
- NoSuchMethodException {
- Method makeDexElements =
- findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class);
+ private Object[] makeDexElements(List<? extends File> files)
+ throws IOException, SecurityException, IllegalArgumentException,
+ InstantiationException, IllegalAccessException, InvocationTargetException {
+ Object[] elements = new Object[files.size()];
+ for (int i = 0; i < elements.length; i++) {
+ File file = files.get(i);
+ elements[i] = elementConstructor.newInstance(
+ file,
+ new ZipFile(file),
+ DexFile.loadDex(file.getPath(), optimizedPathFor(file), 0));
+ }
+ return elements;
+ }
- return (Object[]) makeDexElements.invoke(dexPathList, files, optimizedDirectory);
+ /**
+ * Converts a zip file path of an extracted secondary dex to an output file path for an
+ * associated optimized dex file.
+ */
+ private static String optimizedPathFor(File path) {
+ // Any reproducible name ending with ".dex" should do but lets keep the same name
+ // as DexPathList.optimizedPathFor
+
+ File optimizedDirectory = path.getParentFile();
+ String fileName = path.getName();
+ String optimizedFileName =
+ fileName.substring(0, fileName.length() - EXTRACTED_SUFFIX_LENGTH)
+ + MultiDexExtractor.DEX_SUFFIX;
+ File result = new File(optimizedDirectory, optimizedFileName);
+ return result.getPath();
}
}
@@ -561,7 +639,7 @@ public final class MultiDex {
* Installer for platform versions 4 to 13.
*/
private static final class V4 {
- private static void install(ClassLoader loader,
+ static void install(ClassLoader loader,
List<? extends File> additionalClassPathEntries)
throws IllegalArgumentException, IllegalAccessException,
NoSuchFieldException, IOException {
diff --git a/android/support/multidex/MultiDexExtractor.java b/android/support/multidex/MultiDexExtractor.java
index 39b6bf78..f0fd6d42 100644
--- a/android/support/multidex/MultiDexExtractor.java
+++ b/android/support/multidex/MultiDexExtractor.java
@@ -40,8 +40,10 @@ import java.util.zip.ZipOutputStream;
/**
* Exposes application secondary dex files as files in the application data
* directory.
+ * {@link MultiDexExtractor} is taking the file lock in the dex dir on creation and release it
+ * during close.
*/
-final class MultiDexExtractor {
+final class MultiDexExtractor implements Closeable {
/**
* Zip file containing one secondary dex file.
@@ -61,10 +63,10 @@ final class MultiDexExtractor {
* {@code classes3.dex}, etc.
*/
private static final String DEX_PREFIX = "classes";
- private static final String DEX_SUFFIX = ".dex";
+ static final String DEX_SUFFIX = ".dex";
private static final String EXTRACTED_NAME_EXT = ".classes";
- private static final String EXTRACTED_SUFFIX = ".zip";
+ static final String EXTRACTED_SUFFIX = ".zip";
private static final int MAX_EXTRACT_ATTEMPTS = 3;
private static final String PREFS_FILE = "multidex.version";
@@ -82,6 +84,35 @@ final class MultiDexExtractor {
private static final long NO_VALUE = -1L;
private static final String LOCK_FILENAME = "MultiDex.lock";
+ private final File sourceApk;
+ private final long sourceCrc;
+ private final File dexDir;
+ private final RandomAccessFile lockRaf;
+ private final FileChannel lockChannel;
+ private final FileLock cacheLock;
+
+ MultiDexExtractor(File sourceApk, File dexDir) throws IOException {
+ Log.i(TAG, "MultiDexExtractor(" + sourceApk.getPath() + ", " + dexDir.getPath() + ")");
+ this.sourceApk = sourceApk;
+ this.dexDir = dexDir;
+ sourceCrc = getZipCrc(sourceApk);
+ File lockFile = new File(dexDir, LOCK_FILENAME);
+ lockRaf = new RandomAccessFile(lockFile, "rw");
+ try {
+ lockChannel = lockRaf.getChannel();
+ try {
+ Log.i(TAG, "Blocking on lock " + lockFile.getPath());
+ cacheLock = lockChannel.lock();
+ } catch (IOException | RuntimeException | Error e) {
+ closeQuietly(lockChannel);
+ throw e;
+ }
+ Log.i(TAG, lockFile.getPath() + " locked");
+ } catch (IOException | RuntimeException | Error e) {
+ closeQuietly(lockRaf);
+ throw e;
+ }
+ }
/**
* Extracts application secondary dexes into files in the application data
@@ -92,74 +123,54 @@ final class MultiDexExtractor {
* @throws IOException if encounters a problem while reading or writing
* secondary dex files
*/
- static List<? extends File> load(Context context, File sourceApk, File dexDir,
- String prefsKeyPrefix,
- boolean forceReload) throws IOException {
+ List<? extends File> load(Context context, String prefsKeyPrefix, boolean forceReload)
+ throws IOException {
Log.i(TAG, "MultiDexExtractor.load(" + sourceApk.getPath() + ", " + forceReload + ", " +
prefsKeyPrefix + ")");
- long currentCrc = getZipCrc(sourceApk);
+ if (!cacheLock.isValid()) {
+ throw new IllegalStateException("MultiDexExtractor was closed");
+ }
- // Validity check and extraction must be done only while the lock file has been taken.
- File lockFile = new File(dexDir, LOCK_FILENAME);
- RandomAccessFile lockRaf = new RandomAccessFile(lockFile, "rw");
- FileChannel lockChannel = null;
- FileLock cacheLock = null;
List<ExtractedDex> files;
- IOException releaseLockException = null;
- try {
- lockChannel = lockRaf.getChannel();
- Log.i(TAG, "Blocking on lock " + lockFile.getPath());
- cacheLock = lockChannel.lock();
- Log.i(TAG, lockFile.getPath() + " locked");
-
- if (!forceReload && !isModified(context, sourceApk, currentCrc, prefsKeyPrefix)) {
- try {
- files = loadExistingExtractions(context, sourceApk, dexDir, prefsKeyPrefix);
- } catch (IOException ioe) {
- Log.w(TAG, "Failed to reload existing extracted secondary dex files,"
- + " falling back to fresh extraction", ioe);
- files = performExtractions(sourceApk, dexDir);
- putStoredApkInfo(context, prefsKeyPrefix, getTimeStamp(sourceApk), currentCrc,
- files);
- }
- } else {
- Log.i(TAG, "Detected that extraction must be performed.");
- files = performExtractions(sourceApk, dexDir);
- putStoredApkInfo(context, prefsKeyPrefix, getTimeStamp(sourceApk), currentCrc,
+ if (!forceReload && !isModified(context, sourceApk, sourceCrc, prefsKeyPrefix)) {
+ try {
+ files = loadExistingExtractions(context, prefsKeyPrefix);
+ } catch (IOException ioe) {
+ Log.w(TAG, "Failed to reload existing extracted secondary dex files,"
+ + " falling back to fresh extraction", ioe);
+ files = performExtractions();
+ putStoredApkInfo(context, prefsKeyPrefix, getTimeStamp(sourceApk), sourceCrc,
files);
}
- } finally {
- if (cacheLock != null) {
- try {
- cacheLock.release();
- } catch (IOException e) {
- Log.e(TAG, "Failed to release lock on " + lockFile.getPath());
- // Exception while releasing the lock is bad, we want to report it, but not at
- // the price of overriding any already pending exception.
- releaseLockException = e;
- }
- }
- if (lockChannel != null) {
- closeQuietly(lockChannel);
+ } else {
+ if (forceReload) {
+ Log.i(TAG, "Forced extraction must be performed.");
+ } else {
+ Log.i(TAG, "Detected that extraction must be performed.");
}
- closeQuietly(lockRaf);
- }
-
- if (releaseLockException != null) {
- throw releaseLockException;
+ files = performExtractions();
+ putStoredApkInfo(context, prefsKeyPrefix, getTimeStamp(sourceApk), sourceCrc,
+ files);
}
Log.i(TAG, "load found " + files.size() + " secondary dex files");
return files;
}
+ @Override
+ public void close() throws IOException {
+ cacheLock.release();
+ lockChannel.close();
+ lockRaf.close();
+ }
+
/**
* Load previously extracted secondary dex files. Should be called only while owning the lock on
* {@link #LOCK_FILENAME}.
*/
- private static List<ExtractedDex> loadExistingExtractions(
- Context context, File sourceApk, File dexDir,
+ private List<ExtractedDex> loadExistingExtractions(
+ Context context,
String prefsKeyPrefix)
throws IOException {
Log.i(TAG, "loading existing secondary dex files");
@@ -228,16 +239,14 @@ final class MultiDexExtractor {
return computedValue;
}
- private static List<ExtractedDex> performExtractions(File sourceApk, File dexDir)
- throws IOException {
+ private List<ExtractedDex> performExtractions() throws IOException {
final String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
- // Ensure that whatever deletions happen in prepareDexDir only happen if the zip that
- // contains a secondary dex file in there is not consistent with the latest apk. Otherwise,
- // multi-process race conditions can cause a crash loop where one process deletes the zip
- // while another had created it.
- prepareDexDir(dexDir, extractedFilePrefix);
+ // It is safe to fully clear the dex dir because we own the file lock so no other process is
+ // extracting or running optimizing dexopt. It may cause crash of already running
+ // applications if for whatever reason we end up extracting again over a valid extraction.
+ clearDexDir();
List<ExtractedDex> files = new ArrayList<ExtractedDex>();
@@ -272,9 +281,9 @@ final class MultiDexExtractor {
}
// Log size and crc of the extracted zip file
- Log.i(TAG, "Extraction " + (isExtractionSuccessful ? "succeeded" : "failed") +
- " - length " + extractedFile.getAbsolutePath() + ": " +
- extractedFile.length() + " - crc: " + extractedFile.crc);
+ Log.i(TAG, "Extraction " + (isExtractionSuccessful ? "succeeded" : "failed")
+ + " '" + extractedFile.getAbsolutePath() + "': length "
+ + extractedFile.length() + " - crc: " + extractedFile.crc);
if (!isExtractionSuccessful) {
// Delete the extracted file
extractedFile.delete();
@@ -339,19 +348,15 @@ final class MultiDexExtractor {
}
/**
- * This removes old files.
+ * Clear the dex dir from all files but the lock.
*/
- private static void prepareDexDir(File dexDir, final String extractedFilePrefix) {
- FileFilter filter = new FileFilter() {
-
+ private void clearDexDir() {
+ File[] files = dexDir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
- String name = pathname.getName();
- return !(name.startsWith(extractedFilePrefix)
- || name.equals(LOCK_FILENAME));
+ return !pathname.getName().equals(LOCK_FILENAME);
}
- };
- File[] files = dexDir.listFiles(filter);
+ });
if (files == null) {
Log.w(TAG, "Failed to list secondary dex dir content (" + dexDir.getPath() + ").");
return;
diff --git a/android/support/transition/TransitionSet.java b/android/support/transition/TransitionSet.java
index 404245a1..24075bbf 100644
--- a/android/support/transition/TransitionSet.java
+++ b/android/support/transition/TransitionSet.java
@@ -51,7 +51,7 @@ import java.util.ArrayList;
* transition on the affected view targets:</p>
* <pre>
* &lt;transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
- * android:ordering="sequential"&gt;
+ * android:transitionOrdering="sequential"&gt;
* &lt;fade/&gt;
* &lt;changeBounds/&gt;
* &lt;/transitionSet&gt;
@@ -561,6 +561,15 @@ public class TransitionSet extends Transition {
}
@Override
+ public void setPropagation(TransitionPropagation propagation) {
+ super.setPropagation(propagation);
+ int numTransitions = mTransitions.size();
+ for (int i = 0; i < numTransitions; ++i) {
+ mTransitions.get(i).setPropagation(propagation);
+ }
+ }
+
+ @Override
public void setEpicenterCallback(EpicenterCallback epicenterCallback) {
super.setEpicenterCallback(epicenterCallback);
int numTransitions = mTransitions.size();
diff --git a/android/support/transition/TransitionSetTest.java b/android/support/transition/TransitionSetTest.java
index aec9ecb2..d82cd498 100644
--- a/android/support/transition/TransitionSetTest.java
+++ b/android/support/transition/TransitionSetTest.java
@@ -120,4 +120,12 @@ public class TransitionSetTest extends BaseTest {
assertThat(mTransition.getTargetTypes(), hasSize(0));
}
+ @Test
+ public void testSetPropagation() {
+ final TransitionPropagation propagation = new SidePropagation();
+ mTransitionSet.setPropagation(propagation);
+ assertThat(mTransitionSet.getPropagation(), is(propagation));
+ assertThat(mTransition.getPropagation(), is(propagation));
+ }
+
}
diff --git a/android/support/v13/view/DragAndDropPermissionsCompat.java b/android/support/v13/view/DragAndDropPermissionsCompat.java
index 13ed2038..5fe61da9 100644
--- a/android/support/v13/view/DragAndDropPermissionsCompat.java
+++ b/android/support/v13/view/DragAndDropPermissionsCompat.java
@@ -20,57 +20,16 @@ import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.app.Activity;
import android.os.Build;
-import android.support.annotation.RequiresApi;
+import android.support.annotation.Nullable;
import android.support.annotation.RestrictTo;
import android.view.DragAndDropPermissions;
import android.view.DragEvent;
/**
- * Helper for accessing features in {@link android.view.DragAndDropPermissions}
- * introduced after API level 13 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.view.DragAndDropPermissions} a backwards
+ * compatible fashion.
*/
public final class DragAndDropPermissionsCompat {
-
- interface DragAndDropPermissionsCompatImpl {
- Object request(Activity activity, DragEvent dragEvent);
- void release(Object dragAndDropPermissions);
- }
-
- static class BaseDragAndDropPermissionsCompatImpl implements DragAndDropPermissionsCompatImpl {
- @Override
- public Object request(Activity activity, DragEvent dragEvent) {
- return null;
- }
-
- @Override
- public void release(Object dragAndDropPermissions) {
- // no-op
- }
- }
-
- @RequiresApi(24)
- static class Api24DragAndDropPermissionsCompatImpl
- extends BaseDragAndDropPermissionsCompatImpl {
- @Override
- public Object request(Activity activity, DragEvent dragEvent) {
- return activity.requestDragAndDropPermissions(dragEvent);
- }
-
- @Override
- public void release(Object dragAndDropPermissions) {
- ((DragAndDropPermissions) dragAndDropPermissions).release();
- }
- }
-
- private static DragAndDropPermissionsCompatImpl IMPL;
- static {
- if (Build.VERSION.SDK_INT >= 24) {
- IMPL = new Api24DragAndDropPermissionsCompatImpl();
- } else {
- IMPL = new BaseDragAndDropPermissionsCompatImpl();
- }
- }
-
private Object mDragAndDropPermissions;
private DragAndDropPermissionsCompat(Object dragAndDropPermissions) {
@@ -79,18 +38,24 @@ public final class DragAndDropPermissionsCompat {
/** @hide */
@RestrictTo(LIBRARY_GROUP)
+ @Nullable
public static DragAndDropPermissionsCompat request(Activity activity, DragEvent dragEvent) {
- Object dragAndDropPermissions = IMPL.request(activity, dragEvent);
- if (dragAndDropPermissions != null) {
- return new DragAndDropPermissionsCompat(dragAndDropPermissions);
+ if (Build.VERSION.SDK_INT >= 24) {
+ DragAndDropPermissions dragAndDropPermissions =
+ activity.requestDragAndDropPermissions(dragEvent);
+ if (dragAndDropPermissions != null) {
+ return new DragAndDropPermissionsCompat(dragAndDropPermissions);
+ }
}
return null;
}
- /*
+ /**
* Revoke the permission grant explicitly.
*/
public void release() {
- IMPL.release(mDragAndDropPermissions);
+ if (Build.VERSION.SDK_INT >= 24) {
+ ((DragAndDropPermissions) mDragAndDropPermissions).release();
+ }
}
}
diff --git a/android/support/v13/view/DragStartHelper.java b/android/support/v13/view/DragStartHelper.java
index 85bc2f36..f8aed922 100644
--- a/android/support/v13/view/DragStartHelper.java
+++ b/android/support/v13/view/DragStartHelper.java
@@ -69,8 +69,8 @@ import android.view.View;
* </pre>
*/
public class DragStartHelper {
- final private View mView;
- final private OnDragStartListener mListener;
+ private final View mView;
+ private final OnDragStartListener mListener;
private int mLastTouchX, mLastTouchY;
private boolean mDragging;
diff --git a/android/support/v13/view/inputmethod/EditorInfoCompat.java b/android/support/v13/view/inputmethod/EditorInfoCompat.java
index 92743c25..309877dc 100644
--- a/android/support/v13/view/inputmethod/EditorInfoCompat.java
+++ b/android/support/v13/view/inputmethod/EditorInfoCompat.java
@@ -16,7 +16,6 @@
package android.support.v13.view.inputmethod;
-import android.support.annotation.RequiresApi;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
@@ -24,8 +23,7 @@ import android.support.annotation.Nullable;
import android.view.inputmethod.EditorInfo;
/**
- * Helper for accessing features in {@link EditorInfo} introduced after API level 13 in a backwards
- * compatible fashion.
+ * Helper for accessing features in {@link EditorInfo} in a backwards compatible fashion.
*/
public final class EditorInfoCompat {
@@ -69,63 +67,10 @@ public final class EditorInfoCompat {
*/
public static final int IME_FLAG_FORCE_ASCII = 0x80000000;
- private interface EditorInfoCompatImpl {
- void setContentMimeTypes(@NonNull EditorInfo editorInfo,
- @Nullable String[] contentMimeTypes);
- @NonNull
- String[] getContentMimeTypes(@NonNull EditorInfo editorInfo);
- }
-
private static final String[] EMPTY_STRING_ARRAY = new String[0];
- private static final class EditorInfoCompatBaseImpl implements EditorInfoCompatImpl {
- private static String CONTENT_MIME_TYPES_KEY =
- "android.support.v13.view.inputmethod.EditorInfoCompat.CONTENT_MIME_TYPES";
-
- @Override
- public void setContentMimeTypes(@NonNull EditorInfo editorInfo,
- @Nullable String[] contentMimeTypes) {
- if (editorInfo.extras == null) {
- editorInfo.extras = new Bundle();
- }
- editorInfo.extras.putStringArray(CONTENT_MIME_TYPES_KEY, contentMimeTypes);
- }
-
- @NonNull
- @Override
- public String[] getContentMimeTypes(@NonNull EditorInfo editorInfo) {
- if (editorInfo.extras == null) {
- return EMPTY_STRING_ARRAY;
- }
- String[] result = editorInfo.extras.getStringArray(CONTENT_MIME_TYPES_KEY);
- return result != null ? result : EMPTY_STRING_ARRAY;
- }
- }
-
- @RequiresApi(25)
- private static final class EditorInfoCompatApi25Impl implements EditorInfoCompatImpl {
- @Override
- public void setContentMimeTypes(@NonNull EditorInfo editorInfo,
- @Nullable String[] contentMimeTypes) {
- editorInfo.contentMimeTypes = contentMimeTypes;
- }
-
- @NonNull
- @Override
- public String[] getContentMimeTypes(@NonNull EditorInfo editorInfo) {
- final String[] result = editorInfo.contentMimeTypes;
- return result != null ? result : EMPTY_STRING_ARRAY;
- }
- }
-
- private static final EditorInfoCompatImpl IMPL;
- static {
- if (Build.VERSION.SDK_INT >= 25) {
- IMPL = new EditorInfoCompatApi25Impl();
- } else {
- IMPL = new EditorInfoCompatBaseImpl();
- }
- }
+ private static final String CONTENT_MIME_TYPES_KEY =
+ "android.support.v13.view.inputmethod.EditorInfoCompat.CONTENT_MIME_TYPES";
/**
* Sets MIME types that can be accepted by the target editor if the IME calls
@@ -140,7 +85,14 @@ public final class EditorInfoCompat {
*/
public static void setContentMimeTypes(@NonNull EditorInfo editorInfo,
@Nullable String[] contentMimeTypes) {
- IMPL.setContentMimeTypes(editorInfo, contentMimeTypes);
+ if (Build.VERSION.SDK_INT >= 25) {
+ editorInfo.contentMimeTypes = contentMimeTypes;
+ } else {
+ if (editorInfo.extras == null) {
+ editorInfo.extras = new Bundle();
+ }
+ editorInfo.extras.putStringArray(CONTENT_MIME_TYPES_KEY, contentMimeTypes);
+ }
}
/**
@@ -155,7 +107,16 @@ public final class EditorInfoCompat {
*/
@NonNull
public static String[] getContentMimeTypes(EditorInfo editorInfo) {
- return IMPL.getContentMimeTypes(editorInfo);
+ if (Build.VERSION.SDK_INT >= 25) {
+ final String[] result = editorInfo.contentMimeTypes;
+ return result != null ? result : EMPTY_STRING_ARRAY;
+ } else {
+ if (editorInfo.extras == null) {
+ return EMPTY_STRING_ARRAY;
+ }
+ String[] result = editorInfo.extras.getStringArray(CONTENT_MIME_TYPES_KEY);
+ return result != null ? result : EMPTY_STRING_ARRAY;
+ }
}
}
diff --git a/android/support/v13/view/inputmethod/InputConnectionCompat.java b/android/support/v13/view/inputmethod/InputConnectionCompat.java
index 5999575b..d77389bf 100644
--- a/android/support/v13/view/inputmethod/InputConnectionCompat.java
+++ b/android/support/v13/view/inputmethod/InputConnectionCompat.java
@@ -16,7 +16,6 @@
package android.support.v13.view.inputmethod;
-import android.support.annotation.RequiresApi;
import android.content.ClipDescription;
import android.net.Uri;
import android.os.Build;
@@ -36,138 +35,50 @@ import android.view.inputmethod.InputContentInfo;
*/
public final class InputConnectionCompat {
- private interface InputConnectionCompatImpl {
- boolean commitContent(@NonNull InputConnection inputConnection,
- @NonNull InputContentInfoCompat inputContentInfo, int flags, @Nullable Bundle opts);
-
- @NonNull
- InputConnection createWrapper(@NonNull InputConnection ic,
- @NonNull EditorInfo editorInfo, @NonNull OnCommitContentListener callback);
- }
-
- static final class InputContentInfoCompatBaseImpl implements InputConnectionCompatImpl {
-
- private static String COMMIT_CONTENT_ACTION =
- "android.support.v13.view.inputmethod.InputConnectionCompat.COMMIT_CONTENT";
- private static String COMMIT_CONTENT_CONTENT_URI_KEY =
- "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_URI";
- private static String COMMIT_CONTENT_DESCRIPTION_KEY =
- "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_DESCRIPTION";
- private static String COMMIT_CONTENT_LINK_URI_KEY =
- "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_LINK_URI";
- private static String COMMIT_CONTENT_OPTS_KEY =
- "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_OPTS";
- private static String COMMIT_CONTENT_FLAGS_KEY =
- "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_FLAGS";
- private static String COMMIT_CONTENT_RESULT_RECEIVER =
- "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_RESULT_RECEIVER";
-
- @Override
- public boolean commitContent(@NonNull InputConnection inputConnection,
- @NonNull InputContentInfoCompat inputContentInfo, int flags,
- @Nullable Bundle opts) {
- final Bundle params = new Bundle();
- params.putParcelable(COMMIT_CONTENT_CONTENT_URI_KEY, inputContentInfo.getContentUri());
- params.putParcelable(COMMIT_CONTENT_DESCRIPTION_KEY, inputContentInfo.getDescription());
- params.putParcelable(COMMIT_CONTENT_LINK_URI_KEY, inputContentInfo.getLinkUri());
- params.putInt(COMMIT_CONTENT_FLAGS_KEY, flags);
- params.putParcelable(COMMIT_CONTENT_OPTS_KEY, opts);
- // TODO: Support COMMIT_CONTENT_RESULT_RECEIVER.
- return inputConnection.performPrivateCommand(COMMIT_CONTENT_ACTION, params);
+ private static final String COMMIT_CONTENT_ACTION =
+ "android.support.v13.view.inputmethod.InputConnectionCompat.COMMIT_CONTENT";
+ private static final String COMMIT_CONTENT_CONTENT_URI_KEY =
+ "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_URI";
+ private static final String COMMIT_CONTENT_DESCRIPTION_KEY =
+ "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_DESCRIPTION";
+ private static final String COMMIT_CONTENT_LINK_URI_KEY =
+ "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_LINK_URI";
+ private static final String COMMIT_CONTENT_OPTS_KEY =
+ "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_OPTS";
+ private static final String COMMIT_CONTENT_FLAGS_KEY =
+ "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_FLAGS";
+ private static final String COMMIT_CONTENT_RESULT_RECEIVER =
+ "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_RESULT_RECEIVER";
+
+ static boolean handlePerformPrivateCommand(
+ @Nullable String action,
+ @NonNull Bundle data,
+ @NonNull OnCommitContentListener onCommitContentListener) {
+ if (!TextUtils.equals(COMMIT_CONTENT_ACTION, action)) {
+ return false;
}
-
- @NonNull
- @Override
- public InputConnection createWrapper(@NonNull InputConnection ic,
- @NonNull EditorInfo editorInfo,
- @NonNull OnCommitContentListener onCommitContentListener) {
- String[] contentMimeTypes = EditorInfoCompat.getContentMimeTypes(editorInfo);
- if (contentMimeTypes.length == 0) {
- return ic;
- }
- final OnCommitContentListener listener = onCommitContentListener;
- return new InputConnectionWrapper(ic, false /* mutable */) {
- @Override
- public boolean performPrivateCommand(String action, Bundle data) {
- if (InputContentInfoCompatBaseImpl.handlePerformPrivateCommand(action, data,
- listener)) {
- return true;
- }
- return super.performPrivateCommand(action, data);
- }
- };
+ if (data == null) {
+ return false;
}
-
- static boolean handlePerformPrivateCommand(
- @Nullable String action,
- @NonNull Bundle data,
- @NonNull OnCommitContentListener onCommitContentListener) {
- if (!TextUtils.equals(COMMIT_CONTENT_ACTION, action)) {
- return false;
+ ResultReceiver resultReceiver = null;
+ boolean result = false;
+ try {
+ resultReceiver = data.getParcelable(COMMIT_CONTENT_RESULT_RECEIVER);
+ final Uri contentUri = data.getParcelable(COMMIT_CONTENT_CONTENT_URI_KEY);
+ final ClipDescription description = data.getParcelable(
+ COMMIT_CONTENT_DESCRIPTION_KEY);
+ final Uri linkUri = data.getParcelable(COMMIT_CONTENT_LINK_URI_KEY);
+ final int flags = data.getInt(COMMIT_CONTENT_FLAGS_KEY);
+ final Bundle opts = data.getParcelable(COMMIT_CONTENT_OPTS_KEY);
+ final InputContentInfoCompat inputContentInfo =
+ new InputContentInfoCompat(contentUri, description, linkUri);
+ result = onCommitContentListener.onCommitContent(inputContentInfo, flags, opts);
+ } finally {
+ if (resultReceiver != null) {
+ resultReceiver.send(result ? 1 : 0, null);
}
- if (data == null) {
- return false;
- }
- ResultReceiver resultReceiver = null;
- boolean result = false;
- try {
- resultReceiver = data.getParcelable(COMMIT_CONTENT_RESULT_RECEIVER);
- final Uri contentUri = data.getParcelable(COMMIT_CONTENT_CONTENT_URI_KEY);
- final ClipDescription description = data.getParcelable(
- COMMIT_CONTENT_DESCRIPTION_KEY);
- final Uri linkUri = data.getParcelable(COMMIT_CONTENT_LINK_URI_KEY);
- final int flags = data.getInt(COMMIT_CONTENT_FLAGS_KEY);
- final Bundle opts = data.getParcelable(COMMIT_CONTENT_OPTS_KEY);
- final InputContentInfoCompat inputContentInfo =
- new InputContentInfoCompat(contentUri, description, linkUri);
- result = onCommitContentListener.onCommitContent(inputContentInfo, flags, opts);
- } finally {
- if (resultReceiver != null) {
- resultReceiver.send(result ? 1 : 0, null);
- }
- }
- return result;
- }
- }
-
- @RequiresApi(25)
- private static final class InputContentInfoCompatApi25Impl
- implements InputConnectionCompatImpl {
- @Override
- public boolean commitContent(@NonNull InputConnection inputConnection,
- @NonNull InputContentInfoCompat inputContentInfo, int flags,
- @Nullable Bundle opts) {
- return inputConnection.commitContent((InputContentInfo) inputContentInfo.unwrap(),
- flags, opts);
- }
-
- @Nullable
- @Override
- public InputConnection createWrapper(
- @Nullable InputConnection inputConnection, @NonNull EditorInfo editorInfo,
- @Nullable OnCommitContentListener onCommitContentListener) {
- final OnCommitContentListener listener = onCommitContentListener;
- return new InputConnectionWrapper(inputConnection, false /* mutable */) {
- @Override
- public boolean commitContent(InputContentInfo inputContentInfo, int flags,
- Bundle opts) {
- if (listener.onCommitContent(InputContentInfoCompat.wrap(inputContentInfo),
- flags, opts)) {
- return true;
- }
- return super.commitContent(inputContentInfo, flags, opts);
- }
- };
- }
- }
-
- private static final InputConnectionCompatImpl IMPL;
- static {
- if (Build.VERSION.SDK_INT >= 25) {
- IMPL = new InputContentInfoCompatApi25Impl();
- } else {
- IMPL = new InputContentInfoCompatBaseImpl();
}
+ return result;
}
/**
@@ -196,7 +107,19 @@ public final class InputConnectionCompat {
return false;
}
- return IMPL.commitContent(inputConnection, inputContentInfo, flags, opts);
+ if (Build.VERSION.SDK_INT >= 25) {
+ return inputConnection.commitContent(
+ (InputContentInfo) inputContentInfo.unwrap(), flags, opts);
+ } else {
+ final Bundle params = new Bundle();
+ params.putParcelable(COMMIT_CONTENT_CONTENT_URI_KEY, inputContentInfo.getContentUri());
+ params.putParcelable(COMMIT_CONTENT_DESCRIPTION_KEY, inputContentInfo.getDescription());
+ params.putParcelable(COMMIT_CONTENT_LINK_URI_KEY, inputContentInfo.getLinkUri());
+ params.putInt(COMMIT_CONTENT_FLAGS_KEY, flags);
+ params.putParcelable(COMMIT_CONTENT_OPTS_KEY, opts);
+ // TODO: Support COMMIT_CONTENT_RESULT_RECEIVER.
+ return inputConnection.performPrivateCommand(COMMIT_CONTENT_ACTION, params);
+ }
}
/**
@@ -276,7 +199,35 @@ public final class InputConnectionCompat {
if (onCommitContentListener == null) {
throw new IllegalArgumentException("onCommitContentListener must be non-null");
}
- return IMPL.createWrapper(inputConnection, editorInfo, onCommitContentListener);
+ if (Build.VERSION.SDK_INT >= 25) {
+ final OnCommitContentListener listener = onCommitContentListener;
+ return new InputConnectionWrapper(inputConnection, false /* mutable */) {
+ @Override
+ public boolean commitContent(InputContentInfo inputContentInfo, int flags,
+ Bundle opts) {
+ if (listener.onCommitContent(InputContentInfoCompat.wrap(inputContentInfo),
+ flags, opts)) {
+ return true;
+ }
+ return super.commitContent(inputContentInfo, flags, opts);
+ }
+ };
+ } else {
+ String[] contentMimeTypes = EditorInfoCompat.getContentMimeTypes(editorInfo);
+ if (contentMimeTypes.length == 0) {
+ return inputConnection;
+ }
+ final OnCommitContentListener listener = onCommitContentListener;
+ return new InputConnectionWrapper(inputConnection, false /* mutable */) {
+ @Override
+ public boolean performPrivateCommand(String action, Bundle data) {
+ if (InputConnectionCompat.handlePerformPrivateCommand(action, data, listener)) {
+ return true;
+ }
+ return super.performPrivateCommand(action, data);
+ }
+ };
+ }
}
}
diff --git a/android/support/v14/preference/EditTextPreferenceDialogFragment.java b/android/support/v14/preference/EditTextPreferenceDialogFragment.java
index 3ee58725..23b88288 100644
--- a/android/support/v14/preference/EditTextPreferenceDialogFragment.java
+++ b/android/support/v14/preference/EditTextPreferenceDialogFragment.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package android.support.v14.preference;
@@ -65,8 +65,8 @@ public class EditTextPreferenceDialogFragment extends PreferenceDialogFragment {
mEditText = (EditText) view.findViewById(android.R.id.edit);
if (mEditText == null) {
- throw new IllegalStateException("Dialog view must contain an EditText with id" +
- " @android:id/edit");
+ throw new IllegalStateException("Dialog view must contain an EditText with id"
+ + " @android:id/edit");
}
mEditText.setText(mText);
diff --git a/android/support/v14/preference/ListPreferenceDialogFragment.java b/android/support/v14/preference/ListPreferenceDialogFragment.java
index 6119071b..5374cd53 100644
--- a/android/support/v14/preference/ListPreferenceDialogFragment.java
+++ b/android/support/v14/preference/ListPreferenceDialogFragment.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package android.support.v14.preference;
diff --git a/android/support/v14/preference/MultiSelectListPreference.java b/android/support/v14/preference/MultiSelectListPreference.java
index f34b7dcd..16351fec 100644
--- a/android/support/v14/preference/MultiSelectListPreference.java
+++ b/android/support/v14/preference/MultiSelectListPreference.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package android.support.v14.preference;
@@ -23,6 +23,7 @@ import android.os.Parcelable;
import android.support.annotation.ArrayRes;
import android.support.annotation.NonNull;
import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v7.preference.R;
import android.support.v7.preference.internal.AbstractMultiSelectListPreference;
import android.util.AttributeSet;
@@ -35,7 +36,7 @@ import java.util.Set;
* a dialog.
* <p>
* This preference will store a set of strings into the SharedPreferences.
- * This set will contain one or more values from the
+ * This set will contain one or more mValues from the
* {@link #setEntryValues(CharSequence[])} array.
*
* @attr name android:entries
@@ -51,16 +52,16 @@ public class MultiSelectListPreference extends AbstractMultiSelectListPreference
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes(attrs,
- android.support.v7.preference.R.styleable.MultiSelectListPreference, defStyleAttr,
+ R.styleable.MultiSelectListPreference, defStyleAttr,
defStyleRes);
mEntries = TypedArrayUtils.getTextArray(a,
- android.support.v7.preference.R.styleable.MultiSelectListPreference_entries,
- android.support.v7.preference.R.styleable.MultiSelectListPreference_android_entries);
+ R.styleable.MultiSelectListPreference_entries,
+ R.styleable.MultiSelectListPreference_android_entries);
mEntryValues = TypedArrayUtils.getTextArray(a,
- android.support.v7.preference.R.styleable.MultiSelectListPreference_entryValues,
- android.support.v7.preference.R.styleable.MultiSelectListPreference_android_entryValues);
+ R.styleable.MultiSelectListPreference_entryValues,
+ R.styleable.MultiSelectListPreference_android_entryValues);
a.recycle();
}
@@ -71,7 +72,7 @@ public class MultiSelectListPreference extends AbstractMultiSelectListPreference
public MultiSelectListPreference(Context context, AttributeSet attrs) {
this(context, attrs, TypedArrayUtils.getAttr(context,
- android.support.v7.preference.R.attr.dialogPreferenceStyle,
+ R.attr.dialogPreferenceStyle,
android.R.attr.dialogPreferenceStyle));
}
@@ -116,7 +117,7 @@ public class MultiSelectListPreference extends AbstractMultiSelectListPreference
* entries is selected. If a user clicks on the second item in entries, the
* second item in this array will be saved to the preference.
*
- * @param entryValues The array to be used as values to save for the preference.
+ * @param entryValues The array to be used as mValues to save for the preference.
*/
public void setEntryValues(CharSequence[] entryValues) {
mEntryValues = entryValues;
@@ -124,16 +125,16 @@ public class MultiSelectListPreference extends AbstractMultiSelectListPreference
/**
* @see #setEntryValues(CharSequence[])
- * @param entryValuesResId The entry values array as a resource.
+ * @param entryValuesResId The entry mValues array as a resource.
*/
public void setEntryValues(@ArrayRes int entryValuesResId) {
setEntryValues(getContext().getResources().getTextArray(entryValuesResId));
}
/**
- * Returns the array of values to be saved for the preference.
+ * Returns the array of mValues to be saved for the preference.
*
- * @return The array of values.
+ * @return The array of mValues.
*/
@Override
public CharSequence[] getEntryValues() {
@@ -144,7 +145,7 @@ public class MultiSelectListPreference extends AbstractMultiSelectListPreference
* Sets the value of the key. This should contain entries in
* {@link #getEntryValues()}.
*
- * @param values The values to set for the key.
+ * @param values The mValues to set for the key.
*/
@Override
public void setValues(Set<String> values) {
@@ -163,7 +164,7 @@ public class MultiSelectListPreference extends AbstractMultiSelectListPreference
}
/**
- * Returns the index of the given value (in the entry values array).
+ * Returns the index of the given value (in the entry mValues array).
*
* @param value The value whose index should be returned.
* @return The index of the value, or -1 if not found.
@@ -219,7 +220,7 @@ public class MultiSelectListPreference extends AbstractMultiSelectListPreference
}
final SavedState myState = new SavedState(superState);
- myState.values = getValues();
+ myState.mValues = getValues();
return myState;
}
@@ -233,31 +234,31 @@ public class MultiSelectListPreference extends AbstractMultiSelectListPreference
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
- setValues(myState.values);
+ setValues(myState.mValues);
}
private static class SavedState extends BaseSavedState {
- Set<String> values;
+ Set<String> mValues;
- public SavedState(Parcel source) {
+ SavedState(Parcel source) {
super(source);
final int size = source.readInt();
- values = new HashSet<>();
+ mValues = new HashSet<>();
String[] strings = new String[size];
source.readStringArray(strings);
- Collections.addAll(values, strings);
+ Collections.addAll(mValues, strings);
}
- public SavedState(Parcelable superState) {
+ SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
- dest.writeInt(values.size());
- dest.writeStringArray(values.toArray(new String[values.size()]));
+ dest.writeInt(mValues.size());
+ dest.writeStringArray(mValues.toArray(new String[mValues.size()]));
}
public static final Parcelable.Creator<SavedState> CREATOR =
diff --git a/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java b/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java
index 81925833..db81644a 100644
--- a/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java
+++ b/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package android.support.v14.preference;
@@ -60,8 +60,8 @@ public class MultiSelectListPreferenceDialogFragment extends PreferenceDialogFra
if (preference.getEntries() == null || preference.getEntryValues() == null) {
throw new IllegalStateException(
- "MultiSelectListPreference requires an entries array and " +
- "an entryValues array.");
+ "MultiSelectListPreference requires an entries array and "
+ + "an entryValues array.");
}
mNewValues.clear();
diff --git a/android/support/v14/preference/PreferenceDialogFragment.java b/android/support/v14/preference/PreferenceDialogFragment.java
index e7b9f403..a4ae4a96 100644
--- a/android/support/v14/preference/PreferenceDialogFragment.java
+++ b/android/support/v14/preference/PreferenceDialogFragment.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package android.support.v14.preference;
@@ -78,8 +78,8 @@ public abstract class PreferenceDialogFragment extends DialogFragment implements
final Fragment rawFragment = getTargetFragment();
if (!(rawFragment instanceof DialogPreference.TargetFragment)) {
- throw new IllegalStateException("Target fragment must implement TargetFragment" +
- " interface");
+ throw new IllegalStateException("Target fragment must implement TargetFragment"
+ + " interface");
}
final DialogPreference.TargetFragment fragment =
@@ -132,9 +132,9 @@ public abstract class PreferenceDialogFragment extends DialogFragment implements
}
}
+ @NonNull
@Override
- public @NonNull
- Dialog onCreateDialog(Bundle savedInstanceState) {
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
diff --git a/android/support/v14/preference/PreferenceFragment.java b/android/support/v14/preference/PreferenceFragment.java
index 24210505..406465f1 100644
--- a/android/support/v14/preference/PreferenceFragment.java
+++ b/android/support/v14/preference/PreferenceFragment.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package android.support.v14.preference;
@@ -44,6 +44,7 @@ import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceRecyclerViewAccessibilityDelegate;
import android.support.v7.preference.PreferenceScreen;
import android.support.v7.preference.PreferenceViewHolder;
+import android.support.v7.preference.R;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.TypedValue;
@@ -145,7 +146,7 @@ public abstract class PreferenceFragment extends Fragment implements
private final DividerDecoration mDividerDecoration = new DividerDecoration();
private static final int MSG_BIND_PREFERENCES = 1;
- private Handler mHandler = new Handler() {
+ private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -157,7 +158,7 @@ public abstract class PreferenceFragment extends Fragment implements
}
};
- final private Runnable mRequestFocus = new Runnable() {
+ private final Runnable mRequestFocus = new Runnable() {
@Override
public void run() {
mList.focusableViewAvailable(mList);
@@ -484,7 +485,7 @@ public abstract class PreferenceFragment extends Fragment implements
handled = ((OnPreferenceStartFragmentCallback) getCallbackFragment())
.onPreferenceStartFragment(this, preference);
}
- if (!handled && getActivity() instanceof OnPreferenceStartFragmentCallback){
+ if (!handled && getActivity() instanceof OnPreferenceStartFragmentCallback) {
handled = ((OnPreferenceStartFragmentCallback) getActivity())
.onPreferenceStartFragment(this, preference);
}
@@ -656,8 +657,8 @@ public abstract class PreferenceFragment extends Fragment implements
} else if (preference instanceof MultiSelectListPreference) {
f = MultiSelectListPreferenceDialogFragment.newInstance(preference.getKey());
} else {
- throw new IllegalArgumentException("Tried to display dialog for unknown " +
- "preference type. Did you forget to override onDisplayPreferenceDialog()?");
+ throw new IllegalArgumentException("Tried to display dialog for unknown "
+ + "preference type. Did you forget to override onDisplayPreferenceDialog()?");
}
f.setTargetFragment(this, 0);
f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
@@ -686,8 +687,7 @@ public abstract class PreferenceFragment extends Fragment implements
@Override
public void run() {
final RecyclerView.Adapter adapter = mList.getAdapter();
- if (!(adapter instanceof
- PreferenceGroup.PreferencePositionCallback)) {
+ if (!(adapter instanceof PreferenceGroup.PreferencePositionCallback)) {
if (adapter != null) {
throw new IllegalStateException("Adapter must implement "
+ "PreferencePositionCallback");
@@ -726,7 +726,7 @@ public abstract class PreferenceFragment extends Fragment implements
private final Preference mPreference;
private final String mKey;
- public ScrollToPreferenceObserver(RecyclerView.Adapter adapter, RecyclerView list,
+ ScrollToPreferenceObserver(RecyclerView.Adapter adapter, RecyclerView list,
Preference preference, String key) {
mAdapter = adapter;
mList = list;
diff --git a/android/support/v14/preference/SwitchPreference.java b/android/support/v14/preference/SwitchPreference.java
index eae20b8d..197de4e1 100644
--- a/android/support/v14/preference/SwitchPreference.java
+++ b/android/support/v14/preference/SwitchPreference.java
@@ -1,18 +1,18 @@
/*
-* 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
-*/
+ * 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.v14.preference;
@@ -24,6 +24,7 @@ import android.support.annotation.RestrictTo;
import android.support.v4.content.res.TypedArrayUtils;
import android.support.v7.preference.AndroidResources;
import android.support.v7.preference.PreferenceViewHolder;
+import android.support.v7.preference.R;
import android.support.v7.preference.TwoStatePreference;
import android.util.AttributeSet;
import android.view.View;
diff --git a/android/support/v17/leanback/app/PlaybackFragment.java b/android/support/v17/leanback/app/PlaybackFragment.java
index e2e6be48..dc59e0eb 100644
--- a/android/support/v17/leanback/app/PlaybackFragment.java
+++ b/android/support/v17/leanback/app/PlaybackFragment.java
@@ -29,6 +29,7 @@ import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
import android.support.v17.leanback.R;
import android.support.v17.leanback.animation.LogAccelerateInterpolator;
import android.support.v17.leanback.animation.LogDecelerateInterpolator;
@@ -106,6 +107,7 @@ public class PlaybackFragment extends Fragment {
* Resets the focus on the button in the middle of control row.
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
public void resetFocus() {
ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) getVerticalGridView()
.findViewHolderForAdapterPosition(0);
@@ -185,6 +187,7 @@ public class PlaybackFragment extends Fragment {
* @hide
* @deprecated use {@link PlaybackSupportFragment}
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
@Deprecated
public static class OnFadeCompleteListener {
public void onFadeInComplete() {
@@ -366,6 +369,7 @@ public class PlaybackFragment extends Fragment {
* Sets the listener to be called when fade in or out has completed.
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
public void setFadeCompleteListener(OnFadeCompleteListener listener) {
mFadeCompleteListener = listener;
}
@@ -374,6 +378,7 @@ public class PlaybackFragment extends Fragment {
* Returns the listener to be called when fade in or out has completed.
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
public OnFadeCompleteListener getFadeCompleteListener() {
return mFadeCompleteListener;
}
diff --git a/android/support/v17/leanback/app/PlaybackSupportFragment.java b/android/support/v17/leanback/app/PlaybackSupportFragment.java
index a8741aba..ee17e84d 100644
--- a/android/support/v17/leanback/app/PlaybackSupportFragment.java
+++ b/android/support/v17/leanback/app/PlaybackSupportFragment.java
@@ -26,6 +26,7 @@ import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
import android.support.v17.leanback.R;
import android.support.v17.leanback.animation.LogAccelerateInterpolator;
import android.support.v17.leanback.animation.LogDecelerateInterpolator;
@@ -101,6 +102,7 @@ public class PlaybackSupportFragment extends Fragment {
* Resets the focus on the button in the middle of control row.
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
public void resetFocus() {
ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) getVerticalGridView()
.findViewHolderForAdapterPosition(0);
@@ -179,6 +181,7 @@ public class PlaybackSupportFragment extends Fragment {
* completion events.
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
public static class OnFadeCompleteListener {
public void onFadeInComplete() {
}
@@ -359,6 +362,7 @@ public class PlaybackSupportFragment extends Fragment {
* Sets the listener to be called when fade in or out has completed.
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
public void setFadeCompleteListener(OnFadeCompleteListener listener) {
mFadeCompleteListener = listener;
}
@@ -367,6 +371,7 @@ public class PlaybackSupportFragment extends Fragment {
* Returns the listener to be called when fade in or out has completed.
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
public OnFadeCompleteListener getFadeCompleteListener() {
return mFadeCompleteListener;
}
diff --git a/android/support/v17/leanback/media/PlaybackControlGlue.java b/android/support/v17/leanback/media/PlaybackControlGlue.java
index 5bf6cc17..0a788f6e 100644
--- a/android/support/v17/leanback/media/PlaybackControlGlue.java
+++ b/android/support/v17/leanback/media/PlaybackControlGlue.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
+import android.support.annotation.RestrictTo;
import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
import android.support.v17.leanback.widget.Action;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
@@ -356,6 +357,7 @@ public abstract class PlaybackControlGlue extends PlaybackGlue
/**
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
protected SparseArrayObjectAdapter createPrimaryActionsAdapter(
PresenterSelector presenterSelector) {
SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter(presenterSelector);
diff --git a/android/support/v17/leanback/media/PlaybackTransportControlGlue.java b/android/support/v17/leanback/media/PlaybackTransportControlGlue.java
index 4aa9bf66..b81f979b 100644
--- a/android/support/v17/leanback/media/PlaybackTransportControlGlue.java
+++ b/android/support/v17/leanback/media/PlaybackTransportControlGlue.java
@@ -365,7 +365,7 @@ public class PlaybackTransportControlGlue<T extends PlayerAdapter>
@Override
public void onSeekFinished(boolean cancelled) {
if (!cancelled) {
- if (mLastUserPosition > 0) {
+ if (mLastUserPosition >= 0) {
seekTo(mLastUserPosition);
}
} else {
diff --git a/android/support/v17/leanback/transition/TransitionEpicenterCallback.java b/android/support/v17/leanback/transition/TransitionEpicenterCallback.java
index ec7f84cf..bb8e686a 100644
--- a/android/support/v17/leanback/transition/TransitionEpicenterCallback.java
+++ b/android/support/v17/leanback/transition/TransitionEpicenterCallback.java
@@ -14,11 +14,13 @@
package android.support.v17.leanback.transition;
import android.graphics.Rect;
+import android.support.annotation.RestrictTo;
/**
* Class to get the epicenter of Transition.
* @hide
*/
+@RestrictTo(RestrictTo.Scope.LIBRARY)
public abstract class TransitionEpicenterCallback {
/**
@@ -31,4 +33,3 @@ public abstract class TransitionEpicenterCallback {
*/
public abstract Rect onGetEpicenter(Object transition);
}
-
diff --git a/android/support/v17/leanback/util/MathUtil.java b/android/support/v17/leanback/util/MathUtil.java
index 487188de..bf74e405 100644
--- a/android/support/v17/leanback/util/MathUtil.java
+++ b/android/support/v17/leanback/util/MathUtil.java
@@ -13,10 +13,13 @@
*/
package android.support.v17.leanback.util;
+import android.support.annotation.RestrictTo;
+
/**
* Math Utilities for leanback library.
* @hide
*/
+@RestrictTo(RestrictTo.Scope.LIBRARY)
public final class MathUtil {
private MathUtil() {
diff --git a/android/support/v17/leanback/widget/DetailsParallaxDrawable.java b/android/support/v17/leanback/widget/DetailsParallaxDrawable.java
index 37e3480c..1eea7974 100644
--- a/android/support/v17/leanback/widget/DetailsParallaxDrawable.java
+++ b/android/support/v17/leanback/widget/DetailsParallaxDrawable.java
@@ -22,6 +22,7 @@ import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
+import android.support.annotation.RestrictTo;
import android.support.v17.leanback.R;
import android.support.v17.leanback.graphics.CompositeDrawable;
import android.support.v17.leanback.graphics.FitWidthBitmapDrawable;
@@ -56,6 +57,7 @@ import android.util.TypedValue;
* </li>
* @hide
*/
+@RestrictTo(RestrictTo.Scope.LIBRARY)
public class DetailsParallaxDrawable extends CompositeDrawable {
private Drawable mBottomDrawable;
diff --git a/android/support/v17/leanback/widget/GridLayoutManager.java b/android/support/v17/leanback/widget/GridLayoutManager.java
index 613198fd..810cb3bf 100644
--- a/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -2645,8 +2645,9 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
mPrimaryScrollExtra = primaryScrollExtra;
View view = findViewByPosition(position);
// scrollToView() is based on Adapter position. Only call scrollToView() when item
- // is still valid.
- if (view != null && getAdapterPositionByView(view) == position) {
+ // is still valid and no layout is requested, otherwise defer to next layout pass.
+ if (!mBaseGridView.isLayoutRequested()
+ && view != null && getAdapterPositionByView(view) == position) {
mFlag |= PF_IN_SELECTION;
scrollToView(view, smooth);
mFlag &= ~PF_IN_SELECTION;
diff --git a/android/support/v17/leanback/widget/MediaRowFocusView.java b/android/support/v17/leanback/widget/MediaRowFocusView.java
index 1418a2a4..471f64e1 100644
--- a/android/support/v17/leanback/widget/MediaRowFocusView.java
+++ b/android/support/v17/leanback/widget/MediaRowFocusView.java
@@ -17,14 +17,16 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
+import android.support.annotation.RestrictTo;
+import android.support.v17.leanback.R;
import android.util.AttributeSet;
import android.view.View;
-import android.support.v17.leanback.R;
/**
* Creates a view for a media item row in a playlist
* @hide
*/
+@RestrictTo(RestrictTo.Scope.LIBRARY)
class MediaRowFocusView extends View {
private final Paint mPaint;
diff --git a/android/support/v17/leanback/widget/ParallaxEffect.java b/android/support/v17/leanback/widget/ParallaxEffect.java
index 5c06e29b..e1af7626 100644
--- a/android/support/v17/leanback/widget/ParallaxEffect.java
+++ b/android/support/v17/leanback/widget/ParallaxEffect.java
@@ -17,6 +17,7 @@
package android.support.v17.leanback.widget;
import android.animation.PropertyValuesHolder;
+import android.support.annotation.RestrictTo;
import android.support.v17.leanback.widget.Parallax.FloatProperty;
import android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue;
import android.support.v17.leanback.widget.Parallax.IntProperty;
@@ -70,6 +71,7 @@ public abstract class ParallaxEffect {
* @return A list of Float objects that represents weight associated with each variable range.
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
public final List<Float> getWeights() {
return mWeights;
}
@@ -96,6 +98,7 @@ public abstract class ParallaxEffect {
* range.
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
public final void setWeights(float... weights) {
for (float weight : weights) {
if (weight <= 0) {
@@ -121,6 +124,7 @@ public abstract class ParallaxEffect {
* @return This ParallaxEffect object, allowing calls to methods in this class to be chained.
* @hide
*/
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
public final ParallaxEffect weights(float... weights) {
setWeights(weights);
return this;
diff --git a/android/support/v17/leanback/widget/VideoSurfaceView.java b/android/support/v17/leanback/widget/VideoSurfaceView.java
index 29d778c0..d42a60d6 100644
--- a/android/support/v17/leanback/widget/VideoSurfaceView.java
+++ b/android/support/v17/leanback/widget/VideoSurfaceView.java
@@ -17,6 +17,7 @@
package android.support.v17.leanback.widget;
import android.content.Context;
+import android.support.annotation.RestrictTo;
import android.util.AttributeSet;
import android.view.SurfaceView;
@@ -26,6 +27,7 @@ import android.view.SurfaceView;
* This class disables setTransitionVisibility() to avoid the problem.
* @hide
*/
+@RestrictTo(RestrictTo.Scope.LIBRARY)
public class VideoSurfaceView extends SurfaceView {
public VideoSurfaceView(Context context) {
diff --git a/android/support/v4/app/ActivityCompat.java b/android/support/v4/app/ActivityCompat.java
index 9d15be1d..333871a0 100644
--- a/android/support/v4/app/ActivityCompat.java
+++ b/android/support/v4/app/ActivityCompat.java
@@ -29,6 +29,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcelable;
+import android.support.annotation.IdRes;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -340,6 +341,31 @@ public class ActivityCompat extends ContextCompat {
}
/**
+ * Finds a view that was identified by the {@code android:id} XML attribute that was processed
+ * in {@link Activity#onCreate}, or throws an IllegalArgumentException if the ID is invalid, or
+ * there is no matching view in the hierarchy.
+ * <p>
+ * <strong>Note:</strong> In most cases -- depending on compiler support --
+ * the resulting view is automatically cast to the target class type. If
+ * the target class type is unconstrained, an explicit cast may be
+ * necessary.
+ *
+ * @param id the ID to search for
+ * @return a view with given ID
+ * @see Activity#findViewById(int)
+ * @see android.support.v4.view.ViewCompat#requireViewById(View, int)
+ */
+ @NonNull
+ public static <T extends View> T requireViewById(@NonNull Activity activity, @IdRes int id) {
+ // TODO: use and link to Activity#requireViewById() directly, once available
+ T view = activity.findViewById(id);
+ if (view == null) {
+ throw new IllegalArgumentException("ID does not reference a View inside this Activity");
+ }
+ return view;
+ }
+
+ /**
* When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
* android.view.View, String)} was used to start an Activity, <var>callback</var>
* will be called to handle shared elements on the <i>launched</i> Activity. This requires
@@ -538,6 +564,7 @@ public class ActivityCompat extends ContextCompat {
* URIs. {@code null} if no content URIs are associated with the event or if permissions could
* not be granted.
*/
+ @Nullable
public static DragAndDropPermissionsCompat requestDragAndDropPermissions(Activity activity,
DragEvent dragEvent) {
return DragAndDropPermissionsCompat.request(activity, dragEvent);
diff --git a/android/support/v4/app/Fragment.java b/android/support/v4/app/Fragment.java
index 5b560cd2..f3c73ae5 100644
--- a/android/support/v4/app/Fragment.java
+++ b/android/support/v4/app/Fragment.java
@@ -575,7 +575,6 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
/**
* Return the {@link Context} this fragment is currently associated with.
*/
- @Nullable
public Context getContext() {
return mHost == null ? null : mHost.getContext();
}
@@ -585,7 +584,6 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
* May return {@code null} if the fragment is associated with a {@link Context}
* instead.
*/
- @Nullable
final public FragmentActivity getActivity() {
return mHost == null ? null : (FragmentActivity) mHost.getActivity();
}
@@ -594,7 +592,6 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
* Return the host object of this fragment. May return {@code null} if the fragment
* isn't currently being hosted.
*/
- @Nullable
final public Object getHost() {
return mHost == null ? null : mHost.onGetHost();
}
@@ -655,7 +652,6 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
* <p>If this Fragment is a child of another Fragment, the FragmentManager
* returned here will be the parent's {@link #getChildFragmentManager()}.
*/
- @Nullable
final public FragmentManager getFragmentManager() {
return mFragmentManager;
}
@@ -864,6 +860,12 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
}
mUserVisibleHint = isVisibleToUser;
mDeferStart = mState < STARTED && !isVisibleToUser;
+ if (mSavedFragmentState != null) {
+ // Ensure that if the user visible hint is set before the Fragment has
+ // restored its state that we don't lose the new value
+ mSavedFragmentState.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG,
+ mUserVisibleHint);
+ }
}
/**
diff --git a/android/support/v4/app/FragmentActivity.java b/android/support/v4/app/FragmentActivity.java
index 78161a87..e3f56849 100644
--- a/android/support/v4/app/FragmentActivity.java
+++ b/android/support/v4/app/FragmentActivity.java
@@ -273,6 +273,7 @@ public class FragmentActivity extends BaseFragmentActivityApi16 implements
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ mFragments.noteStateNotSaved();
mFragments.dispatchConfigurationChanged(newConfig);
}
diff --git a/android/support/v4/app/LoaderManager.java b/android/support/v4/app/LoaderManager.java
index 521b2184..32e211a5 100644
--- a/android/support/v4/app/LoaderManager.java
+++ b/android/support/v4/app/LoaderManager.java
@@ -16,8 +16,11 @@
package android.support.v4.app;
-import android.app.Activity;
import android.os.Bundle;
+import android.os.Looper;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.v4.content.Loader;
import android.support.v4.util.DebugUtils;
import android.support.v4.util.SparseArrayCompat;
@@ -44,11 +47,15 @@ public abstract class LoaderManager {
/**
* Instantiate and return a new Loader for the given ID.
*
+ * <p>This will always be called from the process's main thread.
+ *
* @param id The ID whose loader is to be created.
* @param args Any arguments supplied by the caller.
* @return Return a new Loader instance that is ready to start loading.
*/
- public Loader<D> onCreateLoader(int id, Bundle args);
+ @MainThread
+ @NonNull
+ Loader<D> onCreateLoader(int id, @Nullable Bundle args);
/**
* Called when a previously created loader has finished its load. Note
@@ -86,19 +93,25 @@ public abstract class LoaderManager {
* method so that the old Cursor is not closed.
* </ul>
*
+ * <p>This will always be called from the process's main thread.
+ *
* @param loader The Loader that has finished.
* @param data The data generated by the Loader.
*/
- public void onLoadFinished(Loader<D> loader, D data);
+ @MainThread
+ void onLoadFinished(@NonNull Loader<D> loader, D data);
/**
* Called when a previously created loader is being reset, and thus
* making its data unavailable. The application should at this point
* remove any references it has to the Loader's data.
*
+ * <p>This will always be called from the process's main thread.
+ *
* @param loader The Loader that is being reset.
*/
- public void onLoaderReset(Loader<D> loader);
+ @MainThread
+ void onLoaderReset(@NonNull Loader<D> loader);
}
/**
@@ -115,6 +128,8 @@ public abstract class LoaderManager {
* be called immediately (inside of this function), so you must be prepared
* for this to happen.
*
+ * <p>Must be called from the process's main thread.
+ *
* @param id A unique identifier for this loader. Can be whatever you want.
* Identifiers are scoped to a particular LoaderManager instance.
* @param args Optional arguments to supply to the loader at construction.
@@ -123,8 +138,10 @@ public abstract class LoaderManager {
* @param callback Interface the LoaderManager will call to report about
* changes in the state of the loader. Required.
*/
- public abstract <D> Loader<D> initLoader(int id, Bundle args,
- LoaderManager.LoaderCallbacks<D> callback);
+ @MainThread
+ @NonNull
+ public abstract <D> Loader<D> initLoader(int id, @Nullable Bundle args,
+ @NonNull LoaderManager.LoaderCallbacks<D> callback);
/**
* Starts a new or restarts an existing {@link android.content.Loader} in
@@ -135,27 +152,35 @@ public abstract class LoaderManager {
* its work. The callback will be delivered before the old loader
* is destroyed.
*
+ * <p>Must be called from the process's main thread.
+ *
* @param id A unique identifier for this loader. Can be whatever you want.
* Identifiers are scoped to a particular LoaderManager instance.
* @param args Optional arguments to supply to the loader at construction.
* @param callback Interface the LoaderManager will call to report about
* changes in the state of the loader. Required.
*/
- public abstract <D> Loader<D> restartLoader(int id, Bundle args,
- LoaderManager.LoaderCallbacks<D> callback);
+ @MainThread
+ @NonNull
+ public abstract <D> Loader<D> restartLoader(int id, @Nullable Bundle args,
+ @NonNull LoaderManager.LoaderCallbacks<D> callback);
/**
* Stops and removes the loader with the given ID. If this loader
* had previously reported data to the client through
* {@link LoaderCallbacks#onLoadFinished(Loader, Object)}, a call
* will be made to {@link LoaderCallbacks#onLoaderReset(Loader)}.
+ *
+ * <p>Must be called from the process's main thread.
*/
+ @MainThread
public abstract void destroyLoader(int id);
/**
* Return the Loader with the given id or null if no matching Loader
* is found.
*/
+ @Nullable
public abstract <D> Loader<D> getLoader(int id);
/**
@@ -378,7 +403,7 @@ class LoaderManagerImpl extends LoaderManager {
}
@Override
- public void onLoadCanceled(Loader<Object> loader) {
+ public void onLoadCanceled(@NonNull Loader<Object> loader) {
if (DEBUG) Log.v(TAG, "onLoadCanceled: " + this);
if (mDestroyed) {
@@ -407,7 +432,7 @@ class LoaderManagerImpl extends LoaderManager {
}
@Override
- public void onLoadComplete(Loader<Object> loader, Object data) {
+ public void onLoadComplete(@NonNull Loader<Object> loader, Object data) {
if (DEBUG) Log.v(TAG, "onLoadComplete: " + this);
if (mDestroyed) {
@@ -563,36 +588,18 @@ class LoaderManagerImpl extends LoaderManager {
}
}
- /**
- * Call to initialize a particular ID with a Loader. If this ID already
- * has a Loader associated with it, it is left unchanged and any previous
- * callbacks replaced with the newly provided ones. If there is not currently
- * a Loader for the ID, a new one is created and started.
- *
- * <p>This function should generally be used when a component is initializing,
- * to ensure that a Loader it relies on is created. This allows it to re-use
- * an existing Loader's data if there already is one, so that for example
- * when an {@link Activity} is re-created after a configuration change it
- * does not need to re-create its loaders.
- *
- * <p>Note that in the case where an existing Loader is re-used, the
- * <var>args</var> given here <em>will be ignored</em> because you will
- * continue using the previous Loader.
- *
- * @param id A unique (to this LoaderManager instance) identifier under
- * which to manage the new Loader.
- * @param args Optional arguments that will be propagated to
- * {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader(int, Bundle) LoaderCallbacks.onCreateLoader()}.
- * @param callback Interface implementing management of this Loader. Required.
- * Its onCreateLoader() method will be called while inside of the function to
- * instantiate the Loader object.
- */
+ @MainThread
+ @NonNull
@Override
@SuppressWarnings("unchecked")
- public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
+ public <D> Loader<D> initLoader(int id, @Nullable Bundle args,
+ @NonNull LoaderManager.LoaderCallbacks<D> callback) {
if (mCreatingLoader) {
throw new IllegalStateException("Called while creating a loader");
}
+ if (Looper.getMainLooper() != Looper.myLooper()) {
+ throw new IllegalStateException("initLoader must be called on the main thread");
+ }
LoaderInfo info = mLoaders.get(id);
@@ -615,35 +622,18 @@ class LoaderManagerImpl extends LoaderManager {
return (Loader<D>)info.mLoader;
}
- /**
- * Call to re-create the Loader associated with a particular ID. If there
- * is currently a Loader associated with this ID, it will be
- * canceled/stopped/destroyed as appropriate. A new Loader with the given
- * arguments will be created and its data delivered to you once available.
- *
- * <p>This function does some throttling of Loaders. If too many Loaders
- * have been created for the given ID but not yet generated their data,
- * new calls to this function will create and return a new Loader but not
- * actually start it until some previous loaders have completed.
- *
- * <p>After calling this function, any previous Loaders associated with
- * this ID will be considered invalid, and you will receive no further
- * data updates from them.
- *
- * @param id A unique (to this LoaderManager instance) identifier under
- * which to manage the new Loader.
- * @param args Optional arguments that will be propagated to
- * {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader(int, Bundle) LoaderCallbacks.onCreateLoader()}.
- * @param callback Interface implementing management of this Loader. Required.
- * Its onCreateLoader() method will be called while inside of the function to
- * instantiate the Loader object.
- */
+ @MainThread
+ @NonNull
@Override
@SuppressWarnings("unchecked")
- public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
+ public <D> Loader<D> restartLoader(int id, @Nullable Bundle args,
+ @NonNull LoaderManager.LoaderCallbacks<D> callback) {
if (mCreatingLoader) {
throw new IllegalStateException("Called while creating a loader");
}
+ if (Looper.getMainLooper() != Looper.myLooper()) {
+ throw new IllegalStateException("restartLoader must be called on the main thread");
+ }
LoaderInfo info = mLoaders.get(id);
if (DEBUG) Log.v(TAG, "restartLoader in " + this + ": args=" + args);
@@ -701,18 +691,15 @@ class LoaderManagerImpl extends LoaderManager {
return (Loader<D>)info.mLoader;
}
- /**
- * Rip down, tear apart, shred to pieces a current Loader ID. After returning
- * from this function, any Loader objects associated with this ID are
- * destroyed. Any data associated with them is destroyed. You better not
- * be using it when you do this.
- * @param id Identifier of the Loader to be destroyed.
- */
+ @MainThread
@Override
public void destroyLoader(int id) {
if (mCreatingLoader) {
throw new IllegalStateException("Called while creating a loader");
}
+ if (Looper.getMainLooper() != Looper.myLooper()) {
+ throw new IllegalStateException("destroyLoader must be called on the main thread");
+ }
if (DEBUG) Log.v(TAG, "destroyLoader in " + this + " of " + id);
int idx = mLoaders.indexOfKey(id);
@@ -732,10 +719,7 @@ class LoaderManagerImpl extends LoaderManager {
}
}
- /**
- * Return the most recent Loader object associated with the
- * given ID.
- */
+ @Nullable
@Override
@SuppressWarnings("unchecked")
public <D> Loader<D> getLoader(int id) {
diff --git a/android/support/v4/app/NotificationCompat.java b/android/support/v4/app/NotificationCompat.java
index 6f74e18c..9d71ad1f 100644
--- a/android/support/v4/app/NotificationCompat.java
+++ b/android/support/v4/app/NotificationCompat.java
@@ -453,6 +453,7 @@ public class NotificationCompat {
public @interface StreamType {}
/** @hide */
+ @RestrictTo(LIBRARY_GROUP)
@Retention(SOURCE)
@IntDef({VISIBILITY_PUBLIC, VISIBILITY_PRIVATE, VISIBILITY_SECRET})
public @interface NotificationVisibility {}
@@ -2114,8 +2115,16 @@ public class NotificationCompat {
/**
* Sets the title to be displayed on this conversation. May be set to {@code null}.
- * @param conversationTitle Title displayed for this conversation.
- * @return this object for method chaining.
+ *
+ * <p>This API's behavior was changed in SDK version {@link Build.VERSION_CODES#P}. If your
+ * application's target version is less than {@link Build.VERSION_CODES#P}, setting a
+ * conversation title to a non-null value will make {@link #isGroupConversation()} return
+ * {@code true} and passing {@code null} will make it return {@code false}. In
+ * {@link Build.VERSION_CODES#P} and beyond, use {@link #setGroupConversation(boolean)}
+ * to set group conversation status.
+ *
+ * @param conversationTitle Title displayed for this conversation
+ * @return this object for method chaining
*/
public MessagingStyle setConversationTitle(@Nullable CharSequence conversationTitle) {
mConversationTitle = conversationTitle;
@@ -2185,9 +2194,27 @@ public class NotificationCompat {
}
/**
- * Returns {@code true} if this notification represents a group conversation.
+ * Returns {@code true} if this notification represents a group conversation, otherwise
+ * {@code false}.
+ *
+ * <p> If the application that generated this {@link MessagingStyle} targets an SDK version
+ * less than {@link Build.VERSION_CODES#P}, this method becomes dependent on whether or
+ * not the conversation title is set; returning {@code true} if the conversation title is
+ * a non-null value, or {@code false} otherwise. From {@link Build.VERSION_CODES#P} forward,
+ * this method returns what's set by {@link #setGroupConversation(boolean)} allowing for
+ * named, non-group conversations.
+ *
+ * @see #setConversationTitle(CharSequence)
*/
public boolean isGroupConversation() {
+ // When target SDK version is < P, a non-null conversation title dictates if this is
+ // as group conversation.
+ if (mBuilder != null
+ && mBuilder.mContext.getApplicationInfo().targetSdkVersion
+ < Build.VERSION_CODES.P) {
+ return mConversationTitle != null;
+ }
+
return mIsGroupConversation;
}
@@ -2769,6 +2796,66 @@ public class NotificationCompat {
* to attach actions.
*/
public static class Action {
+ /**
+ * {@link SemanticAction}: No semantic action defined.
+ */
+ public static final int SEMANTIC_ACTION_NONE = 0;
+
+ /**
+ * {@link SemanticAction}: Reply to a conversation, chat, group, or wherever replies
+ * may be appropriate.
+ */
+ public static final int SEMANTIC_ACTION_REPLY = 1;
+
+ /**
+ * {@link SemanticAction}: Mark content as read.
+ */
+ public static final int SEMANTIC_ACTION_MARK_AS_READ = 2;
+
+ /**
+ * {@link SemanticAction}: Mark content as unread.
+ */
+ public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3;
+
+ /**
+ * {@link SemanticAction}: Delete the content associated with the notification. This
+ * could mean deleting an email, message, etc.
+ */
+ public static final int SEMANTIC_ACTION_DELETE = 4;
+
+ /**
+ * {@link SemanticAction}: Archive the content associated with the notification. This
+ * could mean archiving an email, message, etc.
+ */
+ public static final int SEMANTIC_ACTION_ARCHIVE = 5;
+
+ /**
+ * {@link SemanticAction}: Mute the content associated with the notification. This could
+ * mean silencing a conversation or currently playing media.
+ */
+ public static final int SEMANTIC_ACTION_MUTE = 6;
+
+ /**
+ * {@link SemanticAction}: Unmute the content associated with the notification. This could
+ * mean un-silencing a conversation or currently playing media.
+ */
+ public static final int SEMANTIC_ACTION_UNMUTE = 7;
+
+ /**
+ * {@link SemanticAction}: Mark content with a thumbs up.
+ */
+ public static final int SEMANTIC_ACTION_THUMBS_UP = 8;
+
+ /**
+ * {@link SemanticAction}: Mark content with a thumbs down.
+ */
+ public static final int SEMANTIC_ACTION_THUMBS_DOWN = 9;
+
+ static final String EXTRA_SHOWS_USER_INTERFACE =
+ "android.support.action.showsUserInterface";
+
+ static final String EXTRA_SEMANTIC_ACTION = "android.support.action.semanticAction";
+
final Bundle mExtras;
private final RemoteInput[] mRemoteInputs;
@@ -2785,6 +2872,9 @@ public class NotificationCompat {
private final RemoteInput[] mDataOnlyRemoteInputs;
private boolean mAllowGeneratedReplies;
+ private boolean mShowsUserInterface = true;
+
+ private final @SemanticAction int mSemanticAction;
/**
* Small icon representing the action.
@@ -2801,12 +2891,13 @@ public class NotificationCompat {
public PendingIntent actionIntent;
public Action(int icon, CharSequence title, PendingIntent intent) {
- this(icon, title, intent, new Bundle(), null, null, true);
+ this(icon, title, intent, new Bundle(), null, null, true, SEMANTIC_ACTION_NONE, true);
}
Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
RemoteInput[] remoteInputs, RemoteInput[] dataOnlyRemoteInputs,
- boolean allowGeneratedReplies) {
+ boolean allowGeneratedReplies, @SemanticAction int semanticAction,
+ boolean showsUserInterface) {
this.icon = icon;
this.title = NotificationCompat.Builder.limitCharSequenceLength(title);
this.actionIntent = intent;
@@ -2814,6 +2905,8 @@ public class NotificationCompat {
this.mRemoteInputs = remoteInputs;
this.mDataOnlyRemoteInputs = dataOnlyRemoteInputs;
this.mAllowGeneratedReplies = allowGeneratedReplies;
+ this.mSemanticAction = semanticAction;
+ this.mShowsUserInterface = showsUserInterface;
}
public int getIcon() {
@@ -2853,6 +2946,17 @@ public class NotificationCompat {
}
/**
+ * Returns the {@link SemanticAction} associated with this {@link Action}. A
+ * {@link SemanticAction} denotes what an {@link Action}'s {@link PendingIntent} will do
+ * (eg. reply, mark as read, delete, etc).
+ *
+ * @see SemanticAction
+ */
+ public @SemanticAction int getSemanticAction() {
+ return mSemanticAction;
+ }
+
+ /**
* Get the list of inputs to be collected from the user that ONLY accept data when this
* action is sent. These remote inputs are guaranteed to return true on a call to
* {@link RemoteInput#isDataOnly}.
@@ -2867,6 +2971,14 @@ public class NotificationCompat {
}
/**
+ * Return whether or not triggering this {@link Action}'s {@link PendingIntent} will open a
+ * user interface.
+ */
+ public boolean getShowsUserInterface() {
+ return mShowsUserInterface;
+ }
+
+ /**
* Builder class for {@link Action} objects.
*/
public static final class Builder {
@@ -2876,6 +2988,8 @@ public class NotificationCompat {
private boolean mAllowGeneratedReplies = true;
private final Bundle mExtras;
private ArrayList<RemoteInput> mRemoteInputs;
+ private @SemanticAction int mSemanticAction;
+ private boolean mShowsUserInterface = true;
/**
* Construct a new builder for {@link Action} object.
@@ -2884,7 +2998,7 @@ public class NotificationCompat {
* @param intent the {@link PendingIntent} to fire when users trigger this action
*/
public Builder(int icon, CharSequence title, PendingIntent intent) {
- this(icon, title, intent, new Bundle(), null, true);
+ this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE, true);
}
/**
@@ -2894,11 +3008,13 @@ public class NotificationCompat {
*/
public Builder(Action action) {
this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras),
- action.getRemoteInputs(), action.getAllowGeneratedReplies());
+ action.getRemoteInputs(), action.getAllowGeneratedReplies(),
+ action.getSemanticAction(), action.mShowsUserInterface);
}
private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras,
- RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
+ RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
+ @SemanticAction int semanticAction, boolean showsUserInterface) {
mIcon = icon;
mTitle = NotificationCompat.Builder.limitCharSequenceLength(title);
mIntent = intent;
@@ -2906,6 +3022,8 @@ public class NotificationCompat {
mRemoteInputs = remoteInputs == null ? null : new ArrayList<>(
Arrays.asList(remoteInputs));
mAllowGeneratedReplies = allowGeneratedReplies;
+ mSemanticAction = semanticAction;
+ mShowsUserInterface = showsUserInterface;
}
/**
@@ -2961,6 +3079,32 @@ public class NotificationCompat {
}
/**
+ * Sets the {@link SemanticAction} for this {@link Action}. A {@link SemanticAction}
+ * denotes what an {@link Action}'s {@link PendingIntent} will do (eg. reply, mark
+ * as read, delete, etc).
+ * @param semanticAction a {@link SemanticAction} defined within {@link Action} with
+ * {@code SEMANTIC_ACTION_} prefixes
+ * @return this object for method chaining
+ */
+ public Builder setSemanticAction(@SemanticAction int semanticAction) {
+ mSemanticAction = semanticAction;
+ return this;
+ }
+
+ /**
+ * Set whether or not this {@link Action}'s {@link PendingIntent} will open a user
+ * interface.
+ * @param showsUserInterface {@code true} if this {@link Action}'s {@link PendingIntent}
+ * will open a user interface, otherwise {@code false}
+ * @return this object for method chaining
+ * The default value is {@code true}
+ */
+ public Builder setShowsUserInterface(boolean showsUserInterface) {
+ mShowsUserInterface = showsUserInterface;
+ return this;
+ }
+
+ /**
* Apply an extender to this action builder. Extenders may be used to add
* metadata or change options on this builder.
*/
@@ -2991,7 +3135,8 @@ public class NotificationCompat {
RemoteInput[] textInputsArr = textInputs.isEmpty()
? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
- dataOnlyInputsArr, mAllowGeneratedReplies);
+ dataOnlyInputsArr, mAllowGeneratedReplies, mSemanticAction,
+ mShowsUserInterface);
}
}
@@ -3251,6 +3396,27 @@ public class NotificationCompat {
return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
}
}
+
+ /**
+ * Provides meaning to an {@link Action} that hints at what the associated
+ * {@link PendingIntent} will do. For example, an {@link Action} with a
+ * {@link PendingIntent} that replies to a text message notification may have the
+ * {@link #SEMANTIC_ACTION_REPLY} {@link SemanticAction} set within it.
+ */
+ @IntDef({
+ SEMANTIC_ACTION_NONE,
+ SEMANTIC_ACTION_REPLY,
+ SEMANTIC_ACTION_MARK_AS_READ,
+ SEMANTIC_ACTION_MARK_AS_UNREAD,
+ SEMANTIC_ACTION_DELETE,
+ SEMANTIC_ACTION_ARCHIVE,
+ SEMANTIC_ACTION_MUTE,
+ SEMANTIC_ACTION_UNMUTE,
+ SEMANTIC_ACTION_THUMBS_UP,
+ SEMANTIC_ACTION_THUMBS_DOWN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SemanticAction {}
}
@@ -4651,8 +4817,21 @@ public class NotificationCompat {
allowGeneratedReplies = action.getExtras().getBoolean(
NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES);
}
+
+ final boolean showsUserInterface =
+ action.getExtras().getBoolean(Action.EXTRA_SHOWS_USER_INTERFACE, true);
+
+ final @Action.SemanticAction int semanticAction;
+ if (Build.VERSION.SDK_INT >= 28) {
+ semanticAction = action.getSemanticAction();
+ } else {
+ semanticAction = action.getExtras().getInt(
+ Action.EXTRA_SEMANTIC_ACTION, Action.SEMANTIC_ACTION_NONE);
+ }
+
return new Action(action.icon, action.title, action.actionIntent,
- action.getExtras(), remoteInputs, null, allowGeneratedReplies);
+ action.getExtras(), remoteInputs, null, allowGeneratedReplies,
+ semanticAction, showsUserInterface);
}
/**
diff --git a/android/support/v4/app/NotificationCompatBuilder.java b/android/support/v4/app/NotificationCompatBuilder.java
index db775a55..e5fb4f95 100644
--- a/android/support/v4/app/NotificationCompatBuilder.java
+++ b/android/support/v4/app/NotificationCompatBuilder.java
@@ -248,6 +248,15 @@ class NotificationCompatBuilder implements NotificationBuilderWithBuilderAccesso
if (Build.VERSION.SDK_INT >= 24) {
actionBuilder.setAllowGeneratedReplies(action.getAllowGeneratedReplies());
}
+
+ actionExtras.putInt(NotificationCompat.Action.EXTRA_SEMANTIC_ACTION,
+ action.getSemanticAction());
+ if (Build.VERSION.SDK_INT >= 28) {
+ actionBuilder.setSemanticAction(action.getSemanticAction());
+ }
+
+ actionExtras.putBoolean(NotificationCompat.Action.EXTRA_SHOWS_USER_INTERFACE,
+ action.getShowsUserInterface());
actionBuilder.addExtras(actionExtras);
mBuilder.addAction(actionBuilder.build());
} else if (Build.VERSION.SDK_INT >= 16) {
diff --git a/android/support/v4/app/NotificationCompatJellybean.java b/android/support/v4/app/NotificationCompatJellybean.java
index 9cdd2e95..82f89412 100644
--- a/android/support/v4/app/NotificationCompatJellybean.java
+++ b/android/support/v4/app/NotificationCompatJellybean.java
@@ -129,7 +129,8 @@ class NotificationCompatJellybean {
allowGeneratedReplies = extras.getBoolean(EXTRA_ALLOW_GENERATED_REPLIES);
}
return new NotificationCompat.Action(icon, title, actionIntent, extras, remoteInputs,
- dataOnlyRemoteInputs, allowGeneratedReplies);
+ dataOnlyRemoteInputs, allowGeneratedReplies,
+ NotificationCompat.Action.SEMANTIC_ACTION_NONE, true);
}
public static Bundle writeActionAndGetExtras(
@@ -236,7 +237,9 @@ class NotificationCompatJellybean {
bundle.getBundle(KEY_EXTRAS),
fromBundleArray(getBundleArrayFromBundle(bundle, KEY_REMOTE_INPUTS)),
fromBundleArray(getBundleArrayFromBundle(bundle, KEY_DATA_ONLY_REMOTE_INPUTS)),
- allowGeneratedReplies);
+ allowGeneratedReplies,
+ NotificationCompat.Action.SEMANTIC_ACTION_NONE,
+ true);
}
static Bundle getBundleForAction(NotificationCompat.Action action) {
diff --git a/android/support/v4/app/NotificationManagerCompat.java b/android/support/v4/app/NotificationManagerCompat.java
index 07fcb6c7..7099cb9b 100644
--- a/android/support/v4/app/NotificationManagerCompat.java
+++ b/android/support/v4/app/NotificationManagerCompat.java
@@ -190,7 +190,7 @@ public final class NotificationManagerCompat {
* @param id the ID of the notification
* @param notification the notification to post to the system
*/
- public void notify(int id, Notification notification) {
+ public void notify(int id, @NonNull Notification notification) {
notify(null, id, notification);
}
diff --git a/android/support/v4/content/FileProvider.java b/android/support/v4/content/FileProvider.java
index 8599911a..16164be9 100644
--- a/android/support/v4/content/FileProvider.java
+++ b/android/support/v4/content/FileProvider.java
@@ -29,6 +29,7 @@ import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
+import android.os.Build;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns;
@@ -177,6 +178,17 @@ import java.util.Map;
* subdirectory is the same as the value returned by
* {@link Context#getExternalCacheDir() Context.getExternalCacheDir()}.
* </dd>
+ * <dt>
+ * <pre class="prettyprint">
+ *&lt;external-media-path name="<i>name</i>" path="<i>path</i>" /&gt;
+ *</pre>
+ * </dt>
+ * <dd>
+ * Represents files in the root of your app's external media area. The root path of this
+ * subdirectory is the same as the value returned by the first result of
+ * {@link Context#getExternalMediaDirs() Context.getExternalMediaDirs()}.
+ * <p><strong>Note:</strong> this directory is only available on API 21+ devices.</p>
+ * </dd>
* </dl>
* <p>
* These child elements all use the same attributes:
@@ -336,6 +348,7 @@ public class FileProvider extends ContentProvider {
private static final String TAG_EXTERNAL = "external-path";
private static final String TAG_EXTERNAL_FILES = "external-files-path";
private static final String TAG_EXTERNAL_CACHE = "external-cache-path";
+ private static final String TAG_EXTERNAL_MEDIA = "external-media-path";
private static final String ATTR_NAME = "name";
private static final String ATTR_PATH = "path";
@@ -622,6 +635,12 @@ public class FileProvider extends ContentProvider {
if (externalCacheDirs.length > 0) {
target = externalCacheDirs[0];
}
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
+ && TAG_EXTERNAL_MEDIA.equals(tag)) {
+ File[] externalMediaDirs = context.getExternalMediaDirs();
+ if (externalMediaDirs.length > 0) {
+ target = externalMediaDirs[0];
+ }
}
if (target != null) {
diff --git a/android/support/v4/content/Loader.java b/android/support/v4/content/Loader.java
index 2ac10d73..431964d2 100644
--- a/android/support/v4/content/Loader.java
+++ b/android/support/v4/content/Loader.java
@@ -19,6 +19,7 @@ package android.support.v4.content;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
+import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.util.DebugUtils;
@@ -123,6 +124,7 @@ public class Loader<D> {
*
* @param data the result of the load
*/
+ @MainThread
public void deliverResult(@Nullable D data) {
if (mListener != null) {
mListener.onLoadComplete(this, data);
@@ -135,6 +137,7 @@ public class Loader<D> {
*
* Must be called from the process's main thread.
*/
+ @MainThread
public void deliverCancellation() {
if (mOnLoadCanceledListener != null) {
mOnLoadCanceledListener.onLoadCanceled(this);
@@ -163,6 +166,7 @@ public class Loader<D> {
*
* <p>Must be called from the process's main thread.
*/
+ @MainThread
public void registerListener(int id, @NonNull OnLoadCompleteListener<D> listener) {
if (mListener != null) {
throw new IllegalStateException("There is already a listener registered");
@@ -176,6 +180,7 @@ public class Loader<D> {
*
* Must be called from the process's main thread.
*/
+ @MainThread
public void unregisterListener(@NonNull OnLoadCompleteListener<D> listener) {
if (mListener == null) {
throw new IllegalStateException("No listener register");
@@ -195,6 +200,7 @@ public class Loader<D> {
*
* @param listener The listener to register.
*/
+ @MainThread
public void registerOnLoadCanceledListener(@NonNull OnLoadCanceledListener<D> listener) {
if (mOnLoadCanceledListener != null) {
throw new IllegalStateException("There is already a listener registered");
@@ -210,6 +216,7 @@ public class Loader<D> {
*
* @param listener The listener to unregister.
*/
+ @MainThread
public void unregisterOnLoadCanceledListener(@NonNull OnLoadCanceledListener<D> listener) {
if (mOnLoadCanceledListener == null) {
throw new IllegalStateException("No listener register");
@@ -268,6 +275,7 @@ public class Loader<D> {
*
* <p>Must be called from the process's main thread.
*/
+ @MainThread
public final void startLoading() {
mStarted = true;
mReset = false;
@@ -279,7 +287,9 @@ public class Loader<D> {
* Subclasses must implement this to take care of loading their data,
* as per {@link #startLoading()}. This is not called by clients directly,
* but as a result of a call to {@link #startLoading()}.
+ * This will always be called from the process's main thread.
*/
+ @MainThread
protected void onStartLoading() {
}
@@ -301,6 +311,7 @@ public class Loader<D> {
* is still running and the {@link OnLoadCanceledListener} will be called
* when the task completes.
*/
+ @MainThread
public boolean cancelLoad() {
return onCancelLoad();
}
@@ -316,6 +327,7 @@ public class Loader<D> {
* is still running and the {@link OnLoadCanceledListener} will be called
* when the task completes.
*/
+ @MainThread
protected boolean onCancelLoad() {
return false;
}
@@ -328,6 +340,7 @@ public class Loader<D> {
*
* <p>Must be called from the process's main thread.
*/
+ @MainThread
public void forceLoad() {
onForceLoad();
}
@@ -336,6 +349,7 @@ public class Loader<D> {
* Subclasses must implement this to take care of requests to {@link #forceLoad()}.
* This will always be called from the process's main thread.
*/
+ @MainThread
protected void onForceLoad() {
}
@@ -359,6 +373,7 @@ public class Loader<D> {
*
* <p>Must be called from the process's main thread.
*/
+ @MainThread
public void stopLoading() {
mStarted = false;
onStopLoading();
@@ -370,6 +385,7 @@ public class Loader<D> {
* but as a result of a call to {@link #stopLoading()}.
* This will always be called from the process's main thread.
*/
+ @MainThread
protected void onStopLoading() {
}
@@ -383,12 +399,15 @@ public class Loader<D> {
* Tell the Loader that it is being abandoned. This is called prior
* to {@link #reset} to have it retain its current data but not report
* any new data.
+ *
+ * <p>Must be called from the process's main thread.
*/
+ @MainThread
public void abandon() {
mAbandoned = true;
onAbandon();
}
-
+
/**
* Subclasses implement this to take care of being abandoned. This is
* an optional intermediate state prior to {@link #onReset()} -- it means that
@@ -397,10 +416,12 @@ public class Loader<D> {
* loader <em>must</em> keep its last reported data valid until the final
* {@link #onReset()} happens. You can retrieve the current abandoned
* state with {@link #isAbandoned}.
+ * This will always be called from the process's main thread.
*/
+ @MainThread
protected void onAbandon() {
}
-
+
/**
* This function will normally be called for you automatically by
* {@link android.support.v4.app.LoaderManager} when destroying a Loader. When using
@@ -419,6 +440,7 @@ public class Loader<D> {
*
* <p>Must be called from the process's main thread.
*/
+ @MainThread
public void reset() {
onReset();
mReset = true;
@@ -434,6 +456,7 @@ public class Loader<D> {
* but as a result of a call to {@link #reset()}.
* This will always be called from the process's main thread.
*/
+ @MainThread
protected void onReset() {
}
@@ -481,6 +504,7 @@ public class Loader<D> {
*
* <p>Must be called from the process's main thread.
*/
+ @MainThread
public void onContentChanged() {
if (mStarted) {
forceLoad();
diff --git a/android/support/v4/content/WakefulBroadcastReceiver.java b/android/support/v4/content/WakefulBroadcastReceiver.java
index 8ec3eee6..78555aa4 100644
--- a/android/support/v4/content/WakefulBroadcastReceiver.java
+++ b/android/support/v4/content/WakefulBroadcastReceiver.java
@@ -34,6 +34,9 @@ import android.util.SparseArray;
* for you; you must request the {@link android.Manifest.permission#WAKE_LOCK}
* permission to use it.</p>
*
+ * <p>Wakelocks held by this class are reported to tools as
+ * {@code "androidx.core:wake:<component-name>"}.</p>
+ *
* <h3>Example</h3>
*
* <p>A {@link WakefulBroadcastReceiver} uses the method
@@ -103,7 +106,7 @@ public abstract class WakefulBroadcastReceiver extends BroadcastReceiver {
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "wake:" + comp.flattenToShortString());
+ "androidx.core:wake:" + comp.flattenToShortString());
wl.setReferenceCounted(false);
wl.acquire(60 * 1000);
sActiveWakeLocks.put(id, wl);
diff --git a/android/support/v4/graphics/TypefaceCompatUtil.java b/android/support/v4/graphics/TypefaceCompatUtil.java
index b5d206c8..c524f820 100644
--- a/android/support/v4/graphics/TypefaceCompatUtil.java
+++ b/android/support/v4/graphics/TypefaceCompatUtil.java
@@ -94,11 +94,15 @@ public class TypefaceCompatUtil {
@RequiresApi(19)
public static ByteBuffer mmap(Context context, CancellationSignal cancellationSignal, Uri uri) {
final ContentResolver resolver = context.getContentResolver();
- try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r", cancellationSignal);
- FileInputStream fis = new FileInputStream(pfd.getFileDescriptor())) {
- FileChannel channel = fis.getChannel();
- final long size = channel.size();
- return channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
+ try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r", cancellationSignal)) {
+ if (pfd == null) {
+ return null;
+ }
+ try (FileInputStream fis = new FileInputStream(pfd.getFileDescriptor())) {
+ FileChannel channel = fis.getChannel();
+ final long size = channel.size();
+ return channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
+ }
} catch (IOException e) {
return null;
}
diff --git a/android/support/v4/graphics/drawable/DrawableCompat.java b/android/support/v4/graphics/drawable/DrawableCompat.java
index 4e988eaf..f15354ef 100644
--- a/android/support/v4/graphics/drawable/DrawableCompat.java
+++ b/android/support/v4/graphics/drawable/DrawableCompat.java
@@ -229,8 +229,8 @@ public final class DrawableCompat {
// children manually
if (drawable instanceof InsetDrawable) {
clearColorFilter(((InsetDrawable) drawable).getDrawable());
- } else if (drawable instanceof DrawableWrapper) {
- clearColorFilter(((DrawableWrapper) drawable).getWrappedDrawable());
+ } else if (drawable instanceof WrappedDrawable) {
+ clearColorFilter(((WrappedDrawable) drawable).getWrappedDrawable());
} else if (drawable instanceof DrawableContainer) {
final DrawableContainer container = (DrawableContainer) drawable;
final DrawableContainer.DrawableContainerState state =
@@ -307,17 +307,17 @@ public final class DrawableCompat {
return drawable;
} else if (Build.VERSION.SDK_INT >= 21) {
if (!(drawable instanceof TintAwareDrawable)) {
- return new DrawableWrapperApi21(drawable);
+ return new WrappedDrawableApi21(drawable);
}
return drawable;
} else if (Build.VERSION.SDK_INT >= 19) {
if (!(drawable instanceof TintAwareDrawable)) {
- return new DrawableWrapperApi19(drawable);
+ return new WrappedDrawableApi19(drawable);
}
return drawable;
} else {
if (!(drawable instanceof TintAwareDrawable)) {
- return new DrawableWrapperApi14(drawable);
+ return new WrappedDrawableApi14(drawable);
}
return drawable;
}
@@ -335,8 +335,8 @@ public final class DrawableCompat {
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
public static <T extends Drawable> T unwrap(@NonNull Drawable drawable) {
- if (drawable instanceof DrawableWrapper) {
- return (T) ((DrawableWrapper) drawable).getWrappedDrawable();
+ if (drawable instanceof WrappedDrawable) {
+ return (T) ((WrappedDrawable) drawable).getWrappedDrawable();
}
return (T) drawable;
}
diff --git a/android/support/v4/graphics/drawable/DrawableWrapper.java b/android/support/v4/graphics/drawable/WrappedDrawable.java
index 1574b363..3bd1d683 100644
--- a/android/support/v4/graphics/drawable/DrawableWrapper.java
+++ b/android/support/v4/graphics/drawable/WrappedDrawable.java
@@ -28,7 +28,7 @@ import android.support.annotation.RestrictTo;
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
-public interface DrawableWrapper {
+public interface WrappedDrawable {
Drawable getWrappedDrawable();
void setWrappedDrawable(Drawable drawable);
}
diff --git a/android/support/v4/graphics/drawable/DrawableWrapperApi14.java b/android/support/v4/graphics/drawable/WrappedDrawableApi14.java
index 5b1bbc70..d1218bc7 100644
--- a/android/support/v4/graphics/drawable/DrawableWrapperApi14.java
+++ b/android/support/v4/graphics/drawable/WrappedDrawableApi14.java
@@ -26,7 +26,6 @@ import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
/**
* Drawable which delegates all calls to its wrapped {@link Drawable}.
@@ -34,10 +33,8 @@ import android.support.annotation.RequiresApi;
* Also allows backward compatible tinting via a color or {@link ColorStateList}.
* This functionality is accessed via static methods in {@code DrawableCompat}.
*/
-
-@RequiresApi(14)
-class DrawableWrapperApi14 extends Drawable
- implements Drawable.Callback, DrawableWrapper, TintAwareDrawable {
+class WrappedDrawableApi14 extends Drawable
+ implements Drawable.Callback, WrappedDrawable, TintAwareDrawable {
static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN;
@@ -50,7 +47,7 @@ class DrawableWrapperApi14 extends Drawable
Drawable mDrawable;
- DrawableWrapperApi14(@NonNull DrawableWrapperState state, @Nullable Resources res) {
+ WrappedDrawableApi14(@NonNull DrawableWrapperState state, @Nullable Resources res) {
mState = state;
updateLocalState(res);
}
@@ -60,7 +57,7 @@ class DrawableWrapperApi14 extends Drawable
*
* @param dr the drawable to wrap
*/
- DrawableWrapperApi14(@Nullable Drawable dr) {
+ WrappedDrawableApi14(@Nullable Drawable dr) {
mState = mutateConstantState();
// Now set the drawable...
setWrappedDrawable(dr);
@@ -73,26 +70,17 @@ class DrawableWrapperApi14 extends Drawable
*/
private void updateLocalState(@Nullable Resources res) {
if (mState != null && mState.mDrawableState != null) {
- final Drawable dr = newDrawableFromState(mState.mDrawableState, res);
- setWrappedDrawable(dr);
+ setWrappedDrawable(mState.mDrawableState.newDrawable(res));
}
}
- /**
- * Allows us to call ConstantState.newDrawable(*) is a API safe way
- */
- protected Drawable newDrawableFromState(@NonNull Drawable.ConstantState state,
- @Nullable Resources res) {
- return state.newDrawable(res);
- }
-
@Override
public void jumpToCurrentState() {
mDrawable.jumpToCurrentState();
}
@Override
- public void draw(Canvas canvas) {
+ public void draw(@NonNull Canvas canvas) {
mDrawable.draw(canvas);
}
@@ -144,17 +132,19 @@ class DrawableWrapperApi14 extends Drawable
}
@Override
- public boolean setState(final int[] stateSet) {
+ public boolean setState(@NonNull int[] stateSet) {
boolean handled = mDrawable.setState(stateSet);
handled = updateTint(stateSet) || handled;
return handled;
}
+ @NonNull
@Override
public int[] getState() {
return mDrawable.getState();
}
+ @NonNull
@Override
public Drawable getCurrent() {
return mDrawable.getCurrent();
@@ -196,7 +186,7 @@ class DrawableWrapperApi14 extends Drawable
}
@Override
- public boolean getPadding(Rect padding) {
+ public boolean getPadding(@NonNull Rect padding) {
return mDrawable.getPadding(padding);
}
@@ -210,6 +200,7 @@ class DrawableWrapperApi14 extends Drawable
return null;
}
+ @NonNull
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
@@ -242,7 +233,7 @@ class DrawableWrapperApi14 extends Drawable
* {@inheritDoc}
*/
@Override
- public void invalidateDrawable(Drawable who) {
+ public void invalidateDrawable(@NonNull Drawable who) {
invalidateSelf();
}
@@ -250,7 +241,7 @@ class DrawableWrapperApi14 extends Drawable
* {@inheritDoc}
*/
@Override
- public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
scheduleSelf(what, when);
}
@@ -258,7 +249,7 @@ class DrawableWrapperApi14 extends Drawable
* {@inheritDoc}
*/
@Override
- public void unscheduleDrawable(Drawable who, Runnable what) {
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
unscheduleSelf(what);
}
@@ -279,7 +270,7 @@ class DrawableWrapperApi14 extends Drawable
}
@Override
- public void setTintMode(PorterDuff.Mode tintMode) {
+ public void setTintMode(@NonNull PorterDuff.Mode tintMode) {
mState.mTintMode = tintMode;
updateTint(getState());
}
@@ -364,11 +355,13 @@ class DrawableWrapperApi14 extends Drawable
}
}
+ @NonNull
@Override
public Drawable newDrawable() {
return newDrawable(null);
}
+ @NonNull
@Override
public abstract Drawable newDrawable(@Nullable Resources res);
@@ -389,9 +382,10 @@ class DrawableWrapperApi14 extends Drawable
super(orig, res);
}
+ @NonNull
@Override
public Drawable newDrawable(@Nullable Resources res) {
- return new DrawableWrapperApi14(this, res);
+ return new WrappedDrawableApi14(this, res);
}
}
}
diff --git a/android/support/v4/graphics/drawable/DrawableWrapperApi19.java b/android/support/v4/graphics/drawable/WrappedDrawableApi19.java
index 7707591d..7d6b8d87 100644
--- a/android/support/v4/graphics/drawable/DrawableWrapperApi19.java
+++ b/android/support/v4/graphics/drawable/WrappedDrawableApi19.java
@@ -23,13 +23,13 @@ import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
@RequiresApi(19)
-class DrawableWrapperApi19 extends DrawableWrapperApi14 {
+class WrappedDrawableApi19 extends WrappedDrawableApi14 {
- DrawableWrapperApi19(Drawable drawable) {
+ WrappedDrawableApi19(Drawable drawable) {
super(drawable);
}
- DrawableWrapperApi19(DrawableWrapperState state, Resources resources) {
+ WrappedDrawableApi19(DrawableWrapperState state, Resources resources) {
super(state, resources);
}
@@ -55,9 +55,10 @@ class DrawableWrapperApi19 extends DrawableWrapperApi14 {
super(orig, res);
}
+ @NonNull
@Override
public Drawable newDrawable(@Nullable Resources res) {
- return new DrawableWrapperApi19(this, res);
+ return new WrappedDrawableApi19(this, res);
}
}
}
diff --git a/android/support/v4/graphics/drawable/DrawableWrapperApi21.java b/android/support/v4/graphics/drawable/WrappedDrawableApi21.java
index 5195cc93..b5507428 100644
--- a/android/support/v4/graphics/drawable/DrawableWrapperApi21.java
+++ b/android/support/v4/graphics/drawable/WrappedDrawableApi21.java
@@ -16,8 +16,6 @@
package android.support.v4.graphics.drawable;
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Outline;
@@ -32,22 +30,21 @@ import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
import android.util.Log;
import java.lang.reflect.Method;
@RequiresApi(21)
-class DrawableWrapperApi21 extends DrawableWrapperApi19 {
- private static final String TAG = "DrawableWrapperApi21";
+class WrappedDrawableApi21 extends WrappedDrawableApi19 {
+ private static final String TAG = "WrappedDrawableApi21";
private static Method sIsProjectedDrawableMethod;
- DrawableWrapperApi21(Drawable drawable) {
+ WrappedDrawableApi21(Drawable drawable) {
super(drawable);
findAndCacheIsProjectedDrawableMethod();
}
- DrawableWrapperApi21(DrawableWrapperState state, Resources resources) {
+ WrappedDrawableApi21(DrawableWrapperState state, Resources resources) {
super(state, resources);
findAndCacheIsProjectedDrawableMethod();
}
@@ -63,10 +60,11 @@ class DrawableWrapperApi21 extends DrawableWrapperApi19 {
}
@Override
- public void getOutline(Outline outline) {
+ public void getOutline(@NonNull Outline outline) {
mDrawable.getOutline(outline);
}
+ @NonNull
@Override
public Rect getDirtyBounds() {
return mDrawable.getDirtyBounds();
@@ -100,7 +98,7 @@ class DrawableWrapperApi21 extends DrawableWrapperApi19 {
}
@Override
- public boolean setState(int[] stateSet) {
+ public boolean setState(@NonNull int[] stateSet) {
if (super.setState(stateSet)) {
// Manually invalidate because the framework doesn't currently force an invalidation
// on a state change
@@ -123,9 +121,9 @@ class DrawableWrapperApi21 extends DrawableWrapperApi19 {
}
/**
- * @hide
+ * This method is overriding hidden framework method in {@link Drawable}. It is used by the
+ * system and thus it should not be removed.
*/
- @RestrictTo(LIBRARY_GROUP)
public boolean isProjected() {
if (mDrawable != null && sIsProjectedDrawableMethod != null) {
try {
@@ -150,9 +148,10 @@ class DrawableWrapperApi21 extends DrawableWrapperApi19 {
super(orig, res);
}
+ @NonNull
@Override
public Drawable newDrawable(@Nullable Resources res) {
- return new DrawableWrapperApi21(this, res);
+ return new WrappedDrawableApi21(this, res);
}
}
diff --git a/android/support/v4/util/ArraySet.java b/android/support/v4/util/ArraySet.java
index ab080fa8..8444d2c0 100644
--- a/android/support/v4/util/ArraySet.java
+++ b/android/support/v4/util/ArraySet.java
@@ -18,6 +18,8 @@ package android.support.v4.util;
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.RestrictTo;
import android.util.Log;
@@ -69,16 +71,15 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
* The first entry in the array is a pointer to the next array in the
* list; the second entry is a pointer to the int[] hash code array for it.
*/
- static Object[] sBaseCache;
- static int sBaseCacheSize;
- static Object[] sTwiceBaseCache;
- static int sTwiceBaseCacheSize;
+ private static Object[] sBaseCache;
+ private static int sBaseCacheSize;
+ private static Object[] sTwiceBaseCache;
+ private static int sTwiceBaseCacheSize;
- final boolean mIdentityHashCode;
- int[] mHashes;
- Object[] mArray;
- int mSize;
- MapCollections<E, E> mCollections;
+ private int[] mHashes;
+ private Object[] mArray;
+ private int mSize;
+ private MapCollections<E, E> mCollections;
private int indexOf(Object key, int hash) {
final int N = mSize;
@@ -238,19 +239,13 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
* will grow once items are added to it.
*/
public ArraySet() {
- this(0, false);
+ this(0);
}
/**
* Create a new ArraySet with a given initial capacity.
*/
public ArraySet(int capacity) {
- this(capacity, false);
- }
-
- /** {@hide} */
- public ArraySet(int capacity, boolean identityHashCode) {
- mIdentityHashCode = identityHashCode;
if (capacity == 0) {
mHashes = INT;
mArray = OBJECT;
@@ -263,15 +258,17 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
/**
* Create a new ArraySet with the mappings from the given ArraySet.
*/
- public ArraySet(ArraySet<E> set) {
+ public ArraySet(@Nullable ArraySet<E> set) {
this();
if (set != null) {
addAll(set);
}
}
- /** {@hide} */
- public ArraySet(Collection<E> set) {
+ /**
+ * Create a new ArraySet with the mappings from the given {@link Collection}.
+ */
+ public ArraySet(@Nullable Collection<E> set) {
this();
if (set != null) {
addAll(set);
@@ -326,8 +323,7 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
* @return Returns the index of the value if it exists, else a negative integer.
*/
public int indexOf(Object key) {
- return key == null ? indexOfNull()
- : indexOf(key, mIdentityHashCode ? System.identityHashCode(key) : key.hashCode());
+ return key == null ? indexOfNull() : indexOf(key, key.hashCode());
}
/**
@@ -335,6 +331,7 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
* @param index The desired index, must be between 0 and {@link #size()}-1.
* @return Returns the value stored at the given index.
*/
+ @Nullable
public E valueAt(int index) {
return (E) mArray[index];
}
@@ -357,14 +354,14 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
* when the class of the object is inappropriate for this set.
*/
@Override
- public boolean add(E value) {
+ public boolean add(@Nullable E value) {
final int hash;
int index;
if (value == null) {
hash = 0;
index = indexOfNull();
} else {
- hash = mIdentityHashCode ? System.identityHashCode(value) : value.hashCode();
+ hash = value.hashCode();
index = indexOf(value, hash);
}
if (index >= 0) {
@@ -413,8 +410,7 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
@RestrictTo(LIBRARY_GROUP)
public void append(E value) {
final int index = mSize;
- final int hash = value == null ? 0
- : (mIdentityHashCode ? System.identityHashCode(value) : value.hashCode());
+ final int hash = value == null ? 0 : value.hashCode();
if (index >= mHashes.length) {
throw new IllegalStateException("Array is full");
}
@@ -439,7 +435,7 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
* Perform a {@link #add(Object)} of all values in <var>array</var>
* @param array The array whose contents are to be retrieved.
*/
- public void addAll(ArraySet<? extends E> array) {
+ public void addAll(@NonNull ArraySet<? extends E> array) {
final int N = array.mSize;
ensureCapacity(mSize + N);
if (mSize == 0) {
@@ -555,6 +551,7 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
return mSize;
}
+ @NonNull
@Override
public Object[] toArray() {
Object[] result = new Object[mSize];
@@ -562,8 +559,9 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
return result;
}
+ @NonNull
@Override
- public <T> T[] toArray(T[] array) {
+ public <T> T[] toArray(@NonNull T[] array) {
if (array.length < mSize) {
@SuppressWarnings("unchecked") T[] newArray =
(T[]) Array.newInstance(array.getClass().getComponentType(), mSize);
@@ -732,7 +730,7 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
* in <var>collection</var>, else returns false.
*/
@Override
- public boolean containsAll(Collection<?> collection) {
+ public boolean containsAll(@NonNull Collection<?> collection) {
Iterator<?> it = collection.iterator();
while (it.hasNext()) {
if (!contains(it.next())) {
@@ -747,7 +745,7 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
* @param collection The collection whose contents are to be retrieved.
*/
@Override
- public boolean addAll(Collection<? extends E> collection) {
+ public boolean addAll(@NonNull Collection<? extends E> collection) {
ensureCapacity(mSize + collection.size());
boolean added = false;
for (E value : collection) {
@@ -762,7 +760,7 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
* @return Returns true if any values were removed from the array set, else false.
*/
@Override
- public boolean removeAll(Collection<?> collection) {
+ public boolean removeAll(@NonNull Collection<?> collection) {
boolean removed = false;
for (Object value : collection) {
removed |= remove(value);
@@ -777,7 +775,7 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
* @return Returns true if any values were removed from the array set, else false.
*/
@Override
- public boolean retainAll(Collection<?> collection) {
+ public boolean retainAll(@NonNull Collection<?> collection) {
boolean removed = false;
for (int i = mSize - 1; i >= 0; i--) {
if (!collection.contains(mArray[i])) {
diff --git a/android/support/v4/util/LongSparseArray.java b/android/support/v4/util/LongSparseArray.java
index 25b6bb97..febb5d52 100644
--- a/android/support/v4/util/LongSparseArray.java
+++ b/android/support/v4/util/LongSparseArray.java
@@ -235,6 +235,14 @@ public class LongSparseArray<E> implements Cloneable {
}
/**
+ * Return true if size() is 0.
+ * @return true if size() is 0.
+ */
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ /**
* Given an index in the range <code>0...size()-1</code>, returns
* the key from the <code>index</code>th key-value mapping that this
* LongSparseArray stores.
diff --git a/android/support/v4/util/ObjectsCompat.java b/android/support/v4/util/ObjectsCompat.java
index b6c740e5..747cfb45 100644
--- a/android/support/v4/util/ObjectsCompat.java
+++ b/android/support/v4/util/ObjectsCompat.java
@@ -18,6 +18,7 @@ package android.support.v4.util;
import android.os.Build;
import android.support.annotation.Nullable;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -51,4 +52,46 @@ public class ObjectsCompat {
return (a == b) || (a != null && a.equals(b));
}
}
+
+ /**
+ * Returns the hash code of a non-{@code null} argument and 0 for a {@code null} argument.
+ *
+ * @param o an object
+ * @return the hash code of a non-{@code null} argument and 0 for a {@code null} argument
+ * @see Object#hashCode
+ */
+ public static int hashCode(@Nullable Object o) {
+ return o != null ? o.hashCode() : 0;
+ }
+
+ /**
+ * Generates a hash code for a sequence of input values. The hash code is generated as if all
+ * the input values were placed into an array, and that array were hashed by calling
+ * {@link Arrays#hashCode(Object[])}.
+ *
+ * <p>This method is useful for implementing {@link Object#hashCode()} on objects containing
+ * multiple fields. For example, if an object that has three fields, {@code x}, {@code y}, and
+ * {@code z}, one could write:
+ *
+ * <blockquote><pre>
+ * &#064;Override public int hashCode() {
+ * return ObjectsCompat.hash(x, y, z);
+ * }
+ * </pre></blockquote>
+ *
+ * <b>Warning: When a single object reference is supplied, the returned value does not equal the
+ * hash code of that object reference.</b> This value can be computed by calling
+ * {@link #hashCode(Object)}.
+ *
+ * @param values the values to be hashed
+ * @return a hash value of the sequence of input values
+ * @see Arrays#hashCode(Object[])
+ */
+ public static int hash(@Nullable Object... values) {
+ if (Build.VERSION.SDK_INT >= 19) {
+ return Objects.hash(values);
+ } else {
+ return Arrays.hashCode(values);
+ }
+ }
}
diff --git a/android/support/v4/util/SparseArrayCompat.java b/android/support/v4/util/SparseArrayCompat.java
index aedc4ad0..5238cf06 100644
--- a/android/support/v4/util/SparseArrayCompat.java
+++ b/android/support/v4/util/SparseArrayCompat.java
@@ -228,6 +228,14 @@ public class SparseArrayCompat<E> implements Cloneable {
}
/**
+ * Return true if size() is 0.
+ * @return true if size() is 0.
+ */
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ /**
* Given an index in the range <code>0...size()-1</code>, returns
* the key from the <code>index</code>th key-value mapping that this
* SparseArray stores.
diff --git a/android/support/v4/view/ViewCompat.java b/android/support/v4/view/ViewCompat.java
index 204a1218..abdaa1ad 100644
--- a/android/support/v4/view/ViewCompat.java
+++ b/android/support/v4/view/ViewCompat.java
@@ -60,6 +60,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Helper for accessing features in {@link View}.
@@ -443,6 +444,7 @@ public class ViewCompat {
private static Field sMinHeightField;
private static boolean sMinHeightFieldFetched;
private static WeakHashMap<View, String> sTransitionNameMap;
+ private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
private Method mDispatchStartTemporaryDetach;
private Method mDispatchFinishTemporaryDetach;
private boolean mTempDetachBound;
@@ -1020,6 +1022,21 @@ public class ViewCompat {
public boolean isImportantForAutofill(@NonNull View v) {
return true;
}
+
+ /**
+ * {@link ViewCompat#generateViewId()}
+ */
+ public int generateViewId() {
+ for (;;) {
+ final int result = sNextGeneratedId.get();
+ // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
+ int newValue = result + 1;
+ if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
+ if (sNextGeneratedId.compareAndSet(result, newValue)) {
+ return result;
+ }
+ }
+ }
}
@RequiresApi(15)
@@ -1178,6 +1195,11 @@ public class ViewCompat {
public Display getDisplay(View view) {
return view.getDisplay();
}
+
+ @Override
+ public int generateViewId() {
+ return View.generateViewId();
+ }
}
@RequiresApi(18)
@@ -2413,6 +2435,30 @@ public class ViewCompat {
}
/**
+ * Finds the first descendant view with the given ID, the view itself if the ID matches
+ * {@link View#getId()}, or throws an IllegalArgumentException if the ID is invalid or there
+ * is no matching view in the hierarchy.
+ * <p>
+ * <strong>Note:</strong> In most cases -- depending on compiler support --
+ * the resulting view is automatically cast to the target class type. If
+ * the target class type is unconstrained, an explicit cast may be
+ * necessary.
+ *
+ * @param id the ID to search for
+ * @return a view with given ID
+ * @see View#findViewById(int)
+ */
+ @NonNull
+ public static <T extends View> T requireViewById(@NonNull View view, @IdRes int id) {
+ // TODO: use and link to View#requireViewById() directly, once available
+ T targetView = view.findViewById(id);
+ if (targetView == null) {
+ throw new IllegalArgumentException("ID does not reference a View inside this View");
+ }
+ return targetView;
+ }
+
+ /**
* Indicates whether this View is opaque. An opaque View guarantees that it will
* draw all the pixels overlapping its bounds using a fully opaque color.
*
@@ -3931,5 +3977,15 @@ public class ViewCompat {
return IMPL.hasExplicitFocusable(view);
}
+ /**
+ * Generate a value suitable for use in {@link View#setId(int)}.
+ * This value will not collide with ID values generated at build time by aapt for R.id.
+ *
+ * @return a generated ID value
+ */
+ public static int generateViewId() {
+ return IMPL.generateViewId();
+ }
+
protected ViewCompat() {}
}
diff --git a/android/support/v4/view/ViewConfigurationCompat.java b/android/support/v4/view/ViewConfigurationCompat.java
index 60d37a9f..a12387b1 100644
--- a/android/support/v4/view/ViewConfigurationCompat.java
+++ b/android/support/v4/view/ViewConfigurationCompat.java
@@ -117,5 +117,17 @@ public final class ViewConfigurationCompat {
return 0;
}
+ /**
+ * @param config Used to get the hover slop directly from the {@link ViewConfiguration}.
+ *
+ * @return The hover slop value.
+ */
+ public static int getScaledHoverSlop(ViewConfiguration config) {
+ if (android.os.Build.VERSION.SDK_INT >= 28) {
+ return config.getScaledHoverSlop();
+ }
+ return config.getScaledTouchSlop() / 2;
+ }
+
private ViewConfigurationCompat() {}
}
diff --git a/android/support/v4/view/WindowCompat.java b/android/support/v4/view/WindowCompat.java
index cdf4789c..dd0a736c 100644
--- a/android/support/v4/view/WindowCompat.java
+++ b/android/support/v4/view/WindowCompat.java
@@ -16,6 +16,8 @@
package android.support.v4.view;
+import android.support.annotation.IdRes;
+import android.support.annotation.NonNull;
import android.view.View;
import android.view.Window;
@@ -59,4 +61,29 @@ public final class WindowCompat {
public static final int FEATURE_ACTION_MODE_OVERLAY = 10;
private WindowCompat() {}
+
+ /**
+ * Finds a view that was identified by the {@code android:id} XML attribute
+ * that was processed in {@link android.app.Activity#onCreate}, or throws an
+ * IllegalArgumentException if the ID is invalid, or there is no matching view in the hierarchy.
+ * <p>
+ * <strong>Note:</strong> In most cases -- depending on compiler support --
+ * the resulting view is automatically cast to the target class type. If
+ * the target class type is unconstrained, an explicit cast may be
+ * necessary.
+ *
+ * @param id the ID to search for
+ * @return a view with given ID
+ * @see ViewCompat#requireViewById(View, int)
+ * @see Window#findViewById(int)
+ */
+ @NonNull
+ public static <T extends View> T requireViewById(@NonNull Window window, @IdRes int id) {
+ // TODO: use and link to Window#requireViewById() directly, once available
+ T view = window.findViewById(id);
+ if (view == null) {
+ throw new IllegalArgumentException("ID does not reference a View inside this Window");
+ }
+ return view;
+ }
}
diff --git a/android/support/v4/widget/ContentLoadingProgressBar.java b/android/support/v4/widget/ContentLoadingProgressBar.java
index 356c7b9e..631bec5b 100644
--- a/android/support/v4/widget/ContentLoadingProgressBar.java
+++ b/android/support/v4/widget/ContentLoadingProgressBar.java
@@ -93,9 +93,10 @@ public class ContentLoadingProgressBar extends ProgressBar {
* hidden until it has been shown for at least a minimum show time. If the
* progress view was not yet visible, cancels showing the progress view.
*/
- public void hide() {
+ public synchronized void hide() {
mDismissed = true;
removeCallbacks(mDelayedShow);
+ mPostedShow = false;
long diff = System.currentTimeMillis() - mStartTime;
if (diff >= MIN_SHOW_TIME || mStartTime == -1) {
// The progress spinner has been shown long enough
@@ -117,11 +118,12 @@ public class ContentLoadingProgressBar extends ProgressBar {
* Show the progress view after waiting for a minimum delay. If
* during that time, hide() is called, the view is never made visible.
*/
- public void show() {
+ public synchronized void show() {
// Reset the start time.
mStartTime = -1;
mDismissed = false;
removeCallbacks(mDelayedHide);
+ mPostedHide = false;
if (!mPostedShow) {
postDelayed(mDelayedShow, MIN_DELAY);
mPostedShow = true;
diff --git a/android/support/v4/widget/CursorAdapter.java b/android/support/v4/widget/CursorAdapter.java
index e68229e0..3ea6fc8a 100644
--- a/android/support/v4/widget/CursorAdapter.java
+++ b/android/support/v4/widget/CursorAdapter.java
@@ -43,55 +43,55 @@ public abstract class CursorAdapter extends BaseAdapter implements Filterable,
CursorFilter.CursorFilterClient {
/**
* This field should be made private, so it is hidden from the SDK.
- * {@hide}
+ * @hide
*/
@RestrictTo(LIBRARY_GROUP)
protected boolean mDataValid;
/**
* This field should be made private, so it is hidden from the SDK.
- * {@hide}
+ * @hide
*/
@RestrictTo(LIBRARY_GROUP)
protected boolean mAutoRequery;
/**
* This field should be made private, so it is hidden from the SDK.
- * {@hide}
+ * @hide
*/
@RestrictTo(LIBRARY_GROUP)
protected Cursor mCursor;
/**
* This field should be made private, so it is hidden from the SDK.
- * {@hide}
+ * @hide
*/
@RestrictTo(LIBRARY_GROUP)
protected Context mContext;
/**
* This field should be made private, so it is hidden from the SDK.
- * {@hide}
+ * @hide
*/
@RestrictTo(LIBRARY_GROUP)
protected int mRowIDColumn;
/**
* This field should be made private, so it is hidden from the SDK.
- * {@hide}
+ * @hide
*/
@RestrictTo(LIBRARY_GROUP)
protected ChangeObserver mChangeObserver;
/**
* This field should be made private, so it is hidden from the SDK.
- * {@hide}
+ * @hide
*/
@RestrictTo(LIBRARY_GROUP)
protected DataSetObserver mDataSetObserver;
/**
* This field should be made private, so it is hidden from the SDK.
- * {@hide}
+ * @hide
*/
@RestrictTo(LIBRARY_GROUP)
protected CursorFilter mCursorFilter;
/**
* This field should be made private, so it is hidden from the SDK.
- * {@hide}
+ * @hide
*/
@RestrictTo(LIBRARY_GROUP)
protected FilterQueryProvider mFilterQueryProvider;
diff --git a/android/support/v4/widget/SimpleCursorAdapter.java b/android/support/v4/widget/SimpleCursorAdapter.java
index 291f9e15..ba3ee506 100644
--- a/android/support/v4/widget/SimpleCursorAdapter.java
+++ b/android/support/v4/widget/SimpleCursorAdapter.java
@@ -37,14 +37,14 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter {
/**
* A list of columns containing the data to bind to the UI.
* This field should be made private, so it is hidden from the SDK.
- * {@hide}
+ * @hide
*/
@RestrictTo(LIBRARY_GROUP)
protected int[] mFrom;
/**
* A list of View ids representing the views to which the data must be bound.
* This field should be made private, so it is hidden from the SDK.
- * {@hide}
+ * @hide
*/
@RestrictTo(LIBRARY_GROUP)
protected int[] mTo;
diff --git a/android/support/v4/widget/TextViewCompat.java b/android/support/v4/widget/TextViewCompat.java
index dc87a38b..8789815f 100644
--- a/android/support/v4/widget/TextViewCompat.java
+++ b/android/support/v4/widget/TextViewCompat.java
@@ -18,6 +18,11 @@ package android.support.v4.widget;
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.DrawableRes;
@@ -28,14 +33,22 @@ import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.annotation.StyleRes;
import android.support.v4.os.BuildCompat;
+import android.text.Editable;
import android.util.Log;
import android.util.TypedValue;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
/**
* Helper for accessing features in {@link TextView}.
@@ -219,6 +232,11 @@ public final class TextViewCompat {
}
return new int[0];
}
+
+ public void setCustomSelectionActionModeCallback(TextView textView,
+ ActionMode.Callback callback) {
+ textView.setCustomSelectionActionModeCallback(callback);
+ }
}
@RequiresApi(16)
@@ -314,8 +332,160 @@ public final class TextViewCompat {
}
}
+ @RequiresApi(26)
+ static class TextViewCompatApi26Impl extends TextViewCompatApi23Impl {
+ @Override
+ public void setCustomSelectionActionModeCallback(final TextView textView,
+ final ActionMode.Callback callback) {
+ if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O
+ && Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1) {
+ super.setCustomSelectionActionModeCallback(textView, callback);
+ return;
+ }
+
+
+ // A bug in O and O_MR1 causes a number of options for handling the ACTION_PROCESS_TEXT
+ // intent after selection to not be displayed in the menu, although they should be.
+ // Here we fix this, by removing the menu items created by the framework code, and
+ // adding them (and the missing ones) back correctly.
+ textView.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
+ // This constant should be correlated with its definition in the
+ // android.widget.Editor class.
+ private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100;
+
+ // References to the MenuBuilder class and its removeItemAt(int) method.
+ // Since in most cases the menu instance processed by this callback is going
+ // to be a MenuBuilder, we keep these references to avoid querying for them
+ // frequently by reflection in recomputeProcessTextMenuItems.
+ private Class mMenuBuilderClass;
+ private Method mMenuBuilderRemoveItemAtMethod;
+ private boolean mCanUseMenuBuilderReferences;
+ private boolean mInitializedMenuBuilderReferences = false;
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ return callback.onCreateActionMode(mode, menu);
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ recomputeProcessTextMenuItems(menu);
+ return callback.onPrepareActionMode(mode, menu);
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return callback.onActionItemClicked(mode, item);
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ callback.onDestroyActionMode(mode);
+ }
+
+ private void recomputeProcessTextMenuItems(final Menu menu) {
+ final Context context = textView.getContext();
+ final PackageManager packageManager = context.getPackageManager();
+
+ if (!mInitializedMenuBuilderReferences) {
+ mInitializedMenuBuilderReferences = true;
+ try {
+ mMenuBuilderClass =
+ Class.forName("com.android.internal.view.menu.MenuBuilder");
+ mMenuBuilderRemoveItemAtMethod = mMenuBuilderClass
+ .getDeclaredMethod("removeItemAt", Integer.TYPE);
+ mCanUseMenuBuilderReferences = true;
+ } catch (ClassNotFoundException | NoSuchMethodException e) {
+ mMenuBuilderClass = null;
+ mMenuBuilderRemoveItemAtMethod = null;
+ mCanUseMenuBuilderReferences = false;
+ }
+ }
+ // Remove the menu items created for ACTION_PROCESS_TEXT handlers.
+ try {
+ final Method removeItemAtMethod =
+ (mCanUseMenuBuilderReferences && mMenuBuilderClass.isInstance(menu))
+ ? mMenuBuilderRemoveItemAtMethod
+ : menu.getClass()
+ .getDeclaredMethod("removeItemAt", Integer.TYPE);
+ for (int i = menu.size() - 1; i >= 0; --i) {
+ final MenuItem item = menu.getItem(i);
+ if (item.getIntent() != null && Intent.ACTION_PROCESS_TEXT
+ .equals(item.getIntent().getAction())) {
+ removeItemAtMethod.invoke(menu, i);
+ }
+ }
+ } catch (NoSuchMethodException | IllegalAccessException
+ | InvocationTargetException e) {
+ // There is a menu custom implementation used which is not providing
+ // a removeItemAt(int) menu. There is nothing we can do in this case.
+ return;
+ }
+
+ // Populate the menu again with the ACTION_PROCESS_TEXT handlers.
+ final List<ResolveInfo> supportedActivities =
+ getSupportedActivities(context, packageManager);
+ for (int i = 0; i < supportedActivities.size(); ++i) {
+ final ResolveInfo info = supportedActivities.get(i);
+ menu.add(Menu.NONE, Menu.NONE,
+ MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i,
+ info.loadLabel(packageManager))
+ .setIntent(createProcessTextIntentForResolveInfo(info, textView))
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ }
+ }
+
+ private List<ResolveInfo> getSupportedActivities(final Context context,
+ final PackageManager packageManager) {
+ final List<ResolveInfo> supportedActivities = new ArrayList<>();
+ boolean canStartActivityForResult = context instanceof Activity;
+ if (!canStartActivityForResult) {
+ return supportedActivities;
+ }
+ final List<ResolveInfo> unfiltered =
+ packageManager.queryIntentActivities(createProcessTextIntent(), 0);
+ for (ResolveInfo info : unfiltered) {
+ if (isSupportedActivity(info, context)) {
+ supportedActivities.add(info);
+ }
+ }
+ return supportedActivities;
+ }
+
+ private boolean isSupportedActivity(final ResolveInfo info, final Context context) {
+ if (context.getPackageName().equals(info.activityInfo.packageName)) {
+ return true;
+ }
+ if (!info.activityInfo.exported) {
+ return false;
+ }
+ return info.activityInfo.permission == null
+ || context.checkSelfPermission(info.activityInfo.permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private Intent createProcessTextIntentForResolveInfo(final ResolveInfo info,
+ final TextView textView) {
+ return createProcessTextIntent()
+ .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, !isEditable(textView))
+ .setClassName(info.activityInfo.packageName, info.activityInfo.name);
+ }
+
+ private boolean isEditable(final TextView textView) {
+ return textView instanceof Editable
+ && textView.onCheckIsTextEditor()
+ && textView.isEnabled();
+ }
+
+ private Intent createProcessTextIntent() {
+ return new Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/plain");
+ }
+ });
+ }
+ }
+
@RequiresApi(27)
- static class TextViewCompatApi27Impl extends TextViewCompatApi23Impl {
+ static class TextViewCompatApi27Impl extends TextViewCompatApi26Impl {
@Override
public void setAutoSizeTextTypeWithDefaults(TextView textView, int autoSizeTextType) {
textView.setAutoSizeTextTypeWithDefaults(autoSizeTextType);
@@ -369,6 +539,8 @@ public final class TextViewCompat {
static {
if (BuildCompat.isAtLeastOMR1()) {
IMPL = new TextViewCompatApi27Impl();
+ } else if (Build.VERSION.SDK_INT >= 26) {
+ IMPL = new TextViewCompatApi26Impl();
} else if (Build.VERSION.SDK_INT >= 23) {
IMPL = new TextViewCompatApi23Impl();
} else if (Build.VERSION.SDK_INT >= 18) {
@@ -600,4 +772,31 @@ public final class TextViewCompat {
public static int[] getAutoSizeTextAvailableSizes(@NonNull TextView textView) {
return IMPL.getAutoSizeTextAvailableSizes(textView);
}
+
+ /**
+ * Sets a selection action mode callback on a TextView.
+ *
+ * Also this method can be used to fix a bug in framework SDK 26. On these affected devices,
+ * the bug causes the menu containing the options for handling ACTION_PROCESS_TEXT after text
+ * selection to miss a number of items. This method can be used to fix this wrong behaviour for
+ * a text view, by passing any custom callback implementation. If no custom callback is desired,
+ * a no-op implementation should be provided.
+ *
+ * Note that, by default, the bug will only be fixed when the default floating toolbar menu
+ * implementation is used. If a custom implementation of {@link Menu} is provided, this should
+ * provide the method Menu#removeItemAt(int) which removes a menu item by its position,
+ * as given by Menu#getItem(int). Also, the following post condition should hold: a call
+ * to removeItemAt(i), should not modify the results of getItem(j) for any j < i. Intuitively,
+ * removing an element from the menu should behave as removing an element from a list.
+ * Note that this method does not exist in the {@link Menu} interface. However, it is required,
+ * and going to be called by reflection, in order to display the correct process text items in
+ * the menu.
+ *
+ * @param textView The TextView to set the action selection mode callback on.
+ * @param callback The action selection mode callback to set on textView.
+ */
+ public static void setCustomSelectionActionModeCallback(@NonNull TextView textView,
+ @NonNull ActionMode.Callback callback) {
+ IMPL.setCustomSelectionActionModeCallback(textView, callback);
+ }
}
diff --git a/android/support/v7/preference/Preference.java b/android/support/v7/preference/Preference.java
index fa8461db..88262cd9 100644
--- a/android/support/v7/preference/Preference.java
+++ b/android/support/v7/preference/Preference.java
@@ -16,6 +16,7 @@
package android.support.v7.preference;
+import static android.support.annotation.RestrictTo.Scope.LIBRARY;
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.content.Context;
@@ -1297,6 +1298,7 @@ public class Preference implements Comparable<Preference> {
* preference was removed, modified, and re-added to a {@link PreferenceGroup}
* @hide
*/
+ @RestrictTo(LIBRARY)
public final boolean wasDetached() {
return mWasDetached;
}
@@ -1305,6 +1307,7 @@ public class Preference implements Comparable<Preference> {
* Clears the {@link #wasDetached()} status
* @hide
*/
+ @RestrictTo(LIBRARY)
public final void clearWasDetached() {
mWasDetached = false;
}
diff --git a/android/support/v7/recyclerview/extensions/ListAdapter.java b/android/support/v7/recyclerview/extensions/ListAdapter.java
index 8b28072e..721e0da4 100644
--- a/android/support/v7/recyclerview/extensions/ListAdapter.java
+++ b/android/support/v7/recyclerview/extensions/ListAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2017 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.
@@ -17,6 +17,8 @@
package android.support.v7.recyclerview.extensions;
import android.support.annotation.NonNull;
+import android.support.v7.util.AdapterListUpdateCallback;
+import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
import java.util.List;
@@ -66,7 +68,8 @@ import java.util.List;
* public void onBindViewHolder(UserViewHolder holder, int position) {
* holder.bindTo(getItem(position));
* }
- * public static final DiffCallback&lt;User> DIFF_CALLBACK = new DiffCallback&lt;User>() {
+ * public static final DiffUtil.ItemCallback&lt;User> DIFF_CALLBACK =
+ * new DiffUtil.ItemCallback&lt;User>() {
* {@literal @}Override
* public boolean areItemsTheSame(
* {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
@@ -95,14 +98,14 @@ public abstract class ListAdapter<T, VH extends RecyclerView.ViewHolder>
private final ListAdapterHelper<T> mHelper;
@SuppressWarnings("unused")
- protected ListAdapter(@NonNull DiffCallback<T> diffCallback) {
- mHelper = new ListAdapterHelper<>(new ListAdapterHelper.AdapterCallback(this),
- new ListAdapterConfig.Builder<T>().setDiffCallback(diffCallback).build());
+ protected ListAdapter(@NonNull DiffUtil.ItemCallback<T> diffCallback) {
+ mHelper = new ListAdapterHelper<>(new AdapterListUpdateCallback(this),
+ new ListAdapterConfig.Builder<>(diffCallback).build());
}
@SuppressWarnings("unused")
protected ListAdapter(@NonNull ListAdapterConfig<T> config) {
- mHelper = new ListAdapterHelper<>(new ListAdapterHelper.AdapterCallback(this), config);
+ mHelper = new ListAdapterHelper<>(new AdapterListUpdateCallback(this), config);
}
/**
diff --git a/android/support/v7/recyclerview/extensions/ListAdapterConfig.java b/android/support/v7/recyclerview/extensions/ListAdapterConfig.java
index 25697a11..53fe4bbf 100644
--- a/android/support/v7/recyclerview/extensions/ListAdapterConfig.java
+++ b/android/support/v7/recyclerview/extensions/ListAdapterConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2017 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.
@@ -16,79 +16,91 @@
package android.support.v7.recyclerview.extensions;
-import android.arch.core.executor.ArchTaskExecutor;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.v7.util.DiffUtil;
import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
/**
* Configuration object for {@link ListAdapter}, {@link ListAdapterHelper}, and similar
* background-thread list diffing adapter logic.
* <p>
- * At minimum, defines item diffing behavior with a {@link DiffCallback}, used to compute item
- * differences to pass to a RecyclerView adapter.
+ * At minimum, defines item diffing behavior with a {@link DiffUtil.ItemCallback}, used to compute
+ * item differences to pass to a RecyclerView adapter.
*
* @param <T> Type of items in the lists, and being compared.
*/
public final class ListAdapterConfig<T> {
+ @NonNull
private final Executor mMainThreadExecutor;
+ @NonNull
private final Executor mBackgroundThreadExecutor;
- private final DiffCallback<T> mDiffCallback;
+ @NonNull
+ private final DiffUtil.ItemCallback<T> mDiffCallback;
- private ListAdapterConfig(Executor mainThreadExecutor, Executor backgroundThreadExecutor,
- DiffCallback<T> diffCallback) {
+ private ListAdapterConfig(
+ @NonNull Executor mainThreadExecutor,
+ @NonNull Executor backgroundThreadExecutor,
+ @NonNull DiffUtil.ItemCallback<T> diffCallback) {
mMainThreadExecutor = mainThreadExecutor;
mBackgroundThreadExecutor = backgroundThreadExecutor;
mDiffCallback = diffCallback;
}
+ /** @hide */
+ @SuppressWarnings("WeakerAccess")
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @NonNull
public Executor getMainThreadExecutor() {
return mMainThreadExecutor;
}
+ /** @hide */
+ @SuppressWarnings("WeakerAccess")
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @NonNull
public Executor getBackgroundThreadExecutor() {
return mBackgroundThreadExecutor;
}
- public DiffCallback<T> getDiffCallback() {
+ @SuppressWarnings("WeakerAccess")
+ @NonNull
+ public DiffUtil.ItemCallback<T> getDiffCallback() {
return mDiffCallback;
}
/**
* Builder class for {@link ListAdapterConfig}.
- * <p>
- * You must at minimum specify a DiffCallback with {@link #setDiffCallback(DiffCallback)}
*
* @param <T>
*/
public static class Builder<T> {
private Executor mMainThreadExecutor;
private Executor mBackgroundThreadExecutor;
- private DiffCallback<T> mDiffCallback;
+ private final DiffUtil.ItemCallback<T> mDiffCallback;
- /**
- * The {@link DiffCallback} to be used while diffing an old list with the updated one.
- * Must be provided.
- *
- * @param diffCallback The {@link DiffCallback} instance to compare items in the list.
- * @return this
- */
- @SuppressWarnings("WeakerAccess")
- public ListAdapterConfig.Builder<T> setDiffCallback(DiffCallback<T> diffCallback) {
+ public Builder(@NonNull DiffUtil.ItemCallback<T> diffCallback) {
mDiffCallback = diffCallback;
- return this;
}
/**
* If provided, defines the main thread executor used to dispatch adapter update
* notifications on the main thread.
* <p>
- * If not provided, it will default to the UI thread.
+ * If not provided, it will default to the main thread.
*
* @param executor The executor which can run tasks in the UI thread.
* @return this
+ *
+ * @hide
*/
- @SuppressWarnings("unused")
- public ListAdapterConfig.Builder<T> setMainThreadExecutor(Executor executor) {
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @NonNull
+ public Builder<T> setMainThreadExecutor(Executor executor) {
mMainThreadExecutor = executor;
return this;
}
@@ -97,36 +109,55 @@ public final class ListAdapterConfig<T> {
* If provided, defines the background executor used to calculate the diff between an old
* and a new list.
* <p>
- * If not provided, defaults to the IO thread pool from Architecture Components.
+ * If not provided, defaults to two thread pool executor, shared by all ListAdapterConfigs.
*
* @param executor The background executor to run list diffing.
* @return this
*/
- @SuppressWarnings("unused")
- public ListAdapterConfig.Builder<T> setBackgroundThreadExecutor(Executor executor) {
+ @SuppressWarnings({"unused", "WeakerAccess"})
+ @NonNull
+ public Builder<T> setBackgroundThreadExecutor(Executor executor) {
mBackgroundThreadExecutor = executor;
return this;
}
+ private static class MainThreadExecutor implements Executor {
+ final Handler mHandler = new Handler(Looper.getMainLooper());
+ @Override
+ public void execute(@NonNull Runnable command) {
+ mHandler.post(command);
+ }
+ }
+
/**
* Creates a {@link ListAdapterHelper} with the given parameters.
*
* @return A new ListAdapterConfig.
*/
+ @NonNull
public ListAdapterConfig<T> build() {
- if (mDiffCallback == null) {
- throw new IllegalArgumentException("Must provide a diffCallback");
+ if (mMainThreadExecutor == null) {
+ mMainThreadExecutor = sMainThreadExecutor;
}
if (mBackgroundThreadExecutor == null) {
- mBackgroundThreadExecutor = ArchTaskExecutor.getIOThreadExecutor();
- }
- if (mMainThreadExecutor == null) {
- mMainThreadExecutor = ArchTaskExecutor.getMainThreadExecutor();
+ synchronized (sExecutorLock) {
+ if (sDiffExecutor == null) {
+ sDiffExecutor = Executors.newFixedThreadPool(2);
+ }
+ }
+ mBackgroundThreadExecutor = sDiffExecutor;
}
return new ListAdapterConfig<>(
mMainThreadExecutor,
mBackgroundThreadExecutor,
mDiffCallback);
}
+
+ // TODO: remove the below once supportlib has its own appropriate executors
+ private static final Object sExecutorLock = new Object();
+ private static Executor sDiffExecutor = null;
+
+ // TODO: use MainThreadExecutor from supportlib once one exists
+ private static final Executor sMainThreadExecutor = new MainThreadExecutor();
}
}
diff --git a/android/support/v7/recyclerview/extensions/ListAdapterHelper.java b/android/support/v7/recyclerview/extensions/ListAdapterHelper.java
index d0c7bb3e..bb231b17 100644
--- a/android/support/v7/recyclerview/extensions/ListAdapterHelper.java
+++ b/android/support/v7/recyclerview/extensions/ListAdapterHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2017 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.
@@ -16,8 +16,8 @@
package android.support.v7.recyclerview.extensions;
-import android.arch.lifecycle.LiveData;
-import android.support.annotation.RestrictTo;
+import android.support.annotation.NonNull;
+import android.support.v7.util.AdapterListUpdateCallback;
import android.support.v7.util.DiffUtil;
import android.support.v7.util.ListUpdateCallback;
import android.support.v7.widget.RecyclerView;
@@ -25,17 +25,18 @@ import android.support.v7.widget.RecyclerView;
import java.util.List;
/**
- * Helper object for displaying a List in {@link RecyclerView.Adapter RecyclerView.Adapter}, which
- * signals the adapter of changes when the List is changed by computing changes with DiffUtil in the
+ * Helper object for displaying a List in
+ * {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, which signals the
+ * adapter of changes when the List is changed by computing changes with DiffUtil in the
* background.
* <p>
* For simplicity, the {@link ListAdapter} wrapper class can often be used instead of the
* helper directly. This helper class is exposed for complex cases, and where overriding an adapter
* base class to support List diffing isn't convenient.
* <p>
- * The ListAdapterHelper can take a {@link LiveData} of List and present the data simply for an
- * adapter. It computes differences in List contents via DiffUtil on a background thread as new
- * Lists are received.
+ * The ListAdapterHelper can consume the values from a LiveData of <code>List</code> and present the
+ * data simply for an adapter. It computes differences in List contents via {@link DiffUtil} on a
+ * background thread as new <code>List</code>s are received.
* <p>
* It provides a simple list-like API with {@link #getItem(int)} and {@link #getItemCount()} for an
* adapter to acquire and present data objects.
@@ -68,10 +69,8 @@ import java.util.List;
* }
*
* class UserAdapter extends RecyclerView.Adapter&lt;UserViewHolder> {
- * private final ListAdapterHelper&lt;User> mHelper;
- * public UserAdapter(ListAdapterHelper.Builder&lt;User> builder) {
- * mHelper = new ListAdapterHelper(this, User.DIFF_CALLBACK);
- * }
+ * private final ListAdapterHelper&lt;User> mHelper =
+ * new ListAdapterHelper(this, DIFF_CALLBACK);
* {@literal @}Override
* public int getItemCount() {
* return mHelper.getItemCount();
@@ -84,7 +83,8 @@ import java.util.List;
* User user = mHelper.getItem(position);
* holder.bindTo(user);
* }
- * public static final DiffCallback&lt;User> DIFF_CALLBACK = new DiffCallback&lt;User>() {
+ * public static final DiffUtil.ItemCallback&lt;User> DIFF_CALLBACK
+ * = new DiffUtil.ItemCallback&lt;User>() {
* {@literal @}Override
* public boolean areItemsTheSame(
* {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
@@ -107,47 +107,37 @@ public class ListAdapterHelper<T> {
private final ListUpdateCallback mUpdateCallback;
private final ListAdapterConfig<T> mConfig;
- @SuppressWarnings("WeakerAccess")
- public ListAdapterHelper(ListUpdateCallback listUpdateCallback,
- ListAdapterConfig<T> config) {
- mUpdateCallback = listUpdateCallback;
- mConfig = config;
+ /**
+ * Convenience for
+ * {@code PagedListAdapterHelper(new AdapterListUpdateCallback(adapter),
+ * new ListAdapterConfig.Builder().setDiffCallback(diffCallback).build());}
+ *
+ * @param adapter Adapter to dispatch position updates to.
+ * @param diffCallback ItemCallback that compares items to dispatch appropriate animations when
+ *
+ * @see DiffUtil.DiffResult#dispatchUpdatesTo(RecyclerView.Adapter)
+ */
+ public ListAdapterHelper(@NonNull RecyclerView.Adapter adapter,
+ @NonNull DiffUtil.ItemCallback<T> diffCallback) {
+ mUpdateCallback = new AdapterListUpdateCallback(adapter);
+ mConfig = new ListAdapterConfig.Builder<>(diffCallback).build();
}
/**
- * Default ListUpdateCallback that dispatches directly to an adapter. Can be replaced by a
- * custom ListUpdateCallback if e.g. your adapter has a header in it, and so has an offset
- * between list positions and adapter positions.
+ * Create a ListAdapterHelper with the provided config, and ListUpdateCallback to dispatch
+ * updates to.
+ *
+ * @param listUpdateCallback Callback to dispatch updates to.
+ * @param config Config to define background work Executor, and DiffUtil.ItemCallback for
+ * computing List diffs.
*
- * @hide
+ * @see DiffUtil.DiffResult#dispatchUpdatesTo(RecyclerView.Adapter)
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public static class AdapterCallback implements ListUpdateCallback {
- private final RecyclerView.Adapter mAdapter;
-
- public AdapterCallback(RecyclerView.Adapter adapter) {
- mAdapter = adapter;
- }
-
- @Override
- public void onInserted(int position, int count) {
- mAdapter.notifyItemRangeInserted(position, count);
- }
-
- @Override
- public void onRemoved(int position, int count) {
- mAdapter.notifyItemRangeRemoved(position, count);
- }
-
- @Override
- public void onMoved(int fromPosition, int toPosition) {
- mAdapter.notifyItemMoved(fromPosition, toPosition);
- }
-
- @Override
- public void onChanged(int position, int count, Object payload) {
- mAdapter.notifyItemRangeChanged(position, count, payload);
- }
+ @SuppressWarnings("WeakerAccess")
+ public ListAdapterHelper(@NonNull ListUpdateCallback listUpdateCallback,
+ @NonNull ListAdapterConfig<T> config) {
+ mUpdateCallback = listUpdateCallback;
+ mConfig = config;
}
private List<T> mList;
@@ -173,7 +163,8 @@ public class ListAdapterHelper<T> {
/**
* Get the number of items currently presented by this AdapterHelper. This value can be directly
- * returned to {@link RecyclerView.Adapter#getItemCount()}.
+ * returned to {@link android.support.v7.widget.RecyclerView.Adapter#getItemCount()
+ * RecyclerView.Adapter.getItemCount()}.
*
* @return Number of items being presented.
*/
diff --git a/android/support/v7/util/AdapterListUpdateCallback.java b/android/support/v7/util/AdapterListUpdateCallback.java
new file mode 100644
index 00000000..f86ba7d0
--- /dev/null
+++ b/android/support/v7/util/AdapterListUpdateCallback.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018 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.util;
+
+import android.support.annotation.NonNull;
+import android.support.v7.widget.RecyclerView;
+
+/**
+ * ListUpdateCallback that dispatches update events to the given adapter.
+ *
+ * @see DiffUtil.DiffResult#dispatchUpdatesTo(RecyclerView.Adapter)
+ */
+public final class AdapterListUpdateCallback implements ListUpdateCallback {
+ @NonNull
+ private final RecyclerView.Adapter mAdapter;
+
+ /**
+ * Creates an AdapterListUpdateCallback that will dispatch update events to the given adapter.
+ *
+ * @param adapter The Adapter to send updates to.
+ */
+ public AdapterListUpdateCallback(@NonNull RecyclerView.Adapter adapter) {
+ mAdapter = adapter;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onInserted(int position, int count) {
+ mAdapter.notifyItemRangeInserted(position, count);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onRemoved(int position, int count) {
+ mAdapter.notifyItemRangeRemoved(position, count);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onMoved(int fromPosition, int toPosition) {
+ mAdapter.notifyItemMoved(fromPosition, toPosition);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onChanged(int position, int count, Object payload) {
+ mAdapter.notifyItemRangeChanged(position, count, payload);
+ }
+}
diff --git a/android/support/v7/util/DiffUtil.java b/android/support/v7/util/DiffUtil.java
index ebc33f31..a55a21d5 100644
--- a/android/support/v7/util/DiffUtil.java
+++ b/android/support/v7/util/DiffUtil.java
@@ -16,7 +16,6 @@
package android.support.v7.util;
-import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.v7.widget.RecyclerView;
@@ -369,7 +368,7 @@ public class DiffUtil {
*
* @see Callback#areItemsTheSame(int, int)
*/
- public abstract boolean areItemsTheSame(@NonNull T oldItem, @NonNull T newItem);
+ public abstract boolean areItemsTheSame(T oldItem, T newItem);
/**
* Called to check whether two items have the same data.
@@ -392,7 +391,7 @@ public class DiffUtil {
*
* @see Callback#areContentsTheSame(int, int)
*/
- public abstract boolean areContentsTheSame(@NonNull T oldItem, @NonNull T newItem);
+ public abstract boolean areContentsTheSame(T oldItem, T newItem);
/**
* When {@link #areItemsTheSame(T, T)} returns {@code true} for two items and
@@ -409,7 +408,7 @@ public class DiffUtil {
* @see Callback#getChangePayload(int, int)
*/
@SuppressWarnings({"WeakerAccess", "unused"})
- public Object getChangePayload(@NonNull T oldItem, @NonNull T newItem) {
+ public Object getChangePayload(T oldItem, T newItem) {
return null;
}
}
@@ -721,35 +720,16 @@ public class DiffUtil {
*
* @param adapter A RecyclerView adapter which was displaying the old list and will start
* displaying the new list.
+ * @see AdapterListUpdateCallback
*/
public void dispatchUpdatesTo(final RecyclerView.Adapter adapter) {
- dispatchUpdatesTo(new ListUpdateCallback() {
- @Override
- public void onInserted(int position, int count) {
- adapter.notifyItemRangeInserted(position, count);
- }
-
- @Override
- public void onRemoved(int position, int count) {
- adapter.notifyItemRangeRemoved(position, count);
- }
-
- @Override
- public void onMoved(int fromPosition, int toPosition) {
- adapter.notifyItemMoved(fromPosition, toPosition);
- }
-
- @Override
- public void onChanged(int position, int count, Object payload) {
- adapter.notifyItemRangeChanged(position, count, payload);
- }
- });
+ dispatchUpdatesTo(new AdapterListUpdateCallback(adapter));
}
/**
* Dispatches update operations to the given Callback.
* <p>
- * These updates are atomic such that the first update call effects every update call that
+ * These updates are atomic such that the first update call affects every update call that
* comes after it (the same as RecyclerView).
*
* @param updateCallback The callback to receive the update operations.
diff --git a/android/support/v7/widget/AppCompatEditText.java b/android/support/v7/widget/AppCompatEditText.java
index 6831fcbf..fdda68eb 100644
--- a/android/support/v7/widget/AppCompatEditText.java
+++ b/android/support/v7/widget/AppCompatEditText.java
@@ -25,8 +25,10 @@ import android.graphics.drawable.Drawable;
import android.support.annotation.DrawableRes;
import android.support.annotation.Nullable;
import android.support.annotation.RestrictTo;
+import android.support.v4.os.BuildCompat;
import android.support.v4.view.TintableBackgroundView;
import android.support.v7.appcompat.R;
+import android.text.Editable;
import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -71,6 +73,20 @@ public class AppCompatEditText extends EditText implements TintableBackgroundVie
mTextHelper.applyCompoundDrawablesTints();
}
+ /**
+ * Return the text that the view is displaying. If an editable text has not been set yet, this
+ * will return null.
+ */
+ @Override
+ @Nullable public Editable getText() {
+ if (BuildCompat.isAtLeastP()) {
+ return super.getText();
+ }
+ // A bug pre-P makes getText() crash if called before the first setText due to a cast, so
+ // retrieve the editable text.
+ return super.getEditableText();
+ }
+
@Override
public void setBackgroundResource(@DrawableRes int resId) {
super.setBackgroundResource(resId);
diff --git a/android/support/v7/widget/AppCompatProgressBarHelper.java b/android/support/v7/widget/AppCompatProgressBarHelper.java
index 443281e2..a95873cb 100644
--- a/android/support/v7/widget/AppCompatProgressBarHelper.java
+++ b/android/support/v7/widget/AppCompatProgressBarHelper.java
@@ -27,7 +27,7 @@ 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.graphics.drawable.WrappedDrawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.widget.ProgressBar;
@@ -69,11 +69,11 @@ class AppCompatProgressBarHelper {
* traverse layer and state list drawables.
*/
private Drawable tileify(Drawable drawable, boolean clip) {
- if (drawable instanceof DrawableWrapper) {
- Drawable inner = ((DrawableWrapper) drawable).getWrappedDrawable();
+ if (drawable instanceof WrappedDrawable) {
+ Drawable inner = ((WrappedDrawable) drawable).getWrappedDrawable();
if (inner != null) {
inner = tileify(inner, clip);
- ((DrawableWrapper) drawable).setWrappedDrawable(inner);
+ ((WrappedDrawable) drawable).setWrappedDrawable(inner);
}
} else if (drawable instanceof LayerDrawable) {
LayerDrawable background = (LayerDrawable) drawable;
diff --git a/android/support/v7/widget/ContentFrameLayout.java b/android/support/v7/widget/ContentFrameLayout.java
index 11002805..f777901c 100644
--- a/android/support/v7/widget/ContentFrameLayout.java
+++ b/android/support/v7/widget/ContentFrameLayout.java
@@ -16,6 +16,7 @@
package android.support.v7.widget;
+import static android.support.annotation.RestrictTo.Scope.LIBRARY;
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static android.view.View.MeasureSpec.AT_MOST;
import static android.view.View.MeasureSpec.EXACTLY;
@@ -33,6 +34,7 @@ import android.widget.FrameLayout;
/**
* @hide
*/
+@RestrictTo(LIBRARY)
public class ContentFrameLayout extends FrameLayout {
public interface OnAttachListener {
diff --git a/android/support/v7/widget/DrawableUtils.java b/android/support/v7/widget/DrawableUtils.java
index c7820b6b..9216726e 100644
--- a/android/support/v7/widget/DrawableUtils.java
+++ b/android/support/v7/widget/DrawableUtils.java
@@ -30,6 +30,7 @@ import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.RestrictTo;
import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v4.graphics.drawable.WrappedDrawable;
import android.util.Log;
import java.lang.reflect.Field;
@@ -146,9 +147,9 @@ public class DrawableUtils {
}
}
}
- } else if (drawable instanceof android.support.v4.graphics.drawable.DrawableWrapper) {
+ } else if (drawable instanceof WrappedDrawable) {
return canSafelyMutateDrawable(
- ((android.support.v4.graphics.drawable.DrawableWrapper) drawable)
+ ((WrappedDrawable) drawable)
.getWrappedDrawable());
} else if (drawable instanceof android.support.v7.graphics.drawable.DrawableWrapper) {
return canSafelyMutateDrawable(
diff --git a/android/support/v7/widget/DropDownListView.java b/android/support/v7/widget/DropDownListView.java
index 5cad340e..cccb82be 100644
--- a/android/support/v7/widget/DropDownListView.java
+++ b/android/support/v7/widget/DropDownListView.java
@@ -17,12 +17,23 @@
package android.support.v7.widget;
import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.view.ViewPropertyAnimatorCompat;
import android.support.v4.widget.ListViewAutoScrollHelper;
import android.support.v7.appcompat.R;
+import android.support.v7.graphics.drawable.DrawableWrapper;
import android.view.MotionEvent;
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;
/**
* <p>Wrapper class for a ListView. This wrapper can hijack the focus to
@@ -30,7 +41,21 @@ import android.view.View;
* displayed on screen within a drop down. The focus is never actually
* passed to the drop down in this mode; the list only looks focused.</p>
*/
-class DropDownListView extends ListViewCompat {
+class DropDownListView extends ListView {
+ public static final int INVALID_POSITION = -1;
+ public static final int NO_POSITION = -1;
+
+ private final Rect mSelectorRect = new Rect();
+ private int mSelectionLeftPadding = 0;
+ private int mSelectionTopPadding = 0;
+ private int mSelectionRightPadding = 0;
+ private int mSelectionBottomPadding = 0;
+
+ private int mMotionPosition;
+
+ private Field mIsChildViewEnabled;
+
+ private GateKeeperDrawable mSelector;
/*
* WARNING: This is a workaround for a touch mode issue.
@@ -81,10 +106,306 @@ class DropDownListView extends ListViewCompat {
*
* @param context this view's context
*/
- public DropDownListView(Context context, boolean hijackFocus) {
+ DropDownListView(Context context, boolean hijackFocus) {
super(context, null, R.attr.dropDownListViewStyle);
mHijackFocus = hijackFocus;
setCacheColorHint(0); // Transparent, since the background drawable could be anything.
+
+ try {
+ mIsChildViewEnabled = AbsListView.class.getDeclaredField("mIsChildViewEnabled");
+ mIsChildViewEnabled.setAccessible(true);
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ @Override
+ public boolean isInTouchMode() {
+ // WARNING: Please read the comment where mListSelectionHidden is declared
+ return (mHijackFocus && mListSelectionHidden) || super.isInTouchMode();
+ }
+
+ /**
+ * <p>Returns the focus state in the drop down.</p>
+ *
+ * @return true always if hijacking focus
+ */
+ @Override
+ public boolean hasWindowFocus() {
+ return mHijackFocus || super.hasWindowFocus();
+ }
+
+ /**
+ * <p>Returns the focus state in the drop down.</p>
+ *
+ * @return true always if hijacking focus
+ */
+ @Override
+ public boolean isFocused() {
+ return mHijackFocus || super.isFocused();
+ }
+
+ /**
+ * <p>Returns the focus state in the drop down.</p>
+ *
+ * @return true always if hijacking focus
+ */
+ @Override
+ public boolean hasFocus() {
+ return mHijackFocus || super.hasFocus();
+ }
+
+ @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);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mMotionPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
+ break;
+ }
+ return super.onTouchEvent(ev);
+ }
+
+ /**
+ * 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;
+ }
+ }
+
+ /**
+ * 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;
+ ViewGroup.LayoutParams childLp = child.getLayoutParams();
+
+ if (childLp == null) {
+ childLp = generateDefaultLayoutParams();
+ child.setLayoutParams(childLp);
+ }
+
+ if (childLp.height > 0) {
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(childLp.height,
+ MeasureSpec.EXACTLY);
+ } else {
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ }
+ child.measure(widthMeasureSpec, heightMeasureSpec);
+
+ // Since this view was measured directly against the parent measure
+ // spec, we must measure it again before reuse.
+ child.forceLayout();
+
+ 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;
+ }
+
+ private void setSelectorEnabled(boolean enabled) {
+ if (mSelector != null) {
+ mSelector.setEnabled(enabled);
+ }
+ }
+
+ private static class GateKeeperDrawable extends DrawableWrapper {
+ private boolean mEnabled;
+
+ 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;
+ }
}
/**
@@ -169,6 +490,77 @@ class DropDownListView extends ListViewCompat {
mListSelectionHidden = hideListSelection;
}
+ private void updateSelectorStateCompat() {
+ Drawable selector = getSelector();
+ if (selector != null && touchModeDrawsInPressedStateCompat() && isPressed()) {
+ selector.setState(getDrawableState());
+ }
+ }
+
+ private void drawSelectorCompat(Canvas canvas) {
+ if (!mSelectorRect.isEmpty()) {
+ final Drawable selector = getSelector();
+ if (selector != null) {
+ selector.setBounds(mSelectorRect);
+ selector.draw(canvas);
+ }
+ }
+ }
+
+ private 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);
+ }
+ }
+
+ private 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);
+ }
+ }
+
+ private 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 its 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();
+ }
+ }
+
private void clearPressedItem() {
mDrawsInPressedState = false;
setPressed(false);
@@ -233,44 +625,7 @@ class DropDownListView extends ListViewCompat {
refreshDrawableState();
}
- @Override
- protected boolean touchModeDrawsInPressedStateCompat() {
- return mDrawsInPressedState || super.touchModeDrawsInPressedStateCompat();
- }
-
- @Override
- public boolean isInTouchMode() {
- // WARNING: Please read the comment where mListSelectionHidden is declared
- return (mHijackFocus && mListSelectionHidden) || super.isInTouchMode();
- }
-
- /**
- * <p>Returns the focus state in the drop down.</p>
- *
- * @return true always if hijacking focus
- */
- @Override
- public boolean hasWindowFocus() {
- return mHijackFocus || super.hasWindowFocus();
- }
-
- /**
- * <p>Returns the focus state in the drop down.</p>
- *
- * @return true always if hijacking focus
- */
- @Override
- public boolean isFocused() {
- return mHijackFocus || super.isFocused();
- }
-
- /**
- * <p>Returns the focus state in the drop down.</p>
- *
- * @return true always if hijacking focus
- */
- @Override
- public boolean hasFocus() {
- return mHijackFocus || super.hasFocus();
+ private boolean touchModeDrawsInPressedStateCompat() {
+ return mDrawsInPressedState;
}
}
diff --git a/android/support/v7/widget/LinearLayoutCompat.java b/android/support/v7/widget/LinearLayoutCompat.java
index f071ae4f..ef68896f 100644
--- a/android/support/v7/widget/LinearLayoutCompat.java
+++ b/android/support/v7/widget/LinearLayoutCompat.java
@@ -16,6 +16,7 @@
package android.support.v7.widget;
+import static android.support.annotation.RestrictTo.Scope.LIBRARY;
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.content.Context;
@@ -559,6 +560,7 @@ public class LinearLayoutCompat extends ViewGroup {
* @return true if there should be a divider before the child at childIndex
* @hide Pending API consideration. Currently only used internally by the system.
*/
+ @RestrictTo(LIBRARY)
protected boolean hasDividerBeforeChildAt(int childIndex) {
if (childIndex == 0) {
return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
diff --git a/android/support/v7/widget/ListViewCompat.java b/android/support/v7/widget/ListViewCompat.java
deleted file mode 100644
index 3a2fba3b..00000000
--- a/android/support/v7/widget/ListViewCompat.java
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * 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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v7.graphics.drawable.DrawableWrapper;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-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
- */
-@RestrictTo(LIBRARY_GROUP)
-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;
-
- protected int mMotionPosition;
-
- 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);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mMotionPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
- break;
- }
- return super.onTouchEvent(ev);
- }
-
- 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 its 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;
- ViewGroup.LayoutParams childLp = child.getLayoutParams();
-
- if (childLp == null) {
- childLp = generateDefaultLayoutParams();
- child.setLayoutParams(childLp);
- }
-
- if (childLp.height > 0) {
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(childLp.height,
- MeasureSpec.EXACTLY);
- } else {
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
- }
- child.measure(widthMeasureSpec, heightMeasureSpec);
-
- // Since this view was measured directly against the parent measure
- // spec, we must measure it again before reuse.
- child.forceLayout();
-
- 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;
- }
- }
-}
diff --git a/android/support/v7/widget/RecyclerView.java b/android/support/v7/widget/RecyclerView.java
index a2879796..b195d3c0 100644
--- a/android/support/v7/widget/RecyclerView.java
+++ b/android/support/v7/widget/RecyclerView.java
@@ -2722,7 +2722,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
removeCallbacks(mItemAnimatorRunner);
mViewInfoStore.onDetach();
- if (ALLOW_THREAD_GAP_WORK) {
+ if (ALLOW_THREAD_GAP_WORK && mGapWorker != null) {
// Unregister with gap worker
mGapWorker.remove(this);
mGapWorker = null;
@@ -6643,11 +6643,19 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
* @see #onCreateViewHolder(ViewGroup, int)
*/
public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
- TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
- final VH holder = onCreateViewHolder(parent, viewType);
- holder.mItemViewType = viewType;
- TraceCompat.endSection();
- return holder;
+ try {
+ TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
+ final VH holder = onCreateViewHolder(parent, viewType);
+ if (holder.itemView.getParent() != null) {
+ throw new IllegalStateException("ViewHolder views must not be attached when"
+ + " created. Ensure that you are not passing 'true' to the attachToRoot"
+ + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
+ }
+ holder.mItemViewType = viewType;
+ return holder;
+ } finally {
+ TraceCompat.endSection();
+ }
}
/**
@@ -10108,7 +10116,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
if (vScroll == 0 && hScroll == 0) {
return false;
}
- mRecyclerView.scrollBy(hScroll, vScroll);
+ mRecyclerView.smoothScrollBy(hScroll, vScroll);
return true;
}
diff --git a/android/support/v7/widget/StaggeredGridLayoutManager.java b/android/support/v7/widget/StaggeredGridLayoutManager.java
index 55fb14e8..4e560b45 100644
--- a/android/support/v7/widget/StaggeredGridLayoutManager.java
+++ b/android/support/v7/widget/StaggeredGridLayoutManager.java
@@ -16,6 +16,7 @@
package android.support.v7.widget;
+import static android.support.annotation.RestrictTo.Scope.LIBRARY;
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static android.support.v7.widget.LayoutState.ITEM_DIRECTION_HEAD;
import static android.support.v7.widget.LayoutState.ITEM_DIRECTION_TAIL;
@@ -2069,6 +2070,7 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager imple
/** @hide */
@Override
+ @RestrictTo(LIBRARY)
public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
LayoutPrefetchRegistry layoutPrefetchRegistry) {
/* This method uses the simplifying assumption that the next N items (where N = span count)
diff --git a/android/support/v7/widget/TooltipCompatHandler.java b/android/support/v7/widget/TooltipCompatHandler.java
index 63a61982..8de44e6f 100644
--- a/android/support/v7/widget/TooltipCompatHandler.java
+++ b/android/support/v7/widget/TooltipCompatHandler.java
@@ -22,6 +22,7 @@ import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
import android.content.Context;
import android.support.annotation.RestrictTo;
import android.support.v4.view.ViewCompat;
+import android.support.v4.view.ViewConfigurationCompat;
import android.text.TextUtils;
import android.util.Log;
import android.view.MotionEvent;
@@ -46,6 +47,7 @@ class TooltipCompatHandler implements View.OnLongClickListener, View.OnHoverList
private final View mAnchor;
private final CharSequence mTooltipText;
+ private final int mHoverSlop;
private final Runnable mShowRunnable = new Runnable() {
@Override
@@ -104,6 +106,9 @@ class TooltipCompatHandler implements View.OnLongClickListener, View.OnHoverList
private TooltipCompatHandler(View anchor, CharSequence tooltipText) {
mAnchor = anchor;
mTooltipText = tooltipText;
+ mHoverSlop = ViewConfigurationCompat.getScaledHoverSlop(
+ ViewConfiguration.get(mAnchor.getContext()));
+ clearAnchorPos();
mAnchor.setOnLongClickListener(this);
mAnchor.setOnHoverListener(this);
@@ -129,13 +134,12 @@ class TooltipCompatHandler implements View.OnLongClickListener, View.OnHoverList
}
switch (event.getAction()) {
case MotionEvent.ACTION_HOVER_MOVE:
- if (mAnchor.isEnabled() && mPopup == null) {
- mAnchorX = (int) event.getX();
- mAnchorY = (int) event.getY();
+ if (mAnchor.isEnabled() && mPopup == null && updateAnchorPos(event)) {
setPendingHandler(this);
}
break;
case MotionEvent.ACTION_HOVER_EXIT:
+ clearAnchorPos();
hide();
break;
}
@@ -188,6 +192,7 @@ class TooltipCompatHandler implements View.OnLongClickListener, View.OnHoverList
if (mPopup != null) {
mPopup.hide();
mPopup = null;
+ clearAnchorPos();
mAnchor.removeOnAttachStateChangeListener(this);
} else {
Log.e(TAG, "sActiveHandler.mPopup == null");
@@ -216,4 +221,31 @@ class TooltipCompatHandler implements View.OnLongClickListener, View.OnHoverList
private void cancelPendingShow() {
mAnchor.removeCallbacks(mShowRunnable);
}
+
+ /**
+ * Update the anchor position if it significantly (that is by at least mHoverSlope)
+ * different from the previously stored position. Ignoring insignificant changes
+ * filters out the jitter which is typical for such input sources as stylus.
+ *
+ * @return True if the position has been updated.
+ */
+ private boolean updateAnchorPos(MotionEvent event) {
+ final int newAnchorX = (int) event.getX();
+ final int newAnchorY = (int) event.getY();
+ if (Math.abs(newAnchorX - mAnchorX) <= mHoverSlop
+ && Math.abs(newAnchorY - mAnchorY) <= mHoverSlop) {
+ return false;
+ }
+ mAnchorX = newAnchorX;
+ mAnchorY = newAnchorY;
+ return true;
+ }
+
+ /**
+ * Clear the anchor position to ensure that the next change is considered significant.
+ */
+ private void clearAnchorPos() {
+ mAnchorX = Integer.MAX_VALUE;
+ mAnchorY = Integer.MAX_VALUE;
+ }
}
diff --git a/android/support/wear/widget/BoxInsetLayout.java b/android/support/wear/widget/BoxInsetLayout.java
index a8b13814..383bcb7d 100644
--- a/android/support/wear/widget/BoxInsetLayout.java
+++ b/android/support/wear/widget/BoxInsetLayout.java
@@ -46,7 +46,7 @@ import java.lang.annotation.RetentionPolicy;
@UiThread
public class BoxInsetLayout extends ViewGroup {
- private static final float FACTOR = 0.146467f; //(1 - sqrt(2)/2)/2
+ private static final float FACTOR = 0.146447f; //(1 - sqrt(2)/2)/2
private static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP | Gravity.START;
private final int mScreenHeight;