Merge "Support design - fix FloatingActionButton elevation"
diff --git a/api/current.txt b/api/current.txt
index 02f26d7..4ea0b76 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4266,6 +4266,7 @@
method public void onStop();
method public void onViewCreated(android.view.View, android.os.Bundle);
method public void onViewStateRestored(android.os.Bundle);
+ method public void postponeEnterTransition();
method public void registerForContextMenu(android.view.View);
method public final void requestPermissions(java.lang.String[], int);
method public void setAllowEnterTransitionOverlap(boolean);
@@ -4291,6 +4292,7 @@
method public void startActivityForResult(android.content.Intent, int);
method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+ method public void startPostponedEnterTransition();
method public void unregisterForContextMenu(android.view.View);
}
@@ -4415,8 +4417,10 @@
method public abstract boolean popBackStackImmediate(java.lang.String, int);
method public abstract boolean popBackStackImmediate(int, int);
method public abstract void putFragment(android.os.Bundle, java.lang.String, android.support.v4.app.Fragment);
+ method public abstract void registerFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
method public abstract void removeOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
method public abstract android.support.v4.app.Fragment.SavedState saveFragmentInstanceState(android.support.v4.app.Fragment);
+ method public abstract void unregisterFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks);
field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
}
@@ -4429,6 +4433,23 @@
method public abstract java.lang.String getName();
}
+ public abstract class FragmentManager.FragmentLifecycleCallbacks {
+ ctor public FragmentManager.FragmentLifecycleCallbacks();
+ method public void onFragmentActivityCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+ method public void onFragmentAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
+ method public void onFragmentCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+ method public void onFragmentDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+ method public void onFragmentDetached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+ method public void onFragmentPaused(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+ method public void onFragmentPreAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
+ method public void onFragmentResumed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+ method public void onFragmentSaveInstanceState(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+ method public void onFragmentStarted(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+ method public void onFragmentStopped(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+ method public void onFragmentViewCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.view.View, android.os.Bundle);
+ method public void onFragmentViewDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+ }
+
public static abstract interface FragmentManager.OnBackStackChangedListener {
method public abstract void onBackStackChanged();
}
@@ -4478,6 +4499,7 @@
method public abstract android.support.v4.app.FragmentTransaction remove(android.support.v4.app.Fragment);
method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment);
method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment, java.lang.String);
+ method public abstract android.support.v4.app.FragmentTransaction setAllowOptimization(boolean);
method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(int);
method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(java.lang.CharSequence);
method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(int);
@@ -8762,6 +8784,7 @@
method public void addCallback(android.support.v7.media.MediaRouteSelector, android.support.v7.media.MediaRouter.Callback, int);
method public void addProvider(android.support.v7.media.MediaRouteProvider);
method public void addRemoteControlClient(java.lang.Object);
+ method public android.support.v7.media.MediaRouter.RouteInfo getBluetoothRoute();
method public android.support.v7.media.MediaRouter.RouteInfo getDefaultRoute();
method public static android.support.v7.media.MediaRouter getInstance(android.content.Context);
method public android.support.v4.media.session.MediaSessionCompat.Token getMediaSessionToken();
@@ -8835,8 +8858,10 @@
method public int getVolume();
method public int getVolumeHandling();
method public int getVolumeMax();
+ method public boolean isBluetooth();
method public boolean isConnecting();
method public boolean isDefault();
+ method public boolean isDeviceSpeaker();
method public boolean isEnabled();
method public boolean isSelected();
method public boolean matchesSelector(android.support.v7.media.MediaRouteSelector);
diff --git a/build.gradle b/build.gradle
index 15f52bf..e47de23 100644
--- a/build.gradle
+++ b/build.gradle
@@ -182,95 +182,6 @@
files("${project.rootDir}/../../prebuilts/sdk/$apiLevel/android.jar")
}
-/**
- * Populates the sub-project's set of source sets with the specified modules.
- *
- * @param subProject the sub-project to which the modules belong
- * @param apiModules the modules from which to populate
- */
-void createApiSourceSets(Project subProject, List<ApiModule> apiModules) {
- subProject.ext._apiModules = apiModules
- subProject.ext.allSS = []
- if (gradle.ext.studioCompat.enableApiModules) {
- // nothing to do, they are all modules
- return
- }
- // create a jar task for the api specific internal implementations
- def internalJar = subProject.tasks.create(name: "internalJar", type: Jar) {
- baseName "internal_impl"
- }
- apiModules.each { ApiModule apiModule ->
- apiModule.sourceSet = createApiSourceset(subProject, apiModule.folderName, apiModule.folderName,
- apiModule.apiForSourceSet.toString(), apiModule.prev == null ? null : apiModule.prev.sourceSet)
- subProject.ext.allSS.add(apiModule.sourceSet)
- }
- subProject.android.libraryVariants.all { variant ->
- variant.javaCompile.dependsOn internalJar
- }
-}
-
-/**
- * Adds the specified module to the sub-project's set of source sets and
- * internal JAR. Also sets up dependencies, if supplied.
- *
- * @param subProject the sub-project to which the module belongs
- * @param name the name of the module
- * @param folder the module's source folder
- * @param apiLevel the module's compile API level
- * @param previousSource source set dependency (optional)
- * @return a source set for the module
- */
-SourceSet createApiSourceset(Project subProject, String name, String folder, String apiLevel,
- SourceSet previousSource) {
- def sourceSet = subProject.sourceSets.create(name)
- sourceSet.java.srcDirs = [folder]
-
- // The Android gradle plugin doesn't touch Java sub-tasks, so we need to
- // manually set the Java task's boot classpath to the correct Android SDK.
- def compileJavaTaskName = sourceSet.getCompileJavaTaskName();
- def compileJavaOptions = subProject.tasks."${compileJavaTaskName}".options
- compileJavaOptions.bootClasspath = getAndroidPrebuilt(apiLevel)
-
- // Useful for cleaning up compiler warnings...
- //compileJavaOptions.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
-
- def configName = sourceSet.getCompileConfigurationName()
- subProject.getDependencies().add(configName, getAndroidPrebuilt(apiLevel))
- if (previousSource != null) {
- setupDependencies(subProject, configName, previousSource)
- }
- subProject.ext.allSS.add(sourceSet)
- subProject.tasks.internalJar.from sourceSet.output
- return sourceSet
-}
-
-/**
- * Adds the specified source set as a dependency for the sub-project.
- *
- * @param subProject the sub-project to modify
- * @param configName
- * @param previousSourceSet the source set to add as a dependency
- */
-void setupDependencies(Project subProject, String configName, SourceSet previousSourceSet) {
- subProject.getDependencies().add(configName, previousSourceSet.output)
- subProject.getDependencies().add(configName, previousSourceSet.compileClasspath)
-}
-
-void setApiModuleDependencies(Project subProject, DependencyHandler handler, List extraDeps) {
- if (gradle.ext.studioCompat.enableApiModules) {
- subProject.android.enforceUniquePackageName=false
- // add dependency on the latest module
- handler.compile(project(subProject.ext._apiModules.last().moduleName))
- } else {
- handler.compile(files(subProject.tasks.internalJar.archivePath))
- def firstModule = subProject.ext._apiModules[0]
- extraDeps.each { dep ->
- handler."${firstModule.folderName}Compile"(project(dep))
- handler.compile(project(dep))
- }
- }
-}
-
void registerForDocsTask(Task task, Project subProject, releaseVariant) {
task.dependsOn releaseVariant.javaCompile
task.source {
@@ -281,12 +192,6 @@
}
task.classpath += files(releaseVariant.javaCompile.classpath) +
files(releaseVariant.javaCompile.destinationDir)
-
- if (subProject.hasProperty('allSS')) {
- subProject.allSS.each { ss ->
- task.source ss.java
- }
- }
}
// Generates online docs.
@@ -369,8 +274,6 @@
// current SDK is set in studioCompat.gradle
project.ext.currentSdk = gradle.ext.currentSdk
apply plugin: 'maven'
- project.ext.createApiSourceSets = this.&createApiSourceset
- project.ext.setApiModuleDependencies = this.&setApiModuleDependencies
version = rootProject.ext.supportVersion
group = 'com.android.support'
diff --git a/compat/Android.mk b/compat/Android.mk
index 3e1a2a6..ba5b958 100644
--- a/compat/Android.mk
+++ b/compat/Android.mk
@@ -14,181 +14,40 @@
LOCAL_PATH := $(call my-dir)
-# A helper sub-library that makes direct use of Gingerbread APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-gingerbread
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, gingerbread)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-annotations
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Honeycomb APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-honeycomb
-LOCAL_SDK_VERSION := 11
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-gingerbread
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Honeycomb MR1 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-honeycomb-mr1
-LOCAL_SDK_VERSION := 12
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb_mr1)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-honeycomb
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Honeycomb MR2 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-honeycomb-mr2
-LOCAL_SDK_VERSION := 13
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb_mr2)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-honeycomb-mr1
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Ice Cream Sandwich APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-ics
-LOCAL_SDK_VERSION := 14
-LOCAL_SRC_FILES := $(call all-java-files-under, ics)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-honeycomb-mr2
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Ice Cream Sandwich MR1 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-ics-mr1
-LOCAL_SDK_VERSION := 15
-LOCAL_SRC_FILES := $(call all-java-files-under, ics-mr1)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-ics
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JellyBean APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-jellybean
-LOCAL_SDK_VERSION := 16
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-ics-mr1
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JellyBean MR1 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-jellybean-mr1
-LOCAL_SDK_VERSION := 17
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr1)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-jellybean
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JellyBean MR2 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-jellybean-mr2
-LOCAL_SDK_VERSION := 18
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr2)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-jellybean-mr1
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of KitKat APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-kitkat
-LOCAL_SDK_VERSION := 19
-LOCAL_SRC_FILES := $(call all-java-files-under, kitkat)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-jellybean-mr2
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V20 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-api20
-LOCAL_SDK_VERSION := 20
-LOCAL_SRC_FILES := $(call all-java-files-under, api20)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-kitkat
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Lollipop APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-api20
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V22 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-api22
-LOCAL_SDK_VERSION := 22
-LOCAL_SRC_FILES := $(call all-java-files-under, api22)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-api21
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V23 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-api23
-LOCAL_SDK_VERSION := 23
-LOCAL_SRC_FILES := $(call all-java-files-under, api23)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-api22
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V24 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-api24
-LOCAL_SDK_VERSION := 24
-LOCAL_SRC_FILES := $(call all-java-files-under, api24)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-api23
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
# Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+# LOCAL_STATIC_ANDROID_LIBRARIES := \
+# android-support-compat
+#
+# in their makefiles to include the resources and their dependencies in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-compat
-LOCAL_SDK_VERSION := 9
-LOCAL_AIDL_INCLUDES := frameworks/support/compat/java
-LOCAL_SRC_FILES := $(call all-java-files-under, java) \
- $(call all-Iaidl-files-under, java)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/java
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under,gingerbread) \
+ $(call all-java-files-under,honeycomb) \
+ $(call all-java-files-under,honeycomb_mr1) \
+ $(call all-java-files-under,honeycomb_mr2) \
+ $(call all-java-files-under,ics) \
+ $(call all-java-files-under,ics-mr1) \
+ $(call all-java-files-under,jellybean) \
+ $(call all-java-files-under,jellybean-mr1) \
+ $(call all-java-files-under,jellybean-mr2) \
+ $(call all-java-files-under,kitkat) \
+ $(call all-java-files-under,api20) \
+ $(call all-java-files-under,api21) \
+ $(call all-java-files-under,api22) \
+ $(call all-java-files-under,api23) \
+ $(call all-java-files-under,api24) \
+ $(call all-java-files-under,java) \
+ $(call all-Iaidl-files-under,java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-compat-api24
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-annotations
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/compat/api20/android/support/v4/app/NotificationCompatApi20.java b/compat/api20/android/support/v4/app/NotificationCompatApi20.java
index 88ea703..8f4059b 100644
--- a/compat/api20/android/support/v4/app/NotificationCompatApi20.java
+++ b/compat/api20/android/support/v4/app/NotificationCompatApi20.java
@@ -23,10 +23,14 @@
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.widget.RemoteViews;
import java.util.ArrayList;
+@RequiresApi(20)
+@TargetApi(20)
class NotificationCompatApi20 {
public static class Builder implements NotificationBuilderWithBuilderAccessor,
NotificationBuilderWithActions {
diff --git a/compat/api20/android/support/v4/app/RemoteInputCompatApi20.java b/compat/api20/android/support/v4/app/RemoteInputCompatApi20.java
index 5f302f1..2949cfd 100644
--- a/compat/api20/android/support/v4/app/RemoteInputCompatApi20.java
+++ b/compat/api20/android/support/v4/app/RemoteInputCompatApi20.java
@@ -19,7 +19,11 @@
import android.app.RemoteInput;
import android.content.Intent;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(20)
+@TargetApi(20)
class RemoteInputCompatApi20 {
static RemoteInputCompatBase.RemoteInput[] toCompat(RemoteInput[] srcArray,
RemoteInputCompatBase.RemoteInput.Factory factory) {
diff --git a/compat/api20/android/support/v4/view/WindowInsetsCompatApi20.java b/compat/api20/android/support/v4/view/WindowInsetsCompatApi20.java
index e29f6b0..617920c 100644
--- a/compat/api20/android/support/v4/view/WindowInsetsCompatApi20.java
+++ b/compat/api20/android/support/v4/view/WindowInsetsCompatApi20.java
@@ -16,8 +16,12 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.WindowInsets;
+@RequiresApi(20)
+@TargetApi(20)
class WindowInsetsCompatApi20 {
public static Object consumeSystemWindowInsets(Object insets) {
return ((WindowInsets) insets).consumeSystemWindowInsets();
diff --git a/compat/api21/android/support/v4/app/ActivityCompatApi21.java b/compat/api21/android/support/v4/app/ActivityCompatApi21.java
index 51888d4..248b0aa 100644
--- a/compat/api21/android/support/v4/app/ActivityCompatApi21.java
+++ b/compat/api21/android/support/v4/app/ActivityCompatApi21.java
@@ -23,13 +23,15 @@
import android.graphics.RectF;
import android.media.session.MediaController;
import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
-import java.lang.Override;
-import java.lang.String;
import java.util.List;
import java.util.Map;
+@RequiresApi(21)
+@TargetApi(21)
class ActivityCompatApi21 {
public static void setMediaController(Activity activity, Object mediaControllerObj) {
diff --git a/compat/api21/android/support/v4/app/ActivityOptionsCompat21.java b/compat/api21/android/support/v4/app/ActivityOptionsCompat21.java
index 85673bd..16287d2 100644
--- a/compat/api21/android/support/v4/app/ActivityOptionsCompat21.java
+++ b/compat/api21/android/support/v4/app/ActivityOptionsCompat21.java
@@ -16,14 +16,18 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
import android.util.Pair;
import android.view.View;
+@RequiresApi(21)
+@TargetApi(21)
class ActivityOptionsCompat21 {
private final ActivityOptions mActivityOptions;
diff --git a/compat/api21/android/support/v4/app/NotificationCompatApi21.java b/compat/api21/android/support/v4/app/NotificationCompatApi21.java
index f060486..feeb044 100644
--- a/compat/api21/android/support/v4/app/NotificationCompatApi21.java
+++ b/compat/api21/android/support/v4/app/NotificationCompatApi21.java
@@ -22,10 +22,14 @@
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.widget.RemoteViews;
import java.util.ArrayList;
+@RequiresApi(21)
+@TargetApi(21)
class NotificationCompatApi21 {
public static final String CATEGORY_CALL = Notification.CATEGORY_CALL;
diff --git a/compat/api21/android/support/v4/content/ContextCompatApi21.java b/compat/api21/android/support/v4/content/ContextCompatApi21.java
index cf93924..97a0b37 100644
--- a/compat/api21/android/support/v4/content/ContextCompatApi21.java
+++ b/compat/api21/android/support/v4/content/ContextCompatApi21.java
@@ -18,9 +18,13 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import java.io.File;
+@RequiresApi(21)
+@TargetApi(21)
class ContextCompatApi21 {
public static Drawable getDrawable(Context context, int id) {
return context.getDrawable(id);
diff --git a/compat/api21/android/support/v4/content/res/ResourcesCompatApi21.java b/compat/api21/android/support/v4/content/res/ResourcesCompatApi21.java
index 2272c02..f08dbe1 100644
--- a/compat/api21/android/support/v4/content/res/ResourcesCompatApi21.java
+++ b/compat/api21/android/support/v4/content/res/ResourcesCompatApi21.java
@@ -20,7 +20,11 @@
import android.content.res.Resources.NotFoundException;
import android.content.res.Resources.Theme;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(21)
+@TargetApi(21)
class ResourcesCompatApi21 {
public static Drawable getDrawable(Resources res, int id, Theme theme)
throws NotFoundException {
diff --git a/compat/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java b/compat/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java
index e43ed95..a5e8650 100644
--- a/compat/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java
+++ b/compat/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java
@@ -23,6 +23,8 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.DrawableContainer;
import android.graphics.drawable.InsetDrawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.AttributeSet;
import org.xmlpull.v1.XmlPullParser;
@@ -33,6 +35,9 @@
/**
* Implementation of drawable compatibility that can call L APIs.
*/
+
+@RequiresApi(21)
+@TargetApi(21)
class DrawableCompatLollipop {
public static void setHotspot(Drawable drawable, float x, float y) {
diff --git a/compat/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java b/compat/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java
index ea69677..9458f7b 100644
--- a/compat/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java
+++ b/compat/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java
@@ -28,7 +28,11 @@
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(21)
+@TargetApi(21)
class DrawableWrapperLollipop extends DrawableWrapperKitKat {
DrawableWrapperLollipop(Drawable drawable) {
diff --git a/compat/api21/android/support/v4/view/LayoutInflaterCompatLollipop.java b/compat/api21/android/support/v4/view/LayoutInflaterCompatLollipop.java
index b62c74e..7fae8a8 100644
--- a/compat/api21/android/support/v4/view/LayoutInflaterCompatLollipop.java
+++ b/compat/api21/android/support/v4/view/LayoutInflaterCompatLollipop.java
@@ -17,8 +17,12 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.LayoutInflater;
+@RequiresApi(21)
+@TargetApi(21)
class LayoutInflaterCompatLollipop {
static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
inflater.setFactory2(factory != null
diff --git a/compat/api21/android/support/v4/view/ViewCompatLollipop.java b/compat/api21/android/support/v4/view/ViewCompatLollipop.java
index 478e37e..26c462a 100644
--- a/compat/api21/android/support/v4/view/ViewCompatLollipop.java
+++ b/compat/api21/android/support/v4/view/ViewCompatLollipop.java
@@ -21,10 +21,14 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.ViewParent;
import android.view.WindowInsets;
+@RequiresApi(21)
+@TargetApi(21)
class ViewCompatLollipop {
public interface OnApplyWindowInsetsListenerBridge {
diff --git a/compat/api21/android/support/v4/view/ViewGroupCompatLollipop.java b/compat/api21/android/support/v4/view/ViewGroupCompatLollipop.java
index 1a62404..03430e6 100644
--- a/compat/api21/android/support/v4/view/ViewGroupCompatLollipop.java
+++ b/compat/api21/android/support/v4/view/ViewGroupCompatLollipop.java
@@ -16,8 +16,12 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.ViewGroup;
+@RequiresApi(21)
+@TargetApi(21)
class ViewGroupCompatLollipop {
public static void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
diff --git a/compat/api21/android/support/v4/view/ViewParentCompatLollipop.java b/compat/api21/android/support/v4/view/ViewParentCompatLollipop.java
index 7dbcf61..1e65a09 100644
--- a/compat/api21/android/support/v4/view/ViewParentCompatLollipop.java
+++ b/compat/api21/android/support/v4/view/ViewParentCompatLollipop.java
@@ -17,10 +17,14 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.Log;
import android.view.View;
import android.view.ViewParent;
+@RequiresApi(21)
+@TargetApi(21)
class ViewParentCompatLollipop {
private static final String TAG = "ViewParentCompat";
diff --git a/compat/api21/android/support/v4/view/ViewPropertyAnimatorCompatLollipop.java b/compat/api21/android/support/v4/view/ViewPropertyAnimatorCompatLollipop.java
index 3bfc427..2b979a9 100644
--- a/compat/api21/android/support/v4/view/ViewPropertyAnimatorCompatLollipop.java
+++ b/compat/api21/android/support/v4/view/ViewPropertyAnimatorCompatLollipop.java
@@ -16,8 +16,12 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
+@RequiresApi(21)
+@TargetApi(21)
class ViewPropertyAnimatorCompatLollipop {
public static void translationZ(View view, float value) {
diff --git a/compat/api21/android/support/v4/view/WindowInsetsCompatApi21.java b/compat/api21/android/support/v4/view/WindowInsetsCompatApi21.java
index 3fcbfed..5bbb802 100644
--- a/compat/api21/android/support/v4/view/WindowInsetsCompatApi21.java
+++ b/compat/api21/android/support/v4/view/WindowInsetsCompatApi21.java
@@ -17,8 +17,12 @@
package android.support.v4.view;
import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.WindowInsets;
+@RequiresApi(21)
+@TargetApi(21)
class WindowInsetsCompatApi21 {
public static Object consumeStableInsets(Object insets) {
return ((WindowInsets) insets).consumeStableInsets();
diff --git a/compat/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java b/compat/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java
index 0ac8e9d..e24b873 100644
--- a/compat/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java
+++ b/compat/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java
@@ -16,6 +16,8 @@
package android.support.v4.view.accessibility;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -25,6 +27,9 @@
/**
* Api21-specific AccessibilityNodeInfo API implementation.
*/
+
+@RequiresApi(21)
+@TargetApi(21)
class AccessibilityNodeInfoCompatApi21 {
static List<Object> getActionList(Object info) {
Object result = ((AccessibilityNodeInfo) info).getActionList();
diff --git a/compat/api21/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java b/compat/api21/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java
index c166530..23fd7ca 100644
--- a/compat/api21/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java
+++ b/compat/api21/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java
@@ -17,11 +17,16 @@
package android.support.v4.view.accessibility;
import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.accessibility.AccessibilityWindowInfo;
/**
* Api21-specific AccessibilityWindowInfo API implementation.
*/
+
+@RequiresApi(21)
+@TargetApi(21)
class AccessibilityWindowInfoCompatApi21 {
public static Object obtain() {
diff --git a/compat/api21/android/support/v4/view/animation/PathInterpolatorCompatApi21.java b/compat/api21/android/support/v4/view/animation/PathInterpolatorCompatApi21.java
index 3767f27..835e4e0 100644
--- a/compat/api21/android/support/v4/view/animation/PathInterpolatorCompatApi21.java
+++ b/compat/api21/android/support/v4/view/animation/PathInterpolatorCompatApi21.java
@@ -17,12 +17,17 @@
package android.support.v4.view.animation;
import android.graphics.Path;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
/**
* API 21+ implementation for path interpolator compatibility.
*/
+
+@RequiresApi(21)
+@TargetApi(21)
class PathInterpolatorCompatApi21 {
private PathInterpolatorCompatApi21() {
diff --git a/compat/api21/android/support/v4/widget/CompoundButtonCompatLollipop.java b/compat/api21/android/support/v4/widget/CompoundButtonCompatLollipop.java
index 17f4fdb..42aa89a 100644
--- a/compat/api21/android/support/v4/widget/CompoundButtonCompatLollipop.java
+++ b/compat/api21/android/support/v4/widget/CompoundButtonCompatLollipop.java
@@ -18,8 +18,12 @@
import android.content.res.ColorStateList;
import android.graphics.PorterDuff;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.widget.CompoundButton;
+@RequiresApi(21)
+@TargetApi(21)
class CompoundButtonCompatLollipop {
static void setButtonTintList(CompoundButton button, ColorStateList tint) {
diff --git a/compat/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java b/compat/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java
index 6ba379c..f12bc23 100644
--- a/compat/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java
+++ b/compat/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java
@@ -17,8 +17,12 @@
package android.support.v4.widget;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.widget.EdgeEffect;
+@RequiresApi(21)
+@TargetApi(21)
class EdgeEffectCompatLollipop {
public static boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
((EdgeEffect) edgeEffect).onPull(deltaDistance, displacement);
diff --git a/compat/api21/android/support/v4/widget/PopupWindowCompatApi21.java b/compat/api21/android/support/v4/widget/PopupWindowCompatApi21.java
index 3440f3c..393efa6 100644
--- a/compat/api21/android/support/v4/widget/PopupWindowCompatApi21.java
+++ b/compat/api21/android/support/v4/widget/PopupWindowCompatApi21.java
@@ -16,11 +16,15 @@
package android.support.v4.widget;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.Log;
import android.widget.PopupWindow;
import java.lang.reflect.Field;
+@RequiresApi(21)
+@TargetApi(21)
class PopupWindowCompatApi21 {
private static final String TAG = "PopupWindowCompatApi21";
diff --git a/compat/api22/android/support/v4/app/ActivityCompatApi22.java b/compat/api22/android/support/v4/app/ActivityCompatApi22.java
index c56d490..1efef64 100644
--- a/compat/api22/android/support/v4/app/ActivityCompatApi22.java
+++ b/compat/api22/android/support/v4/app/ActivityCompatApi22.java
@@ -18,7 +18,11 @@
import android.app.Activity;
import android.net.Uri;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(22)
+@TargetApi(22)
class ActivityCompatApi22 {
public static Uri getReferrer(Activity activity) {
return activity.getReferrer();
diff --git a/compat/api22/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi22.java b/compat/api22/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi22.java
index 786318d..dd482d4 100644
--- a/compat/api22/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi22.java
+++ b/compat/api22/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi22.java
@@ -16,12 +16,17 @@
package android.support.v4.view.accessibility;
-import android.view.accessibility.AccessibilityNodeInfo;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
/**
* Api22-specific AccessibilityNodeInfo API implementation.
*/
+
+@RequiresApi(22)
+@TargetApi(22)
class AccessibilityNodeInfoCompatApi22 {
public static Object getTraversalBefore(Object info) {
diff --git a/compat/api23/android/support/v4/app/ActivityCompatApi23.java b/compat/api23/android/support/v4/app/ActivityCompatApi23.java
index ab46058..9012f56 100644
--- a/compat/api23/android/support/v4/app/ActivityCompatApi23.java
+++ b/compat/api23/android/support/v4/app/ActivityCompatApi23.java
@@ -22,11 +22,15 @@
import android.graphics.Matrix;
import android.graphics.RectF;
import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import java.util.List;
import java.util.Map;
+@RequiresApi(23)
+@TargetApi(23)
class ActivityCompatApi23 {
public interface OnSharedElementsReadyListenerBridge {
void onSharedElementsReady();
diff --git a/compat/api23/android/support/v4/app/ActivityOptionsCompat23.java b/compat/api23/android/support/v4/app/ActivityOptionsCompat23.java
index b33fb11..81be941 100644
--- a/compat/api23/android/support/v4/app/ActivityOptionsCompat23.java
+++ b/compat/api23/android/support/v4/app/ActivityOptionsCompat23.java
@@ -22,9 +22,13 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.Pair;
import android.view.View;
+@RequiresApi(23)
+@TargetApi(23)
class ActivityOptionsCompat23 {
private final ActivityOptions mActivityOptions;
diff --git a/compat/api23/android/support/v4/app/AppOpsManagerCompat23.java b/compat/api23/android/support/v4/app/AppOpsManagerCompat23.java
index 72e07bf..853fd5d 100644
--- a/compat/api23/android/support/v4/app/AppOpsManagerCompat23.java
+++ b/compat/api23/android/support/v4/app/AppOpsManagerCompat23.java
@@ -18,10 +18,15 @@
import android.app.AppOpsManager;
import android.content.Context;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
/**
* AppOpsManager implementations for API 23.
*/
+
+@RequiresApi(23)
+@TargetApi(23)
class AppOpsManagerCompat23 {
public static String permissionToOp(String permission) {
return AppOpsManager.permissionToOp(permission);
diff --git a/compat/api23/android/support/v4/app/NotificationCompatApi23.java b/compat/api23/android/support/v4/app/NotificationCompatApi23.java
index 61173d1..5262ef3 100644
--- a/compat/api23/android/support/v4/app/NotificationCompatApi23.java
+++ b/compat/api23/android/support/v4/app/NotificationCompatApi23.java
@@ -17,7 +17,11 @@
package android.support.v4.app;
import android.app.Notification;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(23)
+@TargetApi(23)
class NotificationCompatApi23 {
public static final String CATEGORY_REMINDER = Notification.CATEGORY_REMINDER;
diff --git a/compat/api23/android/support/v4/content/ContextCompatApi23.java b/compat/api23/android/support/v4/content/ContextCompatApi23.java
index 64f1c15..c22f5b6 100644
--- a/compat/api23/android/support/v4/content/ContextCompatApi23.java
+++ b/compat/api23/android/support/v4/content/ContextCompatApi23.java
@@ -18,10 +18,11 @@
import android.content.Context;
import android.content.res.ColorStateList;
-import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
-import java.io.File;
-
+@RequiresApi(23)
+@TargetApi(23)
class ContextCompatApi23 {
public static ColorStateList getColorStateList(Context context, int id) {
return context.getColorStateList(id);
diff --git a/compat/api23/android/support/v4/content/res/ResourcesCompatApi23.java b/compat/api23/android/support/v4/content/res/ResourcesCompatApi23.java
index c44f9ce..eade1ef 100644
--- a/compat/api23/android/support/v4/content/res/ResourcesCompatApi23.java
+++ b/compat/api23/android/support/v4/content/res/ResourcesCompatApi23.java
@@ -20,7 +20,11 @@
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.content.res.Resources.Theme;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(23)
+@TargetApi(23)
class ResourcesCompatApi23 {
public static int getColor(Resources res, int id, Theme theme) throws NotFoundException {
return res.getColor(id, theme);
diff --git a/compat/api23/android/support/v4/graphics/drawable/DrawableCompatApi23.java b/compat/api23/android/support/v4/graphics/drawable/DrawableCompatApi23.java
index 2a41b60..e454d41 100644
--- a/compat/api23/android/support/v4/graphics/drawable/DrawableCompatApi23.java
+++ b/compat/api23/android/support/v4/graphics/drawable/DrawableCompatApi23.java
@@ -17,10 +17,15 @@
package android.support.v4.graphics.drawable;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
/**
* Implementation of drawable compatibility that can call M APIs.
*/
+
+@RequiresApi(23)
+@TargetApi(23)
class DrawableCompatApi23 {
public static boolean setLayoutDirection(Drawable drawable, int layoutDirection) {
return drawable.setLayoutDirection(layoutDirection);
diff --git a/compat/api23/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java b/compat/api23/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java
index 801b8ea..7143850 100644
--- a/compat/api23/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java
+++ b/compat/api23/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java
@@ -16,9 +16,11 @@
package android.support.v4.hardware.fingerprint;
+import android.annotation.TargetApi;
import android.content.Context;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Handler;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import java.security.Signature;
@@ -32,6 +34,8 @@
* Actual FingerprintManagerCompat implementation for API level 23 and later.
* @hide
*/
+@RequiresApi(23)
+@TargetApi(23)
@RestrictTo(GROUP_ID)
public final class FingerprintManagerCompatApi23 {
diff --git a/compat/api23/android/support/v4/text/ICUCompatApi23.java b/compat/api23/android/support/v4/text/ICUCompatApi23.java
index 1a3c54f..182c6f3 100644
--- a/compat/api23/android/support/v4/text/ICUCompatApi23.java
+++ b/compat/api23/android/support/v4/text/ICUCompatApi23.java
@@ -16,12 +16,16 @@
package android.support.v4.text;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Locale;
+@RequiresApi(23)
+@TargetApi(23)
class ICUCompatApi23 {
private static final String TAG = "ICUCompatIcs";
diff --git a/compat/api23/android/support/v4/view/ViewCompatMarshmallow.java b/compat/api23/android/support/v4/view/ViewCompatMarshmallow.java
index 38fa479..30645ec 100644
--- a/compat/api23/android/support/v4/view/ViewCompatMarshmallow.java
+++ b/compat/api23/android/support/v4/view/ViewCompatMarshmallow.java
@@ -16,8 +16,12 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
+@RequiresApi(23)
+@TargetApi(23)
class ViewCompatMarshmallow {
public static void setScrollIndicators(View view, int indicators) {
view.setScrollIndicators(indicators);
diff --git a/compat/api23/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi23.java b/compat/api23/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi23.java
index e6175c9..457cd39 100644
--- a/compat/api23/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi23.java
+++ b/compat/api23/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi23.java
@@ -16,8 +16,12 @@
package android.support.v4.view.accessibility;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.accessibility.AccessibilityNodeInfo;
+@RequiresApi(23)
+@TargetApi(23)
class AccessibilityNodeInfoCompatApi23 {
public static Object getActionScrollToPosition() {
return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_TO_POSITION;
diff --git a/compat/api23/android/support/v4/widget/CompoundButtonCompatApi23.java b/compat/api23/android/support/v4/widget/CompoundButtonCompatApi23.java
index 0c55bbc..6dddbdb 100644
--- a/compat/api23/android/support/v4/widget/CompoundButtonCompatApi23.java
+++ b/compat/api23/android/support/v4/widget/CompoundButtonCompatApi23.java
@@ -16,12 +16,13 @@
package android.support.v4.widget;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
-import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.widget.CompoundButton;
+@RequiresApi(23)
+@TargetApi(23)
class CompoundButtonCompatApi23 {
static Drawable getButtonDrawable(CompoundButton button) {
diff --git a/compat/api23/android/support/v4/widget/PopupWindowCompatApi23.java b/compat/api23/android/support/v4/widget/PopupWindowCompatApi23.java
index 96bf8d9..1483e41 100644
--- a/compat/api23/android/support/v4/widget/PopupWindowCompatApi23.java
+++ b/compat/api23/android/support/v4/widget/PopupWindowCompatApi23.java
@@ -16,8 +16,12 @@
package android.support.v4.widget;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.widget.PopupWindow;
+@RequiresApi(23)
+@TargetApi(23)
class PopupWindowCompatApi23 {
static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
diff --git a/compat/api23/android/support/v4/widget/TextViewCompatApi23.java b/compat/api23/android/support/v4/widget/TextViewCompatApi23.java
index ad21409..f31242b 100644
--- a/compat/api23/android/support/v4/widget/TextViewCompatApi23.java
+++ b/compat/api23/android/support/v4/widget/TextViewCompatApi23.java
@@ -16,11 +16,14 @@
package android.support.v4.widget;
-import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.support.annotation.StyleRes;
import android.widget.TextView;
+@RequiresApi(23)
+@TargetApi(23)
class TextViewCompatApi23 {
public static void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) {
textView.setTextAppearance(resId);
diff --git a/compat/api24/android/support/v4/app/ActivityOptionsCompat24.java b/compat/api24/android/support/v4/app/ActivityOptionsCompat24.java
index 4d166e4..d33d8a2 100644
--- a/compat/api24/android/support/v4/app/ActivityOptionsCompat24.java
+++ b/compat/api24/android/support/v4/app/ActivityOptionsCompat24.java
@@ -16,6 +16,7 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityOptions;
import android.app.PendingIntent;
@@ -24,9 +25,12 @@
import android.graphics.Rect;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.util.Pair;
import android.view.View;
+@RequiresApi(24)
+@TargetApi(24)
class ActivityOptionsCompat24 {
public static ActivityOptionsCompat24 makeCustomAnimation(Context context,
diff --git a/compat/api24/android/support/v4/app/NotificationCompatApi24.java b/compat/api24/android/support/v4/app/NotificationCompatApi24.java
index a631f23..6a29d89 100644
--- a/compat/api24/android/support/v4/app/NotificationCompatApi24.java
+++ b/compat/api24/android/support/v4/app/NotificationCompatApi24.java
@@ -23,11 +23,15 @@
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.widget.RemoteViews;
import java.util.ArrayList;
import java.util.List;
+@RequiresApi(24)
+@TargetApi(24)
class NotificationCompatApi24 {
public static final String CATEGORY_CALL = Notification.CATEGORY_CALL;
diff --git a/compat/api24/android/support/v4/app/NotificationManagerCompatApi24.java b/compat/api24/android/support/v4/app/NotificationManagerCompatApi24.java
index b8f599f..468592f 100644
--- a/compat/api24/android/support/v4/app/NotificationManagerCompatApi24.java
+++ b/compat/api24/android/support/v4/app/NotificationManagerCompatApi24.java
@@ -16,7 +16,11 @@
package android.support.v4.app;
import android.app.NotificationManager;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(24)
+@TargetApi(24)
class NotificationManagerCompatApi24 {
public static boolean areNotificationsEnabled(NotificationManager notificationManager) {
return notificationManager.areNotificationsEnabled();
diff --git a/compat/api24/android/support/v4/app/ServiceCompatApi24.java b/compat/api24/android/support/v4/app/ServiceCompatApi24.java
index ad5296d..29b6112 100644
--- a/compat/api24/android/support/v4/app/ServiceCompatApi24.java
+++ b/compat/api24/android/support/v4/app/ServiceCompatApi24.java
@@ -16,7 +16,11 @@
package android.support.v4.app;
import android.app.Service;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(24)
+@TargetApi(24)
class ServiceCompatApi24 {
public static void stopForeground(Service service, int flags) {
service.stopForeground(flags);
diff --git a/compat/api24/android/support/v4/content/ContextCompatApi24.java b/compat/api24/android/support/v4/content/ContextCompatApi24.java
index e94d702..a65f21b 100644
--- a/compat/api24/android/support/v4/content/ContextCompatApi24.java
+++ b/compat/api24/android/support/v4/content/ContextCompatApi24.java
@@ -17,9 +17,13 @@
package android.support.v4.content;
import android.content.Context;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import java.io.File;
+@RequiresApi(24)
+@TargetApi(24)
class ContextCompatApi24 {
public static File getDataDir(Context context) {
return context.getDataDir();
diff --git a/compat/api24/android/support/v4/net/ConnectivityManagerCompatApi24.java b/compat/api24/android/support/v4/net/ConnectivityManagerCompatApi24.java
index 5e0edf9..b6e86cb 100644
--- a/compat/api24/android/support/v4/net/ConnectivityManagerCompatApi24.java
+++ b/compat/api24/android/support/v4/net/ConnectivityManagerCompatApi24.java
@@ -16,11 +16,15 @@
package android.support.v4.net;
+import android.annotation.TargetApi;
import android.net.ConnectivityManager;
+import android.support.annotation.RequiresApi;
/**
* Implementation of ConnectivityManagerCompat that can use API 24 APIs.
*/
+@RequiresApi(24)
+@TargetApi(24)
class ConnectivityManagerCompatApi24 {
public static int getRestrictBackgroundStatus(ConnectivityManager cm) {
return cm.getRestrictBackgroundStatus();
diff --git a/compat/api24/android/support/v4/net/TrafficStatsCompatApi24.java b/compat/api24/android/support/v4/net/TrafficStatsCompatApi24.java
index 02a9699..b2ebec4 100644
--- a/compat/api24/android/support/v4/net/TrafficStatsCompatApi24.java
+++ b/compat/api24/android/support/v4/net/TrafficStatsCompatApi24.java
@@ -16,7 +16,9 @@
package android.support.v4.net;
+import android.annotation.TargetApi;
import android.net.TrafficStats;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import java.net.DatagramSocket;
@@ -24,7 +26,9 @@
import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-/** {@hide} */
+/** @hide */
+@RequiresApi(24)
+@TargetApi(24)
@RestrictTo(GROUP_ID)
public class TrafficStatsCompatApi24 {
public static void tagDatagramSocket(DatagramSocket socket) throws SocketException {
diff --git a/compat/api24/android/support/v4/os/UserManagerCompatApi24.java b/compat/api24/android/support/v4/os/UserManagerCompatApi24.java
index e5f7a7b..0d96ae0 100644
--- a/compat/api24/android/support/v4/os/UserManagerCompatApi24.java
+++ b/compat/api24/android/support/v4/os/UserManagerCompatApi24.java
@@ -16,13 +16,17 @@
package android.support.v4.os;
+import android.annotation.TargetApi;
import android.content.Context;
import android.os.UserManager;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-/** {@hide} */
+/** @hide */
+@RequiresApi(24)
+@TargetApi(24)
@RestrictTo(GROUP_ID)
public class UserManagerCompatApi24 {
public static boolean isUserUnlocked(Context context) {
diff --git a/compat/api24/android/support/v4/view/PointerIconCompatApi24.java b/compat/api24/android/support/v4/view/PointerIconCompatApi24.java
index d8c7ff0..424af92 100644
--- a/compat/api24/android/support/v4/view/PointerIconCompatApi24.java
+++ b/compat/api24/android/support/v4/view/PointerIconCompatApi24.java
@@ -19,8 +19,12 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.PointerIcon;
+@RequiresApi(24)
+@TargetApi(24)
class PointerIconCompatApi24 {
public static Object getSystemIcon(Context context, int style) {
return PointerIcon.getSystemIcon(context, style);
diff --git a/compat/api24/android/support/v4/view/ViewCompatApi24.java b/compat/api24/android/support/v4/view/ViewCompatApi24.java
index 517f3cc..71366a8 100644
--- a/compat/api24/android/support/v4/view/ViewCompatApi24.java
+++ b/compat/api24/android/support/v4/view/ViewCompatApi24.java
@@ -16,9 +16,13 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.PointerIcon;
import android.view.View;
+@RequiresApi(24)
+@TargetApi(24)
class ViewCompatApi24 {
public static void setPointerIcon(View view, Object pointerIcon) {
view.setPointerIcon((PointerIcon)pointerIcon);
diff --git a/compat/api24/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi24.java b/compat/api24/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi24.java
index 14fe273..5e64091 100644
--- a/compat/api24/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi24.java
+++ b/compat/api24/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi24.java
@@ -16,11 +16,16 @@
package android.support.v4.view.accessibility;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.accessibility.AccessibilityNodeInfo;
/**
* Api24-specific AccessibilityNodeInfo API implementation.
*/
+
+@RequiresApi(24)
+@TargetApi(24)
class AccessibilityNodeInfoCompatApi24 {
public static Object getActionSetProgress() {
return AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS;
diff --git a/compat/api24/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi24.java b/compat/api24/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi24.java
index 34ba542..c8aa21a 100644
--- a/compat/api24/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi24.java
+++ b/compat/api24/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi24.java
@@ -16,11 +16,16 @@
package android.support.v4.view.accessibility;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.accessibility.AccessibilityWindowInfo;
/**
* Api24-specific AccessibilityWindowInfo API implementation.
*/
+
+@RequiresApi(24)
+@TargetApi(24)
class AccessibilityWindowInfoCompatApi24 {
public static CharSequence getTitle(Object info) {
return ((AccessibilityWindowInfo) info).getTitle();
diff --git a/compat/build.gradle b/compat/build.gradle
index cd9e693..2f43b54 100644
--- a/compat/build.gradle
+++ b/compat/build.gradle
@@ -1,9 +1,8 @@
apply plugin: 'com.android.library'
archivesBaseName = 'support-compat'
-
-createApiSourceSets(project, gradle.ext.studioCompat.modules.compat.apiTargets)
dependencies {
+ compile project(':support-annotations')
androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
exclude module: 'support-annotations'
}
@@ -18,22 +17,35 @@
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
-setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.compat.dependencies)
android {
- compileSdkVersion 9
+ compileSdkVersion project.ext.currentSdk
defaultConfig {
minSdkVersion 9
- // TODO: get target from branch
- //targetSdkVersion 19
-
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = ['java']
+ main.java.srcDirs = [
+ 'gingerbread',
+ 'honeycomb',
+ 'honeycomb_mr1',
+ 'honeycomb_mr2',
+ 'ics',
+ 'ics-mr1',
+ 'jellybean',
+ 'jellybean-mr1',
+ 'jellybean-mr2',
+ 'kitkat',
+ 'api20',
+ 'api21',
+ 'api22',
+ 'api23',
+ 'api24',
+ 'java'
+ ]
main.aidl.srcDirs = ['java']
androidTest.setRoot('tests')
@@ -89,11 +101,6 @@
exclude('android/service/media/**')
}
- project.ext.allSS.each { ss ->
- javadocTask.source ss.java
- sourcesJarTask.from ss.java.srcDirs
- }
-
artifacts.add('archives', javadocJarTask);
artifacts.add('archives', sourcesJarTask);
}
diff --git a/compat/gingerbread/android/support/v4/animation/GingerbreadAnimatorCompatProvider.java b/compat/gingerbread/android/support/v4/animation/GingerbreadAnimatorCompatProvider.java
index 83e2d58..83ba12a 100644
--- a/compat/gingerbread/android/support/v4/animation/GingerbreadAnimatorCompatProvider.java
+++ b/compat/gingerbread/android/support/v4/animation/GingerbreadAnimatorCompatProvider.java
@@ -16,6 +16,8 @@
package android.support.v4.animation;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import java.util.ArrayList;
@@ -26,6 +28,9 @@
* <p>
* This is not a fully implemented API which is why it is not public.
*/
+
+@RequiresApi(9)
+@TargetApi(9)
class GingerbreadAnimatorCompatProvider implements AnimatorProvider {
@Override
diff --git a/compat/gingerbread/android/support/v4/app/BundleCompatGingerbread.java b/compat/gingerbread/android/support/v4/app/BundleCompatGingerbread.java
index b232baa..f7656be 100644
--- a/compat/gingerbread/android/support/v4/app/BundleCompatGingerbread.java
+++ b/compat/gingerbread/android/support/v4/app/BundleCompatGingerbread.java
@@ -18,11 +18,15 @@
import android.os.Bundle;
import android.os.IBinder;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+@RequiresApi(9)
+@TargetApi(9)
class BundleCompatGingerbread {
private static final String TAG = "BundleCompatGingerbread";
diff --git a/compat/gingerbread/android/support/v4/app/NotificationCompatBase.java b/compat/gingerbread/android/support/v4/app/NotificationCompatBase.java
index e4d0f2b..5f1d8fb 100644
--- a/compat/gingerbread/android/support/v4/app/NotificationCompatBase.java
+++ b/compat/gingerbread/android/support/v4/app/NotificationCompatBase.java
@@ -16,19 +16,27 @@
package android.support.v4.app;
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
+import android.annotation.TargetApi;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
/**
* @hide
*/
@RestrictTo(GROUP_ID)
+@RequiresApi(9)
+@TargetApi(9)
public class NotificationCompatBase {
+ private static Method sSetLatestEventInfo;
public static abstract class Action {
public abstract int getIcon();
@@ -66,7 +74,26 @@
public static Notification add(Notification notification, Context context,
CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent,
PendingIntent fullScreenIntent) {
- notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
+ if (sSetLatestEventInfo == null) {
+ try {
+ sSetLatestEventInfo = Notification.class.getMethod("setLatestEventInfo",
+ Context.class, CharSequence.class, CharSequence.class, PendingIntent.class);
+ } catch (NoSuchMethodException e) {
+ // This method was @removed, so it must exist on later
+ // versions even if it's not in public API.
+ throw new RuntimeException(e);
+ }
+ }
+
+ try {
+ sSetLatestEventInfo.invoke(notification, context,
+ contentTitle, contentText, contentIntent);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ // This method was @removed, so it must be invokable on later
+ // versions even if it's not in public API.
+ throw new RuntimeException(e);
+ }
+
notification.fullScreenIntent = fullScreenIntent;
return notification;
}
diff --git a/compat/gingerbread/android/support/v4/app/RemoteInputCompatBase.java b/compat/gingerbread/android/support/v4/app/RemoteInputCompatBase.java
index 2449336..85117dd 100644
--- a/compat/gingerbread/android/support/v4/app/RemoteInputCompatBase.java
+++ b/compat/gingerbread/android/support/v4/app/RemoteInputCompatBase.java
@@ -17,7 +17,11 @@
package android.support.v4.app;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(9)
+@TargetApi(9)
class RemoteInputCompatBase {
public static abstract class RemoteInput {
diff --git a/compat/gingerbread/android/support/v4/content/res/ConfigurationHelperGingerbread.java b/compat/gingerbread/android/support/v4/content/res/ConfigurationHelperGingerbread.java
index 1a18404..6667431 100644
--- a/compat/gingerbread/android/support/v4/content/res/ConfigurationHelperGingerbread.java
+++ b/compat/gingerbread/android/support/v4/content/res/ConfigurationHelperGingerbread.java
@@ -16,11 +16,14 @@
package android.support.v4.content.res;
-import android.content.Context;
import android.content.res.Resources;
import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.DisplayMetrics;
+@RequiresApi(9)
+@TargetApi(9)
class ConfigurationHelperGingerbread {
static int getScreenHeightDp(@NonNull Resources resources) {
diff --git a/compat/gingerbread/android/support/v4/graphics/drawable/DrawableCompatBase.java b/compat/gingerbread/android/support/v4/graphics/drawable/DrawableCompatBase.java
index 9645df2..8e5cd9f 100644
--- a/compat/gingerbread/android/support/v4/graphics/drawable/DrawableCompatBase.java
+++ b/compat/gingerbread/android/support/v4/graphics/drawable/DrawableCompatBase.java
@@ -20,7 +20,10 @@
import android.content.res.Resources;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.AttributeSet;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -29,6 +32,9 @@
/**
* Base implementation of drawable compatibility.
*/
+
+@RequiresApi(9)
+@TargetApi(9)
class DrawableCompatBase {
public static void setTint(Drawable drawable, int tint) {
diff --git a/compat/gingerbread/android/support/v4/graphics/drawable/DrawableWrapperGingerbread.java b/compat/gingerbread/android/support/v4/graphics/drawable/DrawableWrapperGingerbread.java
index ccaebf7..646c677 100644
--- a/compat/gingerbread/android/support/v4/graphics/drawable/DrawableWrapperGingerbread.java
+++ b/compat/gingerbread/android/support/v4/graphics/drawable/DrawableWrapperGingerbread.java
@@ -26,13 +26,18 @@
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
/**
- * Drawable which delegates all calls to it's wrapped {@link android.graphics.drawable.Drawable}.
- * <p>
+ * Drawable which delegates all calls to it's wrapped {@link Drawable}.
+ * <p/>
* Also allows backward compatible tinting via a color or {@link ColorStateList}.
* This functionality is accessed via static methods in {@code DrawableCompat}.
*/
+
+@RequiresApi(9)
+@TargetApi(9)
class DrawableWrapperGingerbread extends Drawable
implements Drawable.Callback, DrawableWrapper, TintAwareDrawable {
diff --git a/compat/gingerbread/android/support/v4/view/LayoutInflaterCompatBase.java b/compat/gingerbread/android/support/v4/view/LayoutInflaterCompatBase.java
index c85235c..5d97d04 100644
--- a/compat/gingerbread/android/support/v4/view/LayoutInflaterCompatBase.java
+++ b/compat/gingerbread/android/support/v4/view/LayoutInflaterCompatBase.java
@@ -17,10 +17,14 @@
package android.support.v4.view;
import android.content.Context;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
+@RequiresApi(9)
+@TargetApi(9)
class LayoutInflaterCompatBase {
static class FactoryWrapper implements LayoutInflater.Factory {
diff --git a/compat/gingerbread/android/support/v4/view/ViewCompatBase.java b/compat/gingerbread/android/support/v4/view/ViewCompatBase.java
index e7882bd..7c81f5e 100644
--- a/compat/gingerbread/android/support/v4/view/ViewCompatBase.java
+++ b/compat/gingerbread/android/support/v4/view/ViewCompatBase.java
@@ -16,9 +16,11 @@
package android.support.v4.view;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.PorterDuff;
+import android.support.annotation.RequiresApi;
import android.view.Display;
import android.view.View;
import android.view.ViewParent;
@@ -26,6 +28,8 @@
import java.lang.reflect.Field;
+@RequiresApi(9)
+@TargetApi(9)
class ViewCompatBase {
private static final String TAG = "ViewCompatBase";
diff --git a/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorCompatBase.java b/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorCompatBase.java
index 9857632..5f3e253 100644
--- a/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorCompatBase.java
+++ b/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorCompatBase.java
@@ -17,11 +17,16 @@
package android.support.v4.view.animation;
import android.graphics.Path;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.animation.Interpolator;
/**
* Base implementation for path interpolator compatibility.
*/
+
+@RequiresApi(9)
+@TargetApi(9)
class PathInterpolatorCompatBase {
private PathInterpolatorCompatBase() {
diff --git a/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorGingerbread.java b/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorGingerbread.java
index b95fc04..4c96b97 100644
--- a/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorGingerbread.java
+++ b/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorGingerbread.java
@@ -18,11 +18,16 @@
import android.graphics.Path;
import android.graphics.PathMeasure;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.animation.Interpolator;
/**
* A path interpolator implementation compatible with API 9+.
*/
+
+@RequiresApi(9)
+@TargetApi(9)
class PathInterpolatorGingerbread implements Interpolator {
/**
diff --git a/compat/gingerbread/android/support/v4/widget/CompoundButtonCompatGingerbread.java b/compat/gingerbread/android/support/v4/widget/CompoundButtonCompatGingerbread.java
index 6e3e0e4..0fe01f0 100644
--- a/compat/gingerbread/android/support/v4/widget/CompoundButtonCompatGingerbread.java
+++ b/compat/gingerbread/android/support/v4/widget/CompoundButtonCompatGingerbread.java
@@ -19,11 +19,15 @@
import android.content.res.ColorStateList;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.Log;
import android.widget.CompoundButton;
import java.lang.reflect.Field;
+@RequiresApi(9)
+@TargetApi(9)
class CompoundButtonCompatGingerbread {
private static final String TAG = "CompoundButtonCompatGingerbread";
diff --git a/compat/gingerbread/android/support/v4/widget/ListViewCompatGingerbread.java b/compat/gingerbread/android/support/v4/widget/ListViewCompatGingerbread.java
index 14d27b0..79edf2c 100644
--- a/compat/gingerbread/android/support/v4/widget/ListViewCompatGingerbread.java
+++ b/compat/gingerbread/android/support/v4/widget/ListViewCompatGingerbread.java
@@ -16,10 +16,13 @@
package android.support.v4.widget;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
-import android.widget.AbsListView;
import android.widget.ListView;
+@RequiresApi(9)
+@TargetApi(9)
class ListViewCompatGingerbread {
static void scrollListBy(final ListView listView, int y) {
final int firstPosition = listView.getFirstVisiblePosition();
diff --git a/compat/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java b/compat/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java
index 615bc44..e5f3bbf 100644
--- a/compat/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java
@@ -17,6 +17,8 @@
package android.support.v4.app;
import android.app.Activity;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -24,6 +26,9 @@
/**
* Implementation of activity compatibility that can call Honeycomb APIs.
*/
+
+@RequiresApi(11)
+@TargetApi(11)
class ActivityCompatHoneycomb {
static void invalidateOptionsMenu(Activity activity) {
activity.invalidateOptionsMenu();
diff --git a/compat/honeycomb/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java b/compat/honeycomb/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java
index 49495b2..2d364e1 100644
--- a/compat/honeycomb/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java
+++ b/compat/honeycomb/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java
@@ -16,7 +16,9 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
import android.app.Notification;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
@@ -28,6 +30,8 @@
*
* @hide
*/
+@RequiresApi(11)
+@TargetApi(11)
@RestrictTo(GROUP_ID)
public interface NotificationBuilderWithBuilderAccessor {
public Notification.Builder getBuilder();
diff --git a/compat/honeycomb/android/support/v4/app/NotificationCompatHoneycomb.java b/compat/honeycomb/android/support/v4/app/NotificationCompatHoneycomb.java
index 3048e91..44ef21a 100644
--- a/compat/honeycomb/android/support/v4/app/NotificationCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/app/NotificationCompatHoneycomb.java
@@ -20,8 +20,12 @@
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Bitmap;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.widget.RemoteViews;
+@RequiresApi(11)
+@TargetApi(11)
class NotificationCompatHoneycomb {
static Notification add(Context context, Notification n,
CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
diff --git a/compat/honeycomb/android/support/v4/content/ContextCompatHoneycomb.java b/compat/honeycomb/android/support/v4/content/ContextCompatHoneycomb.java
index 9abc97d..6b804ad 100644
--- a/compat/honeycomb/android/support/v4/content/ContextCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/content/ContextCompatHoneycomb.java
@@ -18,12 +18,17 @@
import android.content.Context;
import android.content.Intent;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import java.io.File;
/**
* Implementation of context compatibility that can call Honeycomb APIs.
*/
+
+@RequiresApi(11)
+@TargetApi(11)
class ContextCompatHoneycomb {
static void startActivities(Context context, Intent[] intents) {
diff --git a/compat/honeycomb/android/support/v4/content/ExecutorCompatHoneycomb.java b/compat/honeycomb/android/support/v4/content/ExecutorCompatHoneycomb.java
index f0a9b66..0dcd0d7 100644
--- a/compat/honeycomb/android/support/v4/content/ExecutorCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/content/ExecutorCompatHoneycomb.java
@@ -17,12 +17,17 @@
package android.support.v4.content;
import android.os.AsyncTask;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import java.util.concurrent.Executor;
/**
* Implementation of parallel executor compatibility that can call Honeycomb APIs.
*/
+
+@RequiresApi(11)
+@TargetApi(11)
class ExecutorCompatHoneycomb {
public static Executor getParallelExecutor() {
return AsyncTask.THREAD_POOL_EXECUTOR;
diff --git a/compat/honeycomb/android/support/v4/content/IntentCompatHoneycomb.java b/compat/honeycomb/android/support/v4/content/IntentCompatHoneycomb.java
index 6356898..81ada48 100644
--- a/compat/honeycomb/android/support/v4/content/IntentCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/content/IntentCompatHoneycomb.java
@@ -18,7 +18,11 @@
import android.content.ComponentName;
import android.content.Intent;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(11)
+@TargetApi(11)
class IntentCompatHoneycomb {
public static Intent makeMainActivity(ComponentName mainActivity) {
return Intent.makeMainActivity(mainActivity);
diff --git a/compat/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java b/compat/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java
index 08066a4..e19f8a8 100644
--- a/compat/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java
@@ -16,13 +16,16 @@
package android.support.v4.graphics.drawable;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
/**
* Implementation of drawable compatibility that can call Honeycomb APIs.
*/
+
+@RequiresApi(11)
+@TargetApi(11)
class DrawableCompatHoneycomb {
public static void jumpToCurrentState(Drawable drawable) {
diff --git a/compat/honeycomb/android/support/v4/graphics/drawable/DrawableWrapperHoneycomb.java b/compat/honeycomb/android/support/v4/graphics/drawable/DrawableWrapperHoneycomb.java
index 4c494dd..1bd6355 100644
--- a/compat/honeycomb/android/support/v4/graphics/drawable/DrawableWrapperHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/graphics/drawable/DrawableWrapperHoneycomb.java
@@ -20,7 +20,11 @@
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(11)
+@TargetApi(11)
class DrawableWrapperHoneycomb extends DrawableWrapperGingerbread {
DrawableWrapperHoneycomb(Drawable drawable) {
diff --git a/compat/honeycomb/android/support/v4/os/AsyncTaskCompatHoneycomb.java b/compat/honeycomb/android/support/v4/os/AsyncTaskCompatHoneycomb.java
index 5b05a0e..1b3836e 100644
--- a/compat/honeycomb/android/support/v4/os/AsyncTaskCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/os/AsyncTaskCompatHoneycomb.java
@@ -17,10 +17,15 @@
package android.support.v4.os;
import android.os.AsyncTask;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
/**
* Implementation of AsyncTask compatibility that can call Honeycomb APIs.
*/
+
+@RequiresApi(11)
+@TargetApi(11)
class AsyncTaskCompatHoneycomb {
static <Params, Progress, Result> void executeParallel(
diff --git a/compat/honeycomb/android/support/v4/view/KeyEventCompatHoneycomb.java b/compat/honeycomb/android/support/v4/view/KeyEventCompatHoneycomb.java
index 6cd185b..80425d8 100644
--- a/compat/honeycomb/android/support/v4/view/KeyEventCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/view/KeyEventCompatHoneycomb.java
@@ -16,11 +16,16 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.KeyEvent;
/**
* Implementation of key event compatibility that can call Honeycomb APIs.
*/
+
+@RequiresApi(11)
+@TargetApi(11)
class KeyEventCompatHoneycomb {
public static int normalizeMetaState(int metaState) {
return KeyEvent.normalizeMetaState(metaState);
diff --git a/compat/honeycomb/android/support/v4/view/LayoutInflaterCompatHC.java b/compat/honeycomb/android/support/v4/view/LayoutInflaterCompatHC.java
index 06e72f4..7eea934 100644
--- a/compat/honeycomb/android/support/v4/view/LayoutInflaterCompatHC.java
+++ b/compat/honeycomb/android/support/v4/view/LayoutInflaterCompatHC.java
@@ -17,14 +17,17 @@
package android.support.v4.view;
import android.content.Context;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import java.lang.reflect.Field;
-import java.util.ArrayList;
+@RequiresApi(11)
+@TargetApi(11)
class LayoutInflaterCompatHC {
private static final String TAG = "LayoutInflaterCompatHC";
diff --git a/compat/honeycomb/android/support/v4/view/MenuItemCompatHoneycomb.java b/compat/honeycomb/android/support/v4/view/MenuItemCompatHoneycomb.java
index 1a0e513..0b267d2 100644
--- a/compat/honeycomb/android/support/v4/view/MenuItemCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/view/MenuItemCompatHoneycomb.java
@@ -16,12 +16,17 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.MenuItem;
import android.view.View;
/**
* Implementation of menu compatibility that can call Honeycomb APIs.
*/
+
+@RequiresApi(11)
+@TargetApi(11)
class MenuItemCompatHoneycomb {
public static void setShowAsAction(MenuItem item, int actionEnum) {
item.setShowAsAction(actionEnum);
diff --git a/compat/honeycomb/android/support/v4/view/VelocityTrackerCompatHoneycomb.java b/compat/honeycomb/android/support/v4/view/VelocityTrackerCompatHoneycomb.java
index 4f9d326..189dc03 100644
--- a/compat/honeycomb/android/support/v4/view/VelocityTrackerCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/view/VelocityTrackerCompatHoneycomb.java
@@ -16,11 +16,16 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.VelocityTracker;
/**
* Implementation of velocity tracker compatibility that can call Honeycomb APIs.
*/
+
+@RequiresApi(11)
+@TargetApi(11)
class VelocityTrackerCompatHoneycomb {
public static float getXVelocity(VelocityTracker tracker, int pointerId) {
return tracker.getXVelocity(pointerId);
diff --git a/compat/honeycomb/android/support/v4/view/ViewCompatHC.java b/compat/honeycomb/android/support/v4/view/ViewCompatHC.java
index 5d0467c..607175b 100644
--- a/compat/honeycomb/android/support/v4/view/ViewCompatHC.java
+++ b/compat/honeycomb/android/support/v4/view/ViewCompatHC.java
@@ -19,9 +19,13 @@
import android.animation.ValueAnimator;
import android.graphics.Matrix;
import android.graphics.Paint;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.ViewParent;
+@RequiresApi(11)
+@TargetApi(11)
class ViewCompatHC {
static long getFrameTime() {
return ValueAnimator.getFrameDelay();
diff --git a/compat/honeycomb/android/support/v4/view/ViewGroupCompatHC.java b/compat/honeycomb/android/support/v4/view/ViewGroupCompatHC.java
index 96349ed..3b31adf 100644
--- a/compat/honeycomb/android/support/v4/view/ViewGroupCompatHC.java
+++ b/compat/honeycomb/android/support/v4/view/ViewGroupCompatHC.java
@@ -17,8 +17,12 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.ViewGroup;
+@RequiresApi(11)
+@TargetApi(11)
class ViewGroupCompatHC {
private ViewGroupCompatHC() {
}
diff --git a/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java b/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java
index 9143214..01867d8 100644
--- a/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java
@@ -19,6 +19,8 @@
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.widget.SearchView;
import android.widget.SearchView.OnCloseListener;
@@ -27,6 +29,9 @@
/**
* Implementation of SearchView compatibility that can call Honeycomb APIs.
*/
+
+@RequiresApi(11)
+@TargetApi(11)
class SearchViewCompatHoneycomb {
public static void checkIfLegalArg(View searchView) {
diff --git a/compat/honeycomb_mr1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java b/compat/honeycomb_mr1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java
index 5133a8a..9aaae0b 100644
--- a/compat/honeycomb_mr1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java
+++ b/compat/honeycomb_mr1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java
@@ -17,17 +17,20 @@
package android.support.v4.animation;
import android.animation.Animator;
-import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
-import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
/**
* Uses framework Animators to provide ValueAnimatorCompat interface.
- * <p>
+ * <p/>
* This is not a fully implemented API which is why it is not public.
*/
+
+@RequiresApi(12)
+@TargetApi(12)
class HoneycombMr1AnimatorCompatProvider implements AnimatorProvider {
private TimeInterpolator mDefaultInterpolator;
diff --git a/compat/honeycomb_mr1/android/support/v4/graphics/BitmapCompatHoneycombMr1.java b/compat/honeycomb_mr1/android/support/v4/graphics/BitmapCompatHoneycombMr1.java
index 94358d5..4266460 100644
--- a/compat/honeycomb_mr1/android/support/v4/graphics/BitmapCompatHoneycombMr1.java
+++ b/compat/honeycomb_mr1/android/support/v4/graphics/BitmapCompatHoneycombMr1.java
@@ -16,10 +16,15 @@
package android.support.v4.graphics;
import android.graphics.Bitmap;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
/**
* Implementation of BitmapCompat that can use Honeycomb MR1 APIs.
*/
+
+@RequiresApi(12)
+@TargetApi(12)
class BitmapCompatHoneycombMr1 {
static int getAllocationByteCount(Bitmap bitmap) {
diff --git a/compat/honeycomb_mr1/android/support/v4/view/MotionEventCompatHoneycombMr1.java b/compat/honeycomb_mr1/android/support/v4/view/MotionEventCompatHoneycombMr1.java
index 406fcf3..f14e77d 100644
--- a/compat/honeycomb_mr1/android/support/v4/view/MotionEventCompatHoneycombMr1.java
+++ b/compat/honeycomb_mr1/android/support/v4/view/MotionEventCompatHoneycombMr1.java
@@ -16,11 +16,16 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.MotionEvent;
/**
* Motion event compatibility class for API 12+.
*/
+
+@RequiresApi(12)
+@TargetApi(12)
class MotionEventCompatHoneycombMr1 {
static float getAxisValue(MotionEvent event, int axis) {
return event.getAxisValue(axis);
diff --git a/compat/honeycomb_mr2/android/support/v4/content/res/ConfigurationHelperHoneycombMr2.java b/compat/honeycomb_mr2/android/support/v4/content/res/ConfigurationHelperHoneycombMr2.java
index 62eba95..aa3aaef 100644
--- a/compat/honeycomb_mr2/android/support/v4/content/res/ConfigurationHelperHoneycombMr2.java
+++ b/compat/honeycomb_mr2/android/support/v4/content/res/ConfigurationHelperHoneycombMr2.java
@@ -16,10 +16,13 @@
package android.support.v4.content.res;
-import android.content.Context;
import android.content.res.Resources;
import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(13)
+@TargetApi(13)
class ConfigurationHelperHoneycombMr2 {
static int getScreenHeightDp(@NonNull Resources resources) {
diff --git a/compat/honeycomb_mr2/android/support/v4/net/ConnectivityManagerCompatHoneycombMR2.java b/compat/honeycomb_mr2/android/support/v4/net/ConnectivityManagerCompatHoneycombMR2.java
index 619c1af..a631941 100644
--- a/compat/honeycomb_mr2/android/support/v4/net/ConnectivityManagerCompatHoneycombMR2.java
+++ b/compat/honeycomb_mr2/android/support/v4/net/ConnectivityManagerCompatHoneycombMR2.java
@@ -16,6 +16,11 @@
package android.support.v4.net;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+
import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
@@ -26,12 +31,12 @@
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIMAX;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-
/**
* Implementation of ConnectivityManagerCompat that can use Honeycomb MR2 APIs.
*/
+
+@RequiresApi(13)
+@TargetApi(13)
class ConnectivityManagerCompatHoneycombMR2 {
public static boolean isActiveNetworkMetered(ConnectivityManager cm) {
final NetworkInfo info = cm.getActiveNetworkInfo();
diff --git a/compat/honeycomb_mr2/android/support/v4/os/ParcelableCompatHoneycombMR2.java b/compat/honeycomb_mr2/android/support/v4/os/ParcelableCompatHoneycombMR2.java
index 08acb55..fe754c4 100644
--- a/compat/honeycomb_mr2/android/support/v4/os/ParcelableCompatHoneycombMR2.java
+++ b/compat/honeycomb_mr2/android/support/v4/os/ParcelableCompatHoneycombMR2.java
@@ -18,13 +18,19 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(13)
+@TargetApi(13)
class ParcelableCompatCreatorHoneycombMR2Stub {
static <T> Parcelable.Creator<T> instantiate(ParcelableCompatCreatorCallbacks<T> callbacks) {
return new ParcelableCompatCreatorHoneycombMR2<T>(callbacks);
}
}
+@RequiresApi(13)
+@TargetApi(13)
class ParcelableCompatCreatorHoneycombMR2<T> implements Parcelable.ClassLoaderCreator<T> {
private final ParcelableCompatCreatorCallbacks<T> mCallbacks;
diff --git a/compat/ics-mr1/android/support/v4/content/IntentCompatIcsMr1.java b/compat/ics-mr1/android/support/v4/content/IntentCompatIcsMr1.java
index 0fa2c43..be17cd6 100644
--- a/compat/ics-mr1/android/support/v4/content/IntentCompatIcsMr1.java
+++ b/compat/ics-mr1/android/support/v4/content/IntentCompatIcsMr1.java
@@ -17,7 +17,11 @@
package android.support.v4.content;
import android.content.Intent;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(15)
+@TargetApi(15)
class IntentCompatIcsMr1 {
public static Intent makeMainSelectorActivity(String selectorAction, String selectorCategory) {
diff --git a/compat/ics-mr1/android/support/v4/content/res/ResourcesCompatIcsMr1.java b/compat/ics-mr1/android/support/v4/content/res/ResourcesCompatIcsMr1.java
index 8e14256..be229b5 100644
--- a/compat/ics-mr1/android/support/v4/content/res/ResourcesCompatIcsMr1.java
+++ b/compat/ics-mr1/android/support/v4/content/res/ResourcesCompatIcsMr1.java
@@ -18,9 +18,12 @@
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
-import android.content.res.Resources.Theme;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(15)
+@TargetApi(15)
class ResourcesCompatIcsMr1 {
public static Drawable getDrawableForDensity(Resources res, int id, int density)
throws NotFoundException {
diff --git a/compat/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java b/compat/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java
index 780345c..3cf4e5e 100644
--- a/compat/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java
+++ b/compat/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java
@@ -16,15 +16,16 @@
package android.support.v4.view;
-import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
/**
* Helper for accessing newer features in View introduced in ICS Mr1.
*/
+
+@RequiresApi(15)
+@TargetApi(15)
class ViewCompatICSMr1 {
public static boolean hasOnClickListeners(View v) {
return v.hasOnClickListeners();
diff --git a/compat/ics-mr1/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java b/compat/ics-mr1/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java
index 94164d7..f249bdd 100644
--- a/compat/ics-mr1/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java
+++ b/compat/ics-mr1/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java
@@ -16,11 +16,16 @@
package android.support.v4.view.accessibility;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.accessibility.AccessibilityRecord;
/**
* ICS MR1 specific AccessibilityRecord API implementation.
*/
+
+@RequiresApi(15)
+@TargetApi(15)
class AccessibilityRecordCompatIcsMr1 {
public static int getMaxScrollX(Object record) {
diff --git a/compat/ics/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java b/compat/ics/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java
index ffbea0a..21e797d 100644
--- a/compat/ics/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java
+++ b/compat/ics/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java
@@ -19,10 +19,15 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.pm.ResolveInfo;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
/**
* ICS implementation of the new APIs in AccessibilityServiceInfo.
*/
+
+@RequiresApi(14)
+@TargetApi(14)
class AccessibilityServiceInfoCompatIcs {
public static boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) {
diff --git a/compat/ics/android/support/v4/app/NotificationCompatIceCreamSandwich.java b/compat/ics/android/support/v4/app/NotificationCompatIceCreamSandwich.java
index 0842451..d2e0e44 100644
--- a/compat/ics/android/support/v4/app/NotificationCompatIceCreamSandwich.java
+++ b/compat/ics/android/support/v4/app/NotificationCompatIceCreamSandwich.java
@@ -20,8 +20,12 @@
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Bitmap;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.widget.RemoteViews;
+@RequiresApi(14)
+@TargetApi(14)
class NotificationCompatIceCreamSandwich {
public static class Builder implements NotificationBuilderWithBuilderAccessor {
diff --git a/compat/ics/android/support/v4/app/NotificationManagerCompatIceCreamSandwich.java b/compat/ics/android/support/v4/app/NotificationManagerCompatIceCreamSandwich.java
index f088179..4fcf2b1 100644
--- a/compat/ics/android/support/v4/app/NotificationManagerCompatIceCreamSandwich.java
+++ b/compat/ics/android/support/v4/app/NotificationManagerCompatIceCreamSandwich.java
@@ -17,7 +17,11 @@
package android.support.v4.app;
import android.app.Service;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(14)
+@TargetApi(14)
class NotificationManagerCompatIceCreamSandwich {
static final int SIDE_CHANNEL_BIND_FLAGS = Service.BIND_AUTO_CREATE
| Service.BIND_WAIVE_PRIORITY;
diff --git a/compat/ics/android/support/v4/app/ShareCompatICS.java b/compat/ics/android/support/v4/app/ShareCompatICS.java
index 0f2ff35..a6d1e92 100644
--- a/compat/ics/android/support/v4/app/ShareCompatICS.java
+++ b/compat/ics/android/support/v4/app/ShareCompatICS.java
@@ -18,10 +18,14 @@
import android.app.Activity;
import android.content.Intent;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.ActionProvider;
import android.view.MenuItem;
import android.widget.ShareActionProvider;
+@RequiresApi(14)
+@TargetApi(14)
class ShareCompatICS {
private static final String HISTORY_FILENAME_PREFIX = ".sharecompat_";
diff --git a/compat/ics/android/support/v4/net/TrafficStatsCompatIcs.java b/compat/ics/android/support/v4/net/TrafficStatsCompatIcs.java
index e3f3fab..724c34e 100644
--- a/compat/ics/android/support/v4/net/TrafficStatsCompatIcs.java
+++ b/compat/ics/android/support/v4/net/TrafficStatsCompatIcs.java
@@ -18,6 +18,8 @@
import android.net.TrafficStats;
import android.os.ParcelFileDescriptor;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import java.net.DatagramSocket;
import java.net.Socket;
@@ -26,6 +28,9 @@
/**
* Implementation of TrafficStatsCompat that can call ICS APIs.
*/
+
+@RequiresApi(14)
+@TargetApi(14)
class TrafficStatsCompatIcs {
public static void clearThreadStatsTag() {
TrafficStats.clearThreadStatsTag();
diff --git a/compat/ics/android/support/v4/text/ICUCompatIcs.java b/compat/ics/android/support/v4/text/ICUCompatIcs.java
index dfb9e7e..4baafd6 100644
--- a/compat/ics/android/support/v4/text/ICUCompatIcs.java
+++ b/compat/ics/android/support/v4/text/ICUCompatIcs.java
@@ -16,12 +16,16 @@
package android.support.v4.text;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Locale;
+@RequiresApi(14)
+@TargetApi(14)
class ICUCompatIcs {
private static final String TAG = "ICUCompatIcs";
diff --git a/compat/ics/android/support/v4/view/AccessibilityDelegateCompatIcs.java b/compat/ics/android/support/v4/view/AccessibilityDelegateCompatIcs.java
index 4adeb0c..fee33d7 100644
--- a/compat/ics/android/support/v4/view/AccessibilityDelegateCompatIcs.java
+++ b/compat/ics/android/support/v4/view/AccessibilityDelegateCompatIcs.java
@@ -16,6 +16,8 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
@@ -25,6 +27,9 @@
/**
* ICS specific AccessibilityDelegate API implementation.
*/
+
+@RequiresApi(14)
+@TargetApi(14)
class AccessibilityDelegateCompatIcs {
public interface AccessibilityDelegateBridge {
diff --git a/compat/ics/android/support/v4/view/MenuItemCompatIcs.java b/compat/ics/android/support/v4/view/MenuItemCompatIcs.java
index 8e65079..4dbea9a 100644
--- a/compat/ics/android/support/v4/view/MenuItemCompatIcs.java
+++ b/compat/ics/android/support/v4/view/MenuItemCompatIcs.java
@@ -17,8 +17,12 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.MenuItem;
+@RequiresApi(14)
+@TargetApi(14)
class MenuItemCompatIcs {
public static boolean expandActionView(MenuItem item) {
return item.expandActionView();
diff --git a/compat/ics/android/support/v4/view/MotionEventCompatICS.java b/compat/ics/android/support/v4/view/MotionEventCompatICS.java
index e7979de..e8f9d49 100644
--- a/compat/ics/android/support/v4/view/MotionEventCompatICS.java
+++ b/compat/ics/android/support/v4/view/MotionEventCompatICS.java
@@ -16,8 +16,12 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.MotionEvent;
+@RequiresApi(14)
+@TargetApi(14)
class MotionEventCompatICS {
public static int getButtonState(MotionEvent event) {
return event.getButtonState();
diff --git a/compat/ics/android/support/v4/view/ViewCompatICS.java b/compat/ics/android/support/v4/view/ViewCompatICS.java
index 742c47c..338b009 100644
--- a/compat/ics/android/support/v4/view/ViewCompatICS.java
+++ b/compat/ics/android/support/v4/view/ViewCompatICS.java
@@ -17,6 +17,8 @@
package android.support.v4.view;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.View.AccessibilityDelegate;
import android.view.accessibility.AccessibilityEvent;
@@ -25,6 +27,9 @@
/**
* Helper for accessing newer features in View introduced in ICS.
*/
+
+@RequiresApi(14)
+@TargetApi(14)
class ViewCompatICS {
public static boolean canScrollHorizontally(View v, int direction) {
diff --git a/compat/ics/android/support/v4/view/ViewConfigurationCompatICS.java b/compat/ics/android/support/v4/view/ViewConfigurationCompatICS.java
index 13a9647..19a7174 100644
--- a/compat/ics/android/support/v4/view/ViewConfigurationCompatICS.java
+++ b/compat/ics/android/support/v4/view/ViewConfigurationCompatICS.java
@@ -16,11 +16,16 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.ViewConfiguration;
/**
* Implementation of menu compatibility that can call ICS APIs.
*/
+
+@RequiresApi(14)
+@TargetApi(14)
class ViewConfigurationCompatICS {
static boolean hasPermanentMenuKey(ViewConfiguration config) {
return config.hasPermanentMenuKey();
diff --git a/compat/ics/android/support/v4/view/ViewGroupCompatIcs.java b/compat/ics/android/support/v4/view/ViewGroupCompatIcs.java
index 73de780..bb03e7d 100644
--- a/compat/ics/android/support/v4/view/ViewGroupCompatIcs.java
+++ b/compat/ics/android/support/v4/view/ViewGroupCompatIcs.java
@@ -16,6 +16,8 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
@@ -23,6 +25,9 @@
/**
* ICS specific ViewGroup API implementation.
*/
+
+@RequiresApi(14)
+@TargetApi(14)
class ViewGroupCompatIcs {
public static boolean onRequestSendAccessibilityEvent(ViewGroup group, View child,
AccessibilityEvent event) {
diff --git a/compat/ics/android/support/v4/view/ViewParentCompatICS.java b/compat/ics/android/support/v4/view/ViewParentCompatICS.java
index f9fc5a5..693aa40 100644
--- a/compat/ics/android/support/v4/view/ViewParentCompatICS.java
+++ b/compat/ics/android/support/v4/view/ViewParentCompatICS.java
@@ -16,6 +16,8 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
@@ -23,6 +25,9 @@
/**
* ICS-specific ViewParent API implementation.
*/
+
+@RequiresApi(14)
+@TargetApi(14)
class ViewParentCompatICS {
public static boolean requestSendAccessibilityEvent(
ViewParent parent, View child, AccessibilityEvent event) {
diff --git a/compat/ics/android/support/v4/view/ViewPropertyAnimatorCompatICS.java b/compat/ics/android/support/v4/view/ViewPropertyAnimatorCompatICS.java
index 1fd7f3e..9cc5583 100644
--- a/compat/ics/android/support/v4/view/ViewPropertyAnimatorCompatICS.java
+++ b/compat/ics/android/support/v4/view/ViewPropertyAnimatorCompatICS.java
@@ -17,9 +17,13 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.animation.Interpolator;
+@RequiresApi(14)
+@TargetApi(14)
class ViewPropertyAnimatorCompatICS {
public static void setDuration(View view, long value) {
diff --git a/compat/ics/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java b/compat/ics/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java
index 632c4d1..0d5196f 100644
--- a/compat/ics/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java
+++ b/compat/ics/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java
@@ -16,12 +16,17 @@
package android.support.v4.view.accessibility;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityRecord;
/**
* ICS specific AccessibilityEvent API implementation.
*/
+
+@RequiresApi(14)
+@TargetApi(14)
class AccessibilityEventCompatIcs {
public static int getRecordCount(AccessibilityEvent event) {
diff --git a/compat/ics/android/support/v4/view/accessibility/AccessibilityManagerCompatIcs.java b/compat/ics/android/support/v4/view/accessibility/AccessibilityManagerCompatIcs.java
index 3b5be26..4af6aa3 100644
--- a/compat/ics/android/support/v4/view/accessibility/AccessibilityManagerCompatIcs.java
+++ b/compat/ics/android/support/v4/view/accessibility/AccessibilityManagerCompatIcs.java
@@ -17,6 +17,8 @@
package android.support.v4.view.accessibility;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
@@ -25,6 +27,9 @@
/**
* ICS specific AccessibilityManager API implementation.
*/
+
+@RequiresApi(14)
+@TargetApi(14)
class AccessibilityManagerCompatIcs {
public static class AccessibilityStateChangeListenerWrapper
diff --git a/compat/ics/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java b/compat/ics/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java
index 17f9aa8..51faa89 100644
--- a/compat/ics/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java
+++ b/compat/ics/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java
@@ -16,7 +16,9 @@
package android.support.v4.view.accessibility;
+import android.annotation.TargetApi;
import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -25,6 +27,9 @@
/**
* ICS specific AccessibilityNodeInfo API implementation.
*/
+
+@RequiresApi(14)
+@TargetApi(14)
class AccessibilityNodeInfoCompatIcs {
public static Object obtain() {
return AccessibilityNodeInfo.obtain();
diff --git a/compat/ics/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java b/compat/ics/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java
index 0ebcc6a..f6e078f 100644
--- a/compat/ics/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java
+++ b/compat/ics/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java
@@ -17,6 +17,8 @@
package android.support.v4.view.accessibility;
import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.accessibility.AccessibilityRecord;
@@ -25,6 +27,9 @@
/**
* ICS specific AccessibilityRecord API implementation.
*/
+
+@RequiresApi(14)
+@TargetApi(14)
class AccessibilityRecordCompatIcs {
public static Object obtain() {
diff --git a/compat/ics/android/support/v4/widget/EdgeEffectCompatIcs.java b/compat/ics/android/support/v4/widget/EdgeEffectCompatIcs.java
index c02eeb4..1f75b4a 100644
--- a/compat/ics/android/support/v4/widget/EdgeEffectCompatIcs.java
+++ b/compat/ics/android/support/v4/widget/EdgeEffectCompatIcs.java
@@ -17,14 +17,19 @@
import android.content.Context;
import android.graphics.Canvas;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.widget.EdgeEffect;
/**
* Stub implementation that contains a real EdgeEffect on ICS.
- *
+ * <p/>
* This class is an implementation detail for EdgeEffectCompat
* and should not be used directly.
*/
+
+@RequiresApi(14)
+@TargetApi(14)
class EdgeEffectCompatIcs {
public static Object newEdgeEffect(Context context) {
return new EdgeEffect(context);
diff --git a/compat/ics/android/support/v4/widget/ScrollerCompatIcs.java b/compat/ics/android/support/v4/widget/ScrollerCompatIcs.java
index 76b9a2b..be7a07e 100644
--- a/compat/ics/android/support/v4/widget/ScrollerCompatIcs.java
+++ b/compat/ics/android/support/v4/widget/ScrollerCompatIcs.java
@@ -16,11 +16,16 @@
package android.support.v4.widget;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.widget.OverScroller;
/**
* ICS API access for ScrollerCompat
*/
+
+@RequiresApi(14)
+@TargetApi(14)
class ScrollerCompatIcs {
public static float getCurrVelocity(Object scroller) {
return ((OverScroller) scroller).getCurrVelocity();
diff --git a/compat/ics/android/support/v4/widget/SearchViewCompatIcs.java b/compat/ics/android/support/v4/widget/SearchViewCompatIcs.java
index b8d719c..3938081 100644
--- a/compat/ics/android/support/v4/widget/SearchViewCompatIcs.java
+++ b/compat/ics/android/support/v4/widget/SearchViewCompatIcs.java
@@ -17,12 +17,17 @@
package android.support.v4.widget;
import android.content.Context;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.widget.SearchView;
/**
* Implementation of SearchView compatibility that can call ICS APIs.
*/
+
+@RequiresApi(14)
+@TargetApi(14)
class SearchViewCompatIcs {
public static class MySearchView extends SearchView {
diff --git a/compat/java/android/support/v4/app/ActivityOptionsCompat.java b/compat/java/android/support/v4/app/ActivityOptionsCompat.java
index 68fc441..57ca1a4 100644
--- a/compat/java/android/support/v4/app/ActivityOptionsCompat.java
+++ b/compat/java/android/support/v4/app/ActivityOptionsCompat.java
@@ -16,6 +16,7 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
@@ -24,6 +25,7 @@
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.support.v4.util.Pair;
import android.view.View;
@@ -306,6 +308,8 @@
return new ActivityOptionsCompat();
}
+ @RequiresApi(16)
+ @TargetApi(16)
private static class ActivityOptionsImplJB extends ActivityOptionsCompat {
private final ActivityOptionsCompatJB mImpl;
@@ -327,6 +331,8 @@
}
}
+ @RequiresApi(21)
+ @TargetApi(21)
private static class ActivityOptionsImpl21 extends ActivityOptionsCompat {
private final ActivityOptionsCompat21 mImpl;
@@ -349,6 +355,8 @@
}
}
+ @RequiresApi(23)
+ @TargetApi(23)
private static class ActivityOptionsImpl23 extends ActivityOptionsCompat {
private final ActivityOptionsCompat23 mImpl;
@@ -376,6 +384,8 @@
}
}
+ @RequiresApi(24)
+ @TargetApi(24)
private static class ActivityOptionsImpl24 extends ActivityOptionsCompat {
private final ActivityOptionsCompat24 mImpl;
diff --git a/compat/java/android/support/v4/view/MenuItemCompat.java b/compat/java/android/support/v4/view/MenuItemCompat.java
index 1467754..7b57e26 100644
--- a/compat/java/android/support/v4/view/MenuItemCompat.java
+++ b/compat/java/android/support/v4/view/MenuItemCompat.java
@@ -16,6 +16,7 @@
package android.support.v4.view;
+import android.os.Build;
import android.support.v4.internal.view.SupportMenuItem;
import android.util.Log;
import android.view.MenuItem;
@@ -244,10 +245,9 @@
*/
static final MenuVersionImpl IMPL;
static {
- final int version = android.os.Build.VERSION.SDK_INT;
- if (version >= 14) {
+ if (Build.VERSION.SDK_INT >= 14) {
IMPL = new IcsMenuVersionImpl();
- } else if (version >= 11) {
+ } else if (Build.VERSION.SDK_INT >= 11) {
IMPL = new HoneycombMenuVersionImpl();
} else {
IMPL = new BaseMenuVersionImpl();
diff --git a/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java b/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java
index faa0939..656fae9 100644
--- a/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java
+++ b/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java
@@ -23,6 +23,11 @@
import java.lang.reflect.Field;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+
+@RequiresApi(9)
+@TargetApi(9)
class TextViewCompatGingerbread {
private static final String LOG_TAG = "TextViewCompatGingerbread";
diff --git a/compat/jellybean-mr1/android/support/v4/content/res/ConfigurationHelperJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/content/res/ConfigurationHelperJellybeanMr1.java
index b16db18..eecc581 100644
--- a/compat/jellybean-mr1/android/support/v4/content/res/ConfigurationHelperJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/content/res/ConfigurationHelperJellybeanMr1.java
@@ -16,10 +16,13 @@
package android.support.v4.content.res;
-import android.content.Context;
import android.content.res.Resources;
import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(17)
+@TargetApi(17)
class ConfigurationHelperJellybeanMr1 {
static int getDensityDpi(@NonNull Resources resources) {
return resources.getConfiguration().densityDpi;
diff --git a/compat/jellybean-mr1/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java
index 09891c9..d8da519 100644
--- a/compat/jellybean-mr1/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java
@@ -17,16 +17,18 @@
package android.support.v4.graphics.drawable;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.Log;
-import android.widget.CompoundButton;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Implementation of drawable compatibility that can call Jellybean MR1 APIs.
*/
+
+@RequiresApi(17)
+@TargetApi(17)
class DrawableCompatJellybeanMr1 {
private static final String TAG = "DrawableCompatJellybeanMr1";
diff --git a/compat/jellybean-mr1/android/support/v4/hardware/display/DisplayManagerJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/hardware/display/DisplayManagerJellybeanMr1.java
index b44a2e8..9cb5637 100644
--- a/compat/jellybean-mr1/android/support/v4/hardware/display/DisplayManagerJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/hardware/display/DisplayManagerJellybeanMr1.java
@@ -16,9 +16,13 @@
package android.support.v4.hardware.display;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.support.annotation.RequiresApi;
import android.view.Display;
+@RequiresApi(17)
+@TargetApi(17)
final class DisplayManagerJellybeanMr1 {
public static Object getDisplayManager(Context context) {
return context.getSystemService(Context.DISPLAY_SERVICE);
diff --git a/compat/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java
index d5b675b..ad354e7 100644
--- a/compat/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java
@@ -18,6 +18,8 @@
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.text.TextUtils;
import java.util.Locale;
@@ -25,6 +27,9 @@
/**
* Jellybean MR1 - specific TextUtils API access.
*/
+
+@RequiresApi(17)
+@TargetApi(17)
class TextUtilsCompatJellybeanMr1 {
@NonNull
public static String htmlEncode(@NonNull String s) {
diff --git a/compat/jellybean-mr1/android/support/v4/view/GravityCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/view/GravityCompatJellybeanMr1.java
index 1e6077d..efb1d77 100644
--- a/compat/jellybean-mr1/android/support/v4/view/GravityCompatJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/view/GravityCompatJellybeanMr1.java
@@ -18,8 +18,12 @@
package android.support.v4.view;
import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.Gravity;
+@RequiresApi(17)
+@TargetApi(17)
class GravityCompatJellybeanMr1 {
public static int getAbsoluteGravity(int gravity, int layoutDirection) {
diff --git a/compat/jellybean-mr1/android/support/v4/view/MarginLayoutParamsCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/view/MarginLayoutParamsCompatJellybeanMr1.java
index 2fe9bc8..c446abd 100644
--- a/compat/jellybean-mr1/android/support/v4/view/MarginLayoutParamsCompatJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/view/MarginLayoutParamsCompatJellybeanMr1.java
@@ -17,8 +17,12 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.ViewGroup;
+@RequiresApi(17)
+@TargetApi(17)
class MarginLayoutParamsCompatJellybeanMr1 {
public static int getMarginStart(ViewGroup.MarginLayoutParams lp) {
return lp.getMarginStart();
diff --git a/compat/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java
index c39ef2a..79b5ce2 100644
--- a/compat/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java
@@ -16,13 +16,18 @@
package android.support.v4.view;
+import android.annotation.TargetApi;
import android.graphics.Paint;
+import android.support.annotation.RequiresApi;
import android.view.Display;
import android.view.View;
/**
* Jellybean MR1 - specific View API access.
*/
+
+@RequiresApi(17)
+@TargetApi(17)
class ViewCompatJellybeanMr1 {
public static int getLabelFor(View view) {
diff --git a/compat/jellybean-mr1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java
index a4b9677..aa20646 100644
--- a/compat/jellybean-mr1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java
@@ -16,9 +16,13 @@
package android.support.v4.view.accessibility;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
+@RequiresApi(17)
+@TargetApi(17)
class AccessibilityNodeInfoCompatJellybeanMr1 {
public static void setLabelFor(Object info, View labeled) {
diff --git a/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java b/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
index fc088ed..34cd6e4 100644
--- a/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
@@ -16,12 +16,16 @@
package android.support.v4.widget;
+import android.annotation.TargetApi;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.view.View;
import android.widget.TextView;
+@RequiresApi(17)
+@TargetApi(17)
class TextViewCompatJbMr1 {
public static void setCompoundDrawablesRelative(@NonNull TextView textView,
diff --git a/compat/jellybean-mr2/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBeanMr2.java b/compat/jellybean-mr2/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBeanMr2.java
index 3a41b7a..acc72b1 100644
--- a/compat/jellybean-mr2/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBeanMr2.java
+++ b/compat/jellybean-mr2/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBeanMr2.java
@@ -17,11 +17,15 @@
package android.support.v4.accessibilityservice;
import android.accessibilityservice.AccessibilityServiceInfo;
-import android.content.pm.ResolveInfo;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
/**
* ICS implementation of the new APIs in AccessibilityServiceInfo.
*/
+
+@RequiresApi(18)
+@TargetApi(18)
class AccessibilityServiceInfoCompatJellyBeanMr2 {
public static int getCapabilities(AccessibilityServiceInfo info) {
diff --git a/compat/jellybean-mr2/android/support/v4/app/BundleCompatJellybeanMR2.java b/compat/jellybean-mr2/android/support/v4/app/BundleCompatJellybeanMR2.java
index bafefba..598ff31 100644
--- a/compat/jellybean-mr2/android/support/v4/app/BundleCompatJellybeanMR2.java
+++ b/compat/jellybean-mr2/android/support/v4/app/BundleCompatJellybeanMR2.java
@@ -18,7 +18,14 @@
import android.os.Bundle;
import android.os.IBinder;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+/**
+ * @hide
+ */
+@RequiresApi(18)
+@TargetApi(18)
class BundleCompatJellybeanMR2 {
public static IBinder getBinder(Bundle bundle, String key) {
return bundle.getBinder(key);
diff --git a/compat/jellybean-mr2/android/support/v4/graphics/BitmapCompatJellybeanMR2.java b/compat/jellybean-mr2/android/support/v4/graphics/BitmapCompatJellybeanMR2.java
index 21f0d49..20739d1 100644
--- a/compat/jellybean-mr2/android/support/v4/graphics/BitmapCompatJellybeanMR2.java
+++ b/compat/jellybean-mr2/android/support/v4/graphics/BitmapCompatJellybeanMR2.java
@@ -16,7 +16,11 @@
package android.support.v4.graphics;
import android.graphics.Bitmap;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(18)
+@TargetApi(18)
class BitmapCompatJellybeanMR2 {
public static boolean hasMipMap(Bitmap bitmap) {
return bitmap.hasMipMap();
diff --git a/compat/jellybean-mr2/android/support/v4/os/TraceJellybeanMR2.java b/compat/jellybean-mr2/android/support/v4/os/TraceJellybeanMR2.java
index 6116947..a41816d 100644
--- a/compat/jellybean-mr2/android/support/v4/os/TraceJellybeanMR2.java
+++ b/compat/jellybean-mr2/android/support/v4/os/TraceJellybeanMR2.java
@@ -14,7 +14,11 @@
package android.support.v4.os;
import android.os.Trace;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(18)
+@TargetApi(18)
class TraceJellybeanMR2 {
public static void beginSection(String section) {
Trace.beginSection(section);
diff --git a/compat/jellybean-mr2/android/support/v4/view/ViewCompatJellybeanMr2.java b/compat/jellybean-mr2/android/support/v4/view/ViewCompatJellybeanMr2.java
index b8fcd9d..46c5d4e 100644
--- a/compat/jellybean-mr2/android/support/v4/view/ViewCompatJellybeanMr2.java
+++ b/compat/jellybean-mr2/android/support/v4/view/ViewCompatJellybeanMr2.java
@@ -16,12 +16,17 @@
package android.support.v4.view;
-import android.view.View;
import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+import android.view.View;
/**
* Jellybean MR2 - specific View API access.
*/
+
+@RequiresApi(18)
+@TargetApi(18)
class ViewCompatJellybeanMr2 {
public static Rect getClipBounds(View view) {
diff --git a/compat/jellybean-mr2/android/support/v4/view/ViewGroupCompatJellybeanMR2.java b/compat/jellybean-mr2/android/support/v4/view/ViewGroupCompatJellybeanMR2.java
index 086b070..e1c8532 100644
--- a/compat/jellybean-mr2/android/support/v4/view/ViewGroupCompatJellybeanMR2.java
+++ b/compat/jellybean-mr2/android/support/v4/view/ViewGroupCompatJellybeanMR2.java
@@ -17,8 +17,12 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.ViewGroup;
+@RequiresApi(18)
+@TargetApi(18)
class ViewGroupCompatJellybeanMR2 {
public static int getLayoutMode(ViewGroup group) {
return group.getLayoutMode();
diff --git a/compat/jellybean-mr2/android/support/v4/view/ViewPropertyAnimatorCompatJellybeanMr2.java b/compat/jellybean-mr2/android/support/v4/view/ViewPropertyAnimatorCompatJellybeanMr2.java
index e9a29ee..14e76a9 100644
--- a/compat/jellybean-mr2/android/support/v4/view/ViewPropertyAnimatorCompatJellybeanMr2.java
+++ b/compat/jellybean-mr2/android/support/v4/view/ViewPropertyAnimatorCompatJellybeanMr2.java
@@ -15,9 +15,13 @@
*/
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.animation.Interpolator;
+@RequiresApi(18)
+@TargetApi(18)
class ViewPropertyAnimatorCompatJellybeanMr2 {
public static Interpolator getInterpolator(View view) {
return (Interpolator) view.animate().getInterpolator();
diff --git a/compat/jellybean-mr2/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java b/compat/jellybean-mr2/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java
index e48d9f7..82bfa11 100644
--- a/compat/jellybean-mr2/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java
+++ b/compat/jellybean-mr2/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java
@@ -16,10 +16,14 @@
package android.support.v4.view.accessibility;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.accessibility.AccessibilityNodeInfo;
import java.util.List;
+@RequiresApi(18)
+@TargetApi(18)
class AccessibilityNodeInfoCompatJellybeanMr2 {
public static void setViewIdResourceName(Object info, String viewId) {
diff --git a/compat/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java b/compat/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java
index 73f9666..2dea37d 100644
--- a/compat/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java
+++ b/compat/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java
@@ -16,12 +16,16 @@
package android.support.v4.widget;
+import android.annotation.TargetApi;
import android.graphics.drawable.Drawable;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.widget.TextView;
+@RequiresApi(18)
+@TargetApi(18)
class TextViewCompatJbMr2 {
public static void setCompoundDrawablesRelative(@NonNull TextView textView,
diff --git a/compat/jellybean/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBean.java b/compat/jellybean/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBean.java
index f0f8d0c..d42cefc 100644
--- a/compat/jellybean/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBean.java
@@ -18,10 +18,15 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.pm.PackageManager;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
/**
* JB implementation of the new APIs in AccessibilityServiceInfo.
*/
+
+@RequiresApi(16)
+@TargetApi(16)
class AccessibilityServiceInfoCompatJellyBean {
public static String loadDescription(AccessibilityServiceInfo info, PackageManager pm) {
diff --git a/compat/jellybean/android/support/v4/app/ActivityCompatJB.java b/compat/jellybean/android/support/v4/app/ActivityCompatJB.java
index 13e6e4e..ad1c9aa 100644
--- a/compat/jellybean/android/support/v4/app/ActivityCompatJB.java
+++ b/compat/jellybean/android/support/v4/app/ActivityCompatJB.java
@@ -16,11 +16,15 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+@RequiresApi(16)
+@TargetApi(16)
class ActivityCompatJB {
public static void startActivityForResult(Activity activity, Intent intent, int requestCode, Bundle options) {
activity.startActivityForResult(intent, requestCode, options);
diff --git a/compat/jellybean/android/support/v4/app/ActivityOptionsCompatJB.java b/compat/jellybean/android/support/v4/app/ActivityOptionsCompatJB.java
index df9d987..1655c4b 100644
--- a/compat/jellybean/android/support/v4/app/ActivityOptionsCompatJB.java
+++ b/compat/jellybean/android/support/v4/app/ActivityOptionsCompatJB.java
@@ -16,12 +16,16 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
import android.app.ActivityOptions;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
import android.view.View;
+@RequiresApi(16)
+@TargetApi(16)
class ActivityOptionsCompatJB {
public static ActivityOptionsCompatJB makeCustomAnimation(Context context,
diff --git a/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java b/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java
index 261d8c4..bb94873 100644
--- a/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java
+++ b/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java
@@ -22,6 +22,8 @@
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.Log;
import android.util.SparseArray;
import android.widget.RemoteViews;
@@ -30,6 +32,8 @@
import java.util.ArrayList;
import java.util.List;
+@RequiresApi(16)
+@TargetApi(16)
class NotificationCompatJellybean {
public static final String TAG = "NotificationCompat";
diff --git a/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java b/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java
index 36f5981..2fa9adc 100644
--- a/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java
+++ b/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java
@@ -20,7 +20,11 @@
import android.content.ClipDescription;
import android.content.Intent;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(16)
+@TargetApi(16)
class RemoteInputCompatJellybean {
/** Label used to denote the clip data type used for remote input transport */
public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
diff --git a/compat/jellybean/android/support/v4/app/ShareCompatJB.java b/compat/jellybean/android/support/v4/app/ShareCompatJB.java
index 8a4de97..58eaa23 100644
--- a/compat/jellybean/android/support/v4/app/ShareCompatJB.java
+++ b/compat/jellybean/android/support/v4/app/ShareCompatJB.java
@@ -16,8 +16,12 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.text.Html;
+@RequiresApi(16)
+@TargetApi(16)
class ShareCompatJB {
public static String escapeHtml(CharSequence html) {
return Html.escapeHtml(html);
diff --git a/compat/jellybean/android/support/v4/content/ContentResolverCompatJellybean.java b/compat/jellybean/android/support/v4/content/ContentResolverCompatJellybean.java
index dcd695e..ea4d610 100644
--- a/compat/jellybean/android/support/v4/content/ContentResolverCompatJellybean.java
+++ b/compat/jellybean/android/support/v4/content/ContentResolverCompatJellybean.java
@@ -20,7 +20,11 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.OperationCanceledException;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(16)
+@TargetApi(16)
class ContentResolverCompatJellybean {
public static Cursor query(ContentResolver resolver, Uri uri, String[] projection,
diff --git a/compat/jellybean/android/support/v4/content/ContextCompatJellybean.java b/compat/jellybean/android/support/v4/content/ContextCompatJellybean.java
index 5e9f910..c00a971 100644
--- a/compat/jellybean/android/support/v4/content/ContextCompatJellybean.java
+++ b/compat/jellybean/android/support/v4/content/ContextCompatJellybean.java
@@ -19,7 +19,11 @@
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(16)
+@TargetApi(16)
class ContextCompatJellybean {
public static void startActivities(Context context, Intent[] intents, Bundle options) {
diff --git a/compat/jellybean/android/support/v4/net/ConnectivityManagerCompatJellyBean.java b/compat/jellybean/android/support/v4/net/ConnectivityManagerCompatJellyBean.java
index 0795fdd..64272b8 100644
--- a/compat/jellybean/android/support/v4/net/ConnectivityManagerCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/net/ConnectivityManagerCompatJellyBean.java
@@ -17,10 +17,15 @@
package android.support.v4.net;
import android.net.ConnectivityManager;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
/**
* Implementation of ConnectivityManagerCompat that can use Jellybean APIs.
*/
+
+@RequiresApi(16)
+@TargetApi(16)
class ConnectivityManagerCompatJellyBean {
public static boolean isActiveNetworkMetered(ConnectivityManager cm) {
return cm.isActiveNetworkMetered();
diff --git a/compat/jellybean/android/support/v4/os/CancellationSignalCompatJellybean.java b/compat/jellybean/android/support/v4/os/CancellationSignalCompatJellybean.java
index 6029286..127fdbf 100644
--- a/compat/jellybean/android/support/v4/os/CancellationSignalCompatJellybean.java
+++ b/compat/jellybean/android/support/v4/os/CancellationSignalCompatJellybean.java
@@ -16,6 +16,11 @@
package android.support.v4.os;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+
+@RequiresApi(16)
+@TargetApi(16)
class CancellationSignalCompatJellybean {
public static Object create() {
return new android.os.CancellationSignal();
diff --git a/compat/jellybean/android/support/v4/view/AccessibilityDelegateCompatJellyBean.java b/compat/jellybean/android/support/v4/view/AccessibilityDelegateCompatJellyBean.java
index 6651b62..e588892 100644
--- a/compat/jellybean/android/support/v4/view/AccessibilityDelegateCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/view/AccessibilityDelegateCompatJellyBean.java
@@ -17,6 +17,8 @@
package android.support.v4.view;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
@@ -27,6 +29,9 @@
/**
* JellyBean specific AccessibilityDelegate API implementation.
*/
+
+@RequiresApi(16)
+@TargetApi(16)
class AccessibilityDelegateCompatJellyBean {
public interface AccessibilityDelegateBridgeJellyBean {
diff --git a/compat/jellybean/android/support/v4/view/ViewCompatJB.java b/compat/jellybean/android/support/v4/view/ViewCompatJB.java
index 3d64da1..ccf34ba 100644
--- a/compat/jellybean/android/support/v4/view/ViewCompatJB.java
+++ b/compat/jellybean/android/support/v4/view/ViewCompatJB.java
@@ -16,14 +16,18 @@
package android.support.v4.view;
+import android.annotation.TargetApi;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
import android.view.View;
import android.view.ViewParent;
/**
* Jellybean-specific View API access
*/
+@RequiresApi(16)
+@TargetApi(16)
class ViewCompatJB {
public static boolean hasTransientState(View view) {
diff --git a/compat/jellybean/android/support/v4/view/ViewPropertyAnimatorCompatJB.java b/compat/jellybean/android/support/v4/view/ViewPropertyAnimatorCompatJB.java
index 6107c42..8e327fe 100644
--- a/compat/jellybean/android/support/v4/view/ViewPropertyAnimatorCompatJB.java
+++ b/compat/jellybean/android/support/v4/view/ViewPropertyAnimatorCompatJB.java
@@ -17,8 +17,12 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.view.View;
+@RequiresApi(16)
+@TargetApi(16)
class ViewPropertyAnimatorCompatJB {
public static void withStartAction(View view, Runnable runnable) {
diff --git a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityEventCompatJellyBean.java b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityEventCompatJellyBean.java
index e557650..9c9ef09 100644
--- a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityEventCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityEventCompatJellyBean.java
@@ -16,8 +16,12 @@
package android.support.v4.view.accessibility;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.accessibility.AccessibilityEvent;
+@RequiresApi(16)
+@TargetApi(16)
class AccessibilityEventCompatJellyBean {
public static void setMovementGranularity(AccessibilityEvent event, int granularity) {
event.setMovementGranularity(granularity);
diff --git a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellyBean.java b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellyBean.java
index c2f4d93..a095b10 100644
--- a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellyBean.java
@@ -17,12 +17,17 @@
package android.support.v4.view.accessibility;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
/**
* JellyBean specific AccessibilityNodeInfo API implementation.
*/
+
+@RequiresApi(16)
+@TargetApi(16)
class AccessibilityNodeInfoCompatJellyBean {
public static void addChild(Object info, View child, int virtualDescendantId) {
diff --git a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatJellyBean.java b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatJellyBean.java
index cb8a3b4..195e2f3 100644
--- a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatJellyBean.java
@@ -17,6 +17,8 @@
package android.support.v4.view.accessibility;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
@@ -25,6 +27,9 @@
/**
* JellyBean specific AccessibilityNodeProvider API implementation.
*/
+
+@RequiresApi(16)
+@TargetApi(16)
class AccessibilityNodeProviderCompatJellyBean {
interface AccessibilityNodeInfoBridge {
public Object createAccessibilityNodeInfo(int virtualViewId);
diff --git a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityRecordCompatJellyBean.java b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityRecordCompatJellyBean.java
index dc9d518..e5a51b7 100644
--- a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityRecordCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityRecordCompatJellyBean.java
@@ -16,12 +16,17 @@
package android.support.v4.view.accessibility;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.accessibility.AccessibilityRecord;
/**
* JellyBean specific AccessibilityRecord API implementation.
*/
+
+@RequiresApi(16)
+@TargetApi(16)
class AccessibilityRecordCompatJellyBean {
public static void setSource(Object record, View root, int virtualDescendantId) {
diff --git a/compat/jellybean/android/support/v4/widget/TextViewCompatJb.java b/compat/jellybean/android/support/v4/widget/TextViewCompatJb.java
index 1658874..4fd4c4e 100644
--- a/compat/jellybean/android/support/v4/widget/TextViewCompatJb.java
+++ b/compat/jellybean/android/support/v4/widget/TextViewCompatJb.java
@@ -16,10 +16,12 @@
package android.support.v4.widget;
-import android.graphics.drawable.Drawable;
-import android.view.View;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.widget.TextView;
+@RequiresApi(16)
+@TargetApi(16)
class TextViewCompatJb {
static int getMaxLines(TextView textView) {
return textView.getMaxLines();
diff --git a/compat/kitkat/android/support/v4/app/ActivityManagerCompatKitKat.java b/compat/kitkat/android/support/v4/app/ActivityManagerCompatKitKat.java
index 3f889f6..f14b553 100644
--- a/compat/kitkat/android/support/v4/app/ActivityManagerCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/app/ActivityManagerCompatKitKat.java
@@ -17,7 +17,11 @@
package android.support.v4.app;
import android.app.ActivityManager;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(19)
+@TargetApi(19)
class ActivityManagerCompatKitKat {
public static boolean isLowRamDevice(ActivityManager am) {
return am.isLowRamDevice();
diff --git a/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java b/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java
index 3039458..5b11daf 100644
--- a/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java
@@ -21,12 +21,16 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.util.SparseArray;
import android.widget.RemoteViews;
import java.util.ArrayList;
import java.util.List;
+@RequiresApi(19)
+@TargetApi(19)
class NotificationCompatKitKat {
public static class Builder implements NotificationBuilderWithBuilderAccessor,
NotificationBuilderWithActions {
diff --git a/compat/kitkat/android/support/v4/app/NotificationManagerCompatKitKat.java b/compat/kitkat/android/support/v4/app/NotificationManagerCompatKitKat.java
index baf58a9..24bacba 100644
--- a/compat/kitkat/android/support/v4/app/NotificationManagerCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/app/NotificationManagerCompatKitKat.java
@@ -18,11 +18,15 @@
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+@RequiresApi(19)
+@TargetApi(19)
class NotificationManagerCompatKitKat {
private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
diff --git a/compat/kitkat/android/support/v4/content/ContextCompatKitKat.java b/compat/kitkat/android/support/v4/content/ContextCompatKitKat.java
index 6b58827..6c1bc91 100644
--- a/compat/kitkat/android/support/v4/content/ContextCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/content/ContextCompatKitKat.java
@@ -17,9 +17,13 @@
package android.support.v4.content;
import android.content.Context;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import java.io.File;
+@RequiresApi(19)
+@TargetApi(19)
class ContextCompatKitKat {
public static File[] getExternalCacheDirs(Context context) {
return context.getExternalCacheDirs();
diff --git a/compat/kitkat/android/support/v4/graphics/BitmapCompatKitKat.java b/compat/kitkat/android/support/v4/graphics/BitmapCompatKitKat.java
index 2d4a66e..ba05a32 100644
--- a/compat/kitkat/android/support/v4/graphics/BitmapCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/graphics/BitmapCompatKitKat.java
@@ -16,10 +16,15 @@
package android.support.v4.graphics;
import android.graphics.Bitmap;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
/**
* Implementation of BitmapCompat that can use KitKat APIs.
*/
+
+@RequiresApi(19)
+@TargetApi(19)
class BitmapCompatKitKat {
static int getAllocationByteCount(Bitmap bitmap) {
diff --git a/compat/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java b/compat/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java
index d9cf352..b63ea3f 100644
--- a/compat/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java
@@ -16,13 +16,16 @@
package android.support.v4.graphics.drawable;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
/**
* Implementation of drawable compatibility that can call KitKat APIs.
*/
+
+@RequiresApi(19)
+@TargetApi(19)
class DrawableCompatKitKat {
public static void setAutoMirrored(Drawable drawable, boolean mirrored) {
drawable.setAutoMirrored(mirrored);
diff --git a/compat/kitkat/android/support/v4/graphics/drawable/DrawableWrapperKitKat.java b/compat/kitkat/android/support/v4/graphics/drawable/DrawableWrapperKitKat.java
index 3cfb9e1..b758563 100644
--- a/compat/kitkat/android/support/v4/graphics/drawable/DrawableWrapperKitKat.java
+++ b/compat/kitkat/android/support/v4/graphics/drawable/DrawableWrapperKitKat.java
@@ -20,7 +20,11 @@
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+@RequiresApi(19)
+@TargetApi(19)
class DrawableWrapperKitKat extends DrawableWrapperHoneycomb {
DrawableWrapperKitKat(Drawable drawable) {
diff --git a/compat/kitkat/android/support/v4/os/EnvironmentCompatKitKat.java b/compat/kitkat/android/support/v4/os/EnvironmentCompatKitKat.java
index 9a803db..b835950 100644
--- a/compat/kitkat/android/support/v4/os/EnvironmentCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/os/EnvironmentCompatKitKat.java
@@ -17,9 +17,13 @@
package android.support.v4.os;
import android.os.Environment;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import java.io.File;
+@RequiresApi(19)
+@TargetApi(19)
class EnvironmentCompatKitKat {
public static String getStorageState(File path) {
return Environment.getStorageState(path);
diff --git a/compat/kitkat/android/support/v4/view/ScaleGestureDetectorCompatKitKat.java b/compat/kitkat/android/support/v4/view/ScaleGestureDetectorCompatKitKat.java
index d3f8eb0..7e873e4 100644
--- a/compat/kitkat/android/support/v4/view/ScaleGestureDetectorCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/ScaleGestureDetectorCompatKitKat.java
@@ -16,14 +16,17 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.ScaleGestureDetector;
-import android.view.MotionEvent;
-import android.content.Context;
/**
* Implementation of ScaleGestureDetector compatibility that can call KitKat APIs. This class is an
* implementation detail for ScaleGestureDetectorCompat and should not be used directly.
*/
+
+@RequiresApi(19)
+@TargetApi(19)
class ScaleGestureDetectorCompatKitKat {
private ScaleGestureDetectorCompatKitKat() {
diff --git a/compat/kitkat/android/support/v4/view/ViewCompatKitKat.java b/compat/kitkat/android/support/v4/view/ViewCompatKitKat.java
index fd1b327..d864e7b 100644
--- a/compat/kitkat/android/support/v4/view/ViewCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/ViewCompatKitKat.java
@@ -16,11 +16,16 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
/**
* KitKat-specific View API implementation.
*/
+
+@RequiresApi(19)
+@TargetApi(19)
class ViewCompatKitKat {
public static int getAccessibilityLiveRegion(View view) {
return view.getAccessibilityLiveRegion();
diff --git a/compat/kitkat/android/support/v4/view/ViewParentCompatKitKat.java b/compat/kitkat/android/support/v4/view/ViewParentCompatKitKat.java
index 5e60a9b..5a00d0c 100644
--- a/compat/kitkat/android/support/v4/view/ViewParentCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/ViewParentCompatKitKat.java
@@ -16,9 +16,13 @@
package android.support.v4.view;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.ViewParent;
+@RequiresApi(19)
+@TargetApi(19)
class ViewParentCompatKitKat {
public static void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,
View source, int changeType) {
diff --git a/compat/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java b/compat/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java
index 64f1969..f507a89 100644
--- a/compat/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java
+++ b/compat/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java
@@ -16,8 +16,12 @@
package android.support.v4.view;
import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.view.View;
+@RequiresApi(19)
+@TargetApi(19)
class ViewPropertyAnimatorCompatKK {
public static void setUpdateListener(final View view,
diff --git a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityEventCompatKitKat.java b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityEventCompatKitKat.java
index 1a2cd94..55e2faa 100644
--- a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityEventCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityEventCompatKitKat.java
@@ -16,8 +16,12 @@
package android.support.v4.view.accessibility;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.accessibility.AccessibilityEvent;
+@RequiresApi(19)
+@TargetApi(19)
class AccessibilityEventCompatKitKat {
public static void setContentChangeTypes(AccessibilityEvent event, int changeTypes) {
event.setContentChangeTypes(changeTypes);
diff --git a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityManagerCompatKitKat.java b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityManagerCompatKitKat.java
index 1cdedc0..cc94249 100644
--- a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityManagerCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityManagerCompatKitKat.java
@@ -16,12 +16,17 @@
package android.support.v4.view.accessibility;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
/**
* KitKat-specific AccessibilityManager API implementation.
*/
+
+@RequiresApi(19)
+@TargetApi(19)
class AccessibilityManagerCompatKitKat {
public static class TouchExplorationStateChangeListenerWrapper
diff --git a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
index 1efc7e9..7bb8b1e 100644
--- a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
@@ -17,11 +17,16 @@
package android.support.v4.view.accessibility;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.accessibility.AccessibilityNodeInfo;
/**
* KitKat-specific AccessibilityNodeInfo API implementation.
*/
+
+@RequiresApi(19)
+@TargetApi(19)
class AccessibilityNodeInfoCompatKitKat {
private static final byte TRAIT_UNSET = -1;
private static final String TRAITS_KEY =
diff --git a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatKitKat.java b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatKitKat.java
index 3aa475d..a2bbbdc 100644
--- a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatKitKat.java
@@ -17,6 +17,8 @@
package android.support.v4.view.accessibility;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
@@ -25,6 +27,9 @@
/**
* KitKat-specific AccessibilityNodeProvider API implementation.
*/
+
+@RequiresApi(19)
+@TargetApi(19)
class AccessibilityNodeProviderCompatKitKat {
interface AccessibilityNodeInfoBridge {
public Object createAccessibilityNodeInfo(int virtualViewId);
diff --git a/compat/kitkat/android/support/v4/widget/ListPopupWindowCompatKitKat.java b/compat/kitkat/android/support/v4/widget/ListPopupWindowCompatKitKat.java
index 7b8d8dd..9678dba 100644
--- a/compat/kitkat/android/support/v4/widget/ListPopupWindowCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/widget/ListPopupWindowCompatKitKat.java
@@ -16,6 +16,8 @@
package android.support.v4.widget;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ListPopupWindow;
@@ -23,6 +25,9 @@
/**
* Implementation of ListPopupWindow compatibility that can call KitKat APIs.
*/
+
+@RequiresApi(19)
+@TargetApi(19)
class ListPopupWindowCompatKitKat {
public static OnTouchListener createDragToOpenListener(Object listPopupWindow, View src) {
return ((ListPopupWindow) listPopupWindow).createDragToOpenListener(src);
diff --git a/compat/kitkat/android/support/v4/widget/ListViewCompatKitKat.java b/compat/kitkat/android/support/v4/widget/ListViewCompatKitKat.java
index f1a66dc..ab2ff53 100644
--- a/compat/kitkat/android/support/v4/widget/ListViewCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/widget/ListViewCompatKitKat.java
@@ -16,9 +16,12 @@
package android.support.v4.widget;
-import android.widget.AbsListView;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.widget.ListView;
+@RequiresApi(19)
+@TargetApi(19)
class ListViewCompatKitKat {
static void scrollListBy(final ListView listView, int y) {
listView.scrollListBy(y);
diff --git a/compat/kitkat/android/support/v4/widget/PopupMenuCompatKitKat.java b/compat/kitkat/android/support/v4/widget/PopupMenuCompatKitKat.java
index 4558798..da7bc7e 100644
--- a/compat/kitkat/android/support/v4/widget/PopupMenuCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/widget/PopupMenuCompatKitKat.java
@@ -16,12 +16,17 @@
package android.support.v4.widget;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View.OnTouchListener;
import android.widget.PopupMenu;
/**
* Implementation of PopupMenu compatibility that can call KitKat APIs.
*/
+
+@RequiresApi(19)
+@TargetApi(19)
class PopupMenuCompatKitKat {
public static OnTouchListener getDragToOpenListener(Object popupMenu) {
return ((PopupMenu) popupMenu).getDragToOpenListener();
diff --git a/compat/kitkat/android/support/v4/widget/PopupWindowCompatKitKat.java b/compat/kitkat/android/support/v4/widget/PopupWindowCompatKitKat.java
index 4333f4a..20b0af4 100644
--- a/compat/kitkat/android/support/v4/widget/PopupWindowCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/widget/PopupWindowCompatKitKat.java
@@ -16,14 +16,17 @@
package android.support.v4.widget;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
import android.view.View;
-import android.view.View.OnTouchListener;
-import android.widget.ListPopupWindow;
import android.widget.PopupWindow;
/**
* Implementation of PopupWindow compatibility that can call KitKat APIs.
*/
+
+@RequiresApi(19)
+@TargetApi(19)
class PopupWindowCompatKitKat {
public static void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff,
int gravity) {
diff --git a/core-ui/Android.mk b/core-ui/Android.mk
index 3765b04..bdad9f9 100644
--- a/core-ui/Android.mk
+++ b/core-ui/Android.mk
@@ -14,63 +14,29 @@
LOCAL_PATH := $(call my-dir)
-# A helper sub-library that makes direct use of Honeycomb APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-ui-honeycomb
-LOCAL_SDK_VERSION := 11
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb)
-LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-annotations \
- android-support-compat
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Ice Cream Sandwich APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-ui-ics
-LOCAL_SDK_VERSION := 14
-LOCAL_SRC_FILES := $(call all-java-files-under, ics)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-ui-honeycomb
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JellyBean MR2 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-ui-jellybean-mr2
-LOCAL_SDK_VERSION := 18
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr2)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-ui-ics
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Lollipop APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-ui-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-ui-jellybean-mr2
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
# Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+# LOCAL_STATIC_ANDROID_LIBRARIES := \
+# android-support-core-ui \
+# android-support-compat
+#
+# in their makefiles to include the resources and their dependencies in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-core-ui
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, java)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under,honeycomb) \
+ $(call all-java-files-under,ics) \
+ $(call all-java-files-under,jellybean-mr2) \
+ $(call all-java-files-under,api21) \
+ $(call all-java-files-under,java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-ui-api21
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-compat \
android-support-annotations
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/core-ui/api21/android/support/v4/widget/DrawerLayoutCompatApi21.java b/core-ui/api21/android/support/v4/widget/DrawerLayoutCompatApi21.java
index 07cc3fa..ff2e93d 100644
--- a/core-ui/api21/android/support/v4/widget/DrawerLayoutCompatApi21.java
+++ b/core-ui/api21/android/support/v4/widget/DrawerLayoutCompatApi21.java
@@ -17,9 +17,11 @@
package android.support.v4.widget;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -28,6 +30,8 @@
/**
* Provides functionality for DrawerLayout unique to API 21
*/
+@RequiresApi(21)
+@TargetApi(21)
class DrawerLayoutCompatApi21 {
private static final int[] THEME_ATTRS = {
diff --git a/core-ui/build.gradle b/core-ui/build.gradle
index 5ae0655..56afd5e 100644
--- a/core-ui/build.gradle
+++ b/core-ui/build.gradle
@@ -1,9 +1,8 @@
apply plugin: 'com.android.library'
archivesBaseName = 'support-core-ui'
-
-createApiSourceSets(project, gradle.ext.studioCompat.modules.coreui.apiTargets)
dependencies {
+ compile project(':support-annotations')
compile project(':support-compat')
androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
exclude module: 'support-annotations'
@@ -19,22 +18,24 @@
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
-setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.coreui.dependencies)
android {
- compileSdkVersion 9
+ compileSdkVersion project.ext.currentSdk
defaultConfig {
minSdkVersion 9
- // TODO: get target from branch
- //targetSdkVersion 19
-
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = ['java']
+ main.java.srcDirs = [
+ 'honeycomb',
+ 'ics',
+ 'jellybean-mr2',
+ 'api21',
+ 'java'
+ ]
androidTest.setRoot('tests')
androidTest.java.srcDir 'tests/java'
@@ -93,11 +94,6 @@
exclude('android/service/media/**')
}
- project.ext.allSS.each { ss ->
- javadocTask.source ss.java
- sourcesJarTask.from ss.java.srcDirs
- }
-
artifacts.add('archives', javadocJarTask);
artifacts.add('archives', sourcesJarTask);
}
diff --git a/core-ui/honeycomb/android/support/v4/app/ActionBarDrawerToggleHoneycomb.java b/core-ui/honeycomb/android/support/v4/app/ActionBarDrawerToggleHoneycomb.java
index bd27954..bd4fd97 100644
--- a/core-ui/honeycomb/android/support/v4/app/ActionBarDrawerToggleHoneycomb.java
+++ b/core-ui/honeycomb/android/support/v4/app/ActionBarDrawerToggleHoneycomb.java
@@ -18,12 +18,13 @@
package android.support.v4.app;
import android.R;
+import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
-import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.support.annotation.RequiresApi;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -38,6 +39,8 @@
* in an action bar without some really gross hacks. Since the MR2 SDK is not published as of
* this writing, the new API is accessed via reflection here if available.
*/
+@RequiresApi(11)
+@TargetApi(11)
class ActionBarDrawerToggleHoneycomb {
private static final String TAG = "ActionBarDrawerToggleHoneycomb";
diff --git a/core-ui/ics/android/support/v4/view/PagerTitleStripIcs.java b/core-ui/ics/android/support/v4/view/PagerTitleStripIcs.java
index 99cc867..c8e70bd 100644
--- a/core-ui/ics/android/support/v4/view/PagerTitleStripIcs.java
+++ b/core-ui/ics/android/support/v4/view/PagerTitleStripIcs.java
@@ -16,13 +16,17 @@
package android.support.v4.view;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.support.annotation.RequiresApi;
import android.text.method.SingleLineTransformationMethod;
import android.view.View;
import android.widget.TextView;
import java.util.Locale;
+@RequiresApi(14)
+@TargetApi(14)
class PagerTitleStripIcs {
public static void setSingleLineAllCaps(TextView text) {
text.setTransformationMethod(new SingleLineAllCapsTransform(text.getContext()));
diff --git a/core-ui/java/android/support/v4/app/ActionBarDrawerToggle.java b/core-ui/java/android/support/v4/app/ActionBarDrawerToggle.java
index 197f96e..4c99341 100644
--- a/core-ui/java/android/support/v4/app/ActionBarDrawerToggle.java
+++ b/core-ui/java/android/support/v4/app/ActionBarDrawerToggle.java
@@ -17,6 +17,7 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
@@ -27,6 +28,7 @@
import android.os.Build;
import android.support.annotation.DrawableRes;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.support.annotation.StringRes;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.GravityCompat;
@@ -132,6 +134,8 @@
}
}
+ @RequiresApi(11)
+ @TargetApi(11)
private static class ActionBarDrawerToggleImplHC implements ActionBarDrawerToggleImpl {
ActionBarDrawerToggleImplHC() {
}
@@ -155,6 +159,8 @@
}
}
+ @RequiresApi(18)
+ @TargetApi(18)
private static class ActionBarDrawerToggleImplJellybeanMR2
implements ActionBarDrawerToggleImpl {
ActionBarDrawerToggleImplJellybeanMR2() {
diff --git a/core-ui/jellybean-mr2/android/support/v4/app/ActionBarDrawerToggleJellybeanMR2.java b/core-ui/jellybean-mr2/android/support/v4/app/ActionBarDrawerToggleJellybeanMR2.java
index 5d98d88..f4dba39 100644
--- a/core-ui/jellybean-mr2/android/support/v4/app/ActionBarDrawerToggleJellybeanMR2.java
+++ b/core-ui/jellybean-mr2/android/support/v4/app/ActionBarDrawerToggleJellybeanMR2.java
@@ -18,13 +18,16 @@
package android.support.v4.app;
import android.R;
+import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
-import android.util.Log;
+import android.support.annotation.RequiresApi;
+@RequiresApi(18)
+@TargetApi(18)
class ActionBarDrawerToggleJellybeanMR2 {
private static final String TAG = "ActionBarDrawerToggleImplJellybeanMR2";
diff --git a/core-utils/Android.mk b/core-utils/Android.mk
index add72db..d3f113e 100644
--- a/core-utils/Android.mk
+++ b/core-utils/Android.mk
@@ -14,107 +14,33 @@
LOCAL_PATH := $(call my-dir)
-# A helper sub-library that makes direct use of Gingerbread APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-gingerbread
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, gingerbread)
-LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-annotations \
- android-support-compat
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Honeycomb APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-honeycomb
-LOCAL_SDK_VERSION := 11
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-utils-gingerbread
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JellyBean APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-jellybean
-LOCAL_SDK_VERSION := 16
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-utils-honeycomb
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of KitKat APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-kitkat
-LOCAL_SDK_VERSION := 19
-LOCAL_SRC_FILES := $(call all-java-files-under, kitkat)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-utils-jellybean
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V20 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-api20
-LOCAL_SDK_VERSION := 20
-LOCAL_SRC_FILES := $(call all-java-files-under, api20)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-utils-kitkat
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Lollipop APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-utils-api20
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V23 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-api23
-LOCAL_SDK_VERSION := 23
-LOCAL_SRC_FILES := $(call all-java-files-under, api23)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-utils-api21
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V24 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-api24
-LOCAL_SDK_VERSION := 24
-LOCAL_SRC_FILES := $(call all-java-files-under, api24)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-utils-api23
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
# Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+# LOCAL_STATIC_ANDROID_LIBRARIES := \
+# android-support-core-utils \
+# android-support-compat
+#
+# in their makefiles to include the resources and their dependencies in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-core-utils
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, java)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under,gingerbread) \
+ $(call all-java-files-under,honeycomb) \
+ $(call all-java-files-under,jellybean) \
+ $(call all-java-files-under,kitkat) \
+ $(call all-java-files-under,api20) \
+ $(call all-java-files-under,api21) \
+ $(call all-java-files-under,api23) \
+ $(call all-java-files-under,api24) \
+ $(call all-java-files-under,java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-core-utils-api24
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-compat \
android-support-annotations
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/core-utils/api20/android/support/v4/print/PrintHelperApi20.java b/core-utils/api20/android/support/v4/print/PrintHelperApi20.java
index ce62106..831e9dd 100644
--- a/core-utils/api20/android/support/v4/print/PrintHelperApi20.java
+++ b/core-utils/api20/android/support/v4/print/PrintHelperApi20.java
@@ -16,11 +16,15 @@
package android.support.v4.print;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.support.annotation.RequiresApi;
/**
* Api20 specific PrintManager API implementation.
*/
+@RequiresApi(20)
+@TargetApi(20)
class PrintHelperApi20 extends PrintHelperKitkat {
PrintHelperApi20(Context context) {
super(context);
diff --git a/core-utils/api21/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.java b/core-utils/api21/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.java
index 081feea..d521293 100644
--- a/core-utils/api21/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.java
+++ b/core-utils/api21/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.java
@@ -16,13 +16,17 @@
package android.support.v4.graphics.drawable;
+import android.annotation.TargetApi;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Outline;
import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
import android.view.Gravity;
import android.view.View;
+@RequiresApi(21)
+@TargetApi(21)
class RoundedBitmapDrawable21 extends RoundedBitmapDrawable {
protected RoundedBitmapDrawable21(Resources res, Bitmap bitmap) {
super(res, bitmap);
diff --git a/core-utils/api21/android/support/v4/provider/DocumentsContractApi21.java b/core-utils/api21/android/support/v4/provider/DocumentsContractApi21.java
index b583a0d..03667b3 100644
--- a/core-utils/api21/android/support/v4/provider/DocumentsContractApi21.java
+++ b/core-utils/api21/android/support/v4/provider/DocumentsContractApi21.java
@@ -16,15 +16,19 @@
package android.support.v4.provider;
+import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.DocumentsContract;
+import android.support.annotation.RequiresApi;
import android.util.Log;
import java.util.ArrayList;
+@RequiresApi(21)
+@TargetApi(21)
class DocumentsContractApi21 {
private static final String TAG = "DocumentFile";
diff --git a/core-utils/api23/android/support/v4/print/PrintHelperApi23.java b/core-utils/api23/android/support/v4/print/PrintHelperApi23.java
index ba646e3..e2f6d69 100644
--- a/core-utils/api23/android/support/v4/print/PrintHelperApi23.java
+++ b/core-utils/api23/android/support/v4/print/PrintHelperApi23.java
@@ -16,12 +16,16 @@
package android.support.v4.print;
+import android.annotation.TargetApi;
import android.content.Context;
import android.print.PrintAttributes;
+import android.support.annotation.RequiresApi;
/**
* Api23 specific PrintManager API implementation.
*/
+@RequiresApi(23)
+@TargetApi(23)
class PrintHelperApi23 extends PrintHelperApi20 {
@Override
protected PrintAttributes.Builder copyAttributes(PrintAttributes other) {
diff --git a/core-utils/api24/android/support/v4/print/PrintHelperApi24.java b/core-utils/api24/android/support/v4/print/PrintHelperApi24.java
index 3815941..36edfbd 100644
--- a/core-utils/api24/android/support/v4/print/PrintHelperApi24.java
+++ b/core-utils/api24/android/support/v4/print/PrintHelperApi24.java
@@ -16,11 +16,15 @@
package android.support.v4.print;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.support.annotation.RequiresApi;
/**
* Api24 specific PrintManager API implementation.
*/
+@RequiresApi(24)
+@TargetApi(24)
class PrintHelperApi24 extends PrintHelperApi23 {
PrintHelperApi24(Context context) {
super(context);
diff --git a/core-utils/build.gradle b/core-utils/build.gradle
index f6ceb14..97325a1 100644
--- a/core-utils/build.gradle
+++ b/core-utils/build.gradle
@@ -1,9 +1,8 @@
apply plugin: 'com.android.library'
archivesBaseName = 'support-core-utils'
-
-createApiSourceSets(project, gradle.ext.studioCompat.modules.coreutils.apiTargets)
dependencies {
+ compile project(':support-annotations')
compile project(':support-compat')
androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
exclude module: 'support-annotations'
@@ -17,24 +16,28 @@
testCompile 'junit:junit:4.12'
}
-sourceCompatibility = JavaVersion.VERSION_1_7
-targetCompatibility = JavaVersion.VERSION_1_7
-setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.coreutils.dependencies)
-
android {
- compileSdkVersion 9
+ compileSdkVersion project.ext.currentSdk
defaultConfig {
minSdkVersion 9
- // TODO: get target from branch
- //targetSdkVersion 19
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = ['java']
+ main.java.srcDirs = [
+ 'gingerbread',
+ 'honeycomb',
+ 'jellybean',
+ 'kitkat',
+ 'api20',
+ 'api21',
+ 'api23',
+ 'api24',
+ 'java'
+ ]
androidTest.setRoot('tests')
androidTest.java.srcDir 'tests/java'
@@ -88,11 +91,6 @@
exclude('android/service/media/**')
}
- project.ext.allSS.each { ss ->
- javadocTask.source ss.java
- sourcesJarTask.from ss.java.srcDirs
- }
-
artifacts.add('archives', javadocJarTask);
artifacts.add('archives', sourcesJarTask);
}
diff --git a/core-utils/gingerbread/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java b/core-utils/gingerbread/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java
index 973d8dc..72b6abb 100644
--- a/core-utils/gingerbread/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java
+++ b/core-utils/gingerbread/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java
@@ -15,6 +15,7 @@
*/
package android.support.v4.graphics.drawable;
+import android.annotation.TargetApi;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
@@ -27,6 +28,7 @@
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
import android.util.DisplayMetrics;
import android.view.Gravity;
@@ -40,6 +42,8 @@
* {@link android.graphics.Canvas}.
* </p>
*/
+@RequiresApi(9)
+@TargetApi(9)
public abstract class RoundedBitmapDrawable extends Drawable {
private static final int DEFAULT_PAINT_FLAGS =
Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG;
diff --git a/core-utils/honeycomb/android/support/v4/app/TaskStackBuilderHoneycomb.java b/core-utils/honeycomb/android/support/v4/app/TaskStackBuilderHoneycomb.java
index 8bb602e..d970019 100644
--- a/core-utils/honeycomb/android/support/v4/app/TaskStackBuilderHoneycomb.java
+++ b/core-utils/honeycomb/android/support/v4/app/TaskStackBuilderHoneycomb.java
@@ -16,13 +16,17 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.support.annotation.RequiresApi;
/**
* Implementation of TaskStackBuilder that can call Honeycomb APIs.
*/
+@RequiresApi(11)
+@TargetApi(11)
class TaskStackBuilderHoneycomb {
public static PendingIntent getActivitiesPendingIntent(Context context, int requestCode,
Intent[] intents, int flags) {
diff --git a/core-utils/jellybean/android/support/v4/app/NavUtilsJB.java b/core-utils/jellybean/android/support/v4/app/NavUtilsJB.java
index a9a76c2..86a28b3 100644
--- a/core-utils/jellybean/android/support/v4/app/NavUtilsJB.java
+++ b/core-utils/jellybean/android/support/v4/app/NavUtilsJB.java
@@ -16,10 +16,14 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.support.annotation.RequiresApi;
+@RequiresApi(16)
+@TargetApi(16)
class NavUtilsJB {
public static Intent getParentActivityIntent(Activity activity) {
return activity.getParentActivityIntent();
diff --git a/core-utils/jellybean/android/support/v4/app/TaskStackBuilderJellybean.java b/core-utils/jellybean/android/support/v4/app/TaskStackBuilderJellybean.java
index 8b79b1b..96c3dda 100644
--- a/core-utils/jellybean/android/support/v4/app/TaskStackBuilderJellybean.java
+++ b/core-utils/jellybean/android/support/v4/app/TaskStackBuilderJellybean.java
@@ -16,11 +16,15 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+@RequiresApi(16)
+@TargetApi(16)
class TaskStackBuilderJellybean {
public static PendingIntent getActivitiesPendingIntent(Context context, int requestCode,
diff --git a/core-utils/kitkat/android/support/v4/print/PrintHelperKitkat.java b/core-utils/kitkat/android/support/v4/print/PrintHelperKitkat.java
index 6e2149e..08a41b2 100644
--- a/core-utils/kitkat/android/support/v4/print/PrintHelperKitkat.java
+++ b/core-utils/kitkat/android/support/v4/print/PrintHelperKitkat.java
@@ -16,6 +16,7 @@
package android.support.v4.print;
+import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
@@ -34,11 +35,12 @@
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
+import android.print.PrintAttributes.MediaSize;
import android.print.PrintDocumentAdapter;
import android.print.PrintDocumentInfo;
import android.print.PrintManager;
-import android.print.PrintAttributes.MediaSize;
import android.print.pdf.PrintedPdfDocument;
+import android.support.annotation.RequiresApi;
import android.util.Log;
import java.io.FileNotFoundException;
@@ -49,6 +51,8 @@
/**
* Kitkat specific PrintManager API implementation.
*/
+@RequiresApi(19)
+@TargetApi(19)
class PrintHelperKitkat {
private static final String LOG_TAG = "PrintHelperKitkat";
// will be <= 300 dpi on A4 (8.3×11.7) paper (worst case of 150 dpi)
diff --git a/core-utils/kitkat/android/support/v4/provider/DocumentsContractApi19.java b/core-utils/kitkat/android/support/v4/provider/DocumentsContractApi19.java
index 10bcf25..f164f17 100644
--- a/core-utils/kitkat/android/support/v4/provider/DocumentsContractApi19.java
+++ b/core-utils/kitkat/android/support/v4/provider/DocumentsContractApi19.java
@@ -16,6 +16,7 @@
package android.support.v4.provider;
+import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -23,9 +24,12 @@
import android.database.Cursor;
import android.net.Uri;
import android.provider.DocumentsContract;
+import android.support.annotation.RequiresApi;
import android.text.TextUtils;
import android.util.Log;
+@RequiresApi(19)
+@TargetApi(19)
class DocumentsContractApi19 {
private static final String TAG = "DocumentFile";
diff --git a/customtabs/Android.mk b/customtabs/Android.mk
index 50e6dbc..cfd9971 100644
--- a/customtabs/Android.mk
+++ b/customtabs/Android.mk
@@ -15,16 +15,25 @@
LOCAL_PATH := $(call my-dir)
# Here is the final static library that apps can link against.
-# The R class is automatically excluded from the generated library.
-# Applications that use this library must specify LOCAL_RESOURCE_DIR
-# in their makefiles to include the resources in their package.
+# Applications that use this library must specify
+#
+# LOCAL_STATIC_ANDROID_LIBRARIES := \
+# android-support-customtabs \
+# android-support-compat
+#
+# in their makefiles to include the resources and their dependencies in their package.
include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-customtabs
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_AIDL_INCLUDES := $LOCAL_PATH/src
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
- $(call all-Iaidl-files-under, src)
-LOCAL_JAVA_LIBRARIES := android-support-annotations \
-android-support-compat
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under,src) \
+ $(call all-Iaidl-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-annotations \
+ android-support-compat
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/design/Android.mk b/design/Android.mk
index 3217915..b3bc846 100644
--- a/design/Android.mk
+++ b/design/Android.mk
@@ -14,158 +14,38 @@
LOCAL_PATH := $(call my-dir)
-# Android libraries referenced by this module's resources.
-resource_libs := \
- android-support-v7-appcompat \
- android-support-v7-recyclerview
-
-# Build the resources using the latest applicable SDK version.
-# We do this here because the final static library must be compiled with an older
-# SDK version than the resources. The resources library and the R class that it
-# contains will not be linked into the final static library.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-design-res
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := $(resource_libs)
-LOCAL_AAPT_FLAGS := --no-version-vectors
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library to resolve cyclic dependencies between src and the platform dependent
-# implementations
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-design-base
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, base)
-LOCAL_JAVA_LIBRARIES := \
- android-support-design-res \
- android-support-compat \
- android-support-core-utils \
- android-support-core-ui \
- android-support-fragment \
- android-support-v7-appcompat \
- android-support-v7-recyclerview
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Gingerbread APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-design-gingerbread
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, gingerbread)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-base
-LOCAL_JAVA_LIBRARIES := \
- android-support-design-res \
- android-support-compat \
- android-support-core-utils \
- android-support-core-ui \
- android-support-fragment \
- android-support-v7-appcompat \
- android-support-v7-recyclerview
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Honeycomb APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-design-honeycomb
-LOCAL_SDK_VERSION := 11
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-gingerbread
-LOCAL_JAVA_LIBRARIES := \
- android-support-design-res \
- android-support-compat \
- android-support-core-utils \
- android-support-core-ui \
- android-support-fragment \
- android-support-v7-appcompat \
- android-support-v7-recyclerview
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Honeycomb MR1 APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-design-honeycomb-mr1
-LOCAL_SDK_VERSION := 12
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb-mr1)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-honeycomb
-LOCAL_JAVA_LIBRARIES := \
- android-support-design-res \
- android-support-compat \
- android-support-core-utils \
- android-support-core-ui \
- android-support-fragment \
- android-support-v7-appcompat \
- android-support-v7-recyclerview
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of ICS APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-design-ics
-LOCAL_SDK_VERSION := 14
-LOCAL_SRC_FILES := $(call all-java-files-under, ics)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-honeycomb-mr1
-LOCAL_JAVA_LIBRARIES := \
- android-support-design-res \
- android-support-compat \
- android-support-core-utils \
- android-support-core-ui \
- android-support-fragment \
- android-support-v7-appcompat \
- android-support-v7-recyclerview \
- android-support-transition
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Lollipop APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-design-lollipop
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, lollipop)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-ics
-LOCAL_JAVA_LIBRARIES := \
- android-support-design-res \
- android-support-compat \
- android-support-core-utils \
- android-support-core-ui \
- android-support-fragment \
- android-support-v7-appcompat \
- android-support-v7-recyclerview \
- android-support-transition
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
# Here is the final static library that apps can link against.
# Applications that use this library must specify
#
# LOCAL_STATIC_ANDROID_LIBRARIES := \
# android-support-design \
+# android-support-transition \
# android-support-v7-appcompat \
# android-support-v7-recyclerview \
-# android-support-compat \
-# android-support-core-utils \
-# android-support-core-ui \
-# android-support-fragment
+# android-support-v4
#
# in their makefiles to include the resources and their dependencies in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-design
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-lollipop
-LOCAL_STATIC_ANDROID_LIBRARIES := android-support-design-res
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under,base) \
+ $(call all-java-files-under,gingerbread) \
+ $(call all-java-files-under,honeycomb) \
+ $(call all-java-files-under,honeycomb-mr1) \
+ $(call all-java-files-under,ics) \
+ $(call all-java-files-under,lollipop) \
+ $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_SHARED_ANDROID_LIBRARIES := \
- $(resource_libs) \
- android-support-compat \
- android-support-core-utils \
- android-support-core-ui \
- android-support-fragment
-LOCAL_JAR_EXCLUDE_FILES := none
+ android-support-transition \
+ android-support-v7-appcompat \
+ android-support-v7-recyclerview \
+ android-support-v4 \
+ android-support-annotations
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+LOCAL_AAPT_FLAGS := \
+ --no-version-vectors \
+ --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/design/build.gradle b/design/build.gradle
index 577b047..1f35a60 100644
--- a/design/build.gradle
+++ b/design/build.gradle
@@ -3,10 +3,7 @@
archivesBaseName = 'design'
dependencies {
- compile project(':support-compat')
- compile project(':support-core-utils')
- compile project(':support-core-ui')
- compile project(':support-fragment')
+ compile project(':support-v4')
compile project(':support-appcompat-v7')
compile project(':support-recyclerview-v7')
compile project(':support-transition')
@@ -38,9 +35,19 @@
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = ['base', 'gingerbread', 'honeycomb', 'honeycomb-mr1', 'ics', 'lollipop', 'src']
- main.res.srcDirs 'res', 'res-public'
- main.assets.srcDir 'assets'
+ main.java.srcDirs = [
+ 'base',
+ 'gingerbread',
+ 'honeycomb',
+ 'honeycomb-mr1',
+ 'ics',
+ 'lollipop',
+ 'src'
+ ]
+ main.res.srcDirs = [
+ 'res',
+ 'res-public'
+ ]
main.resources.srcDir 'src'
androidTest.setRoot('tests')
diff --git a/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java b/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java
index 1cb1cfe..e1f287b 100644
--- a/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java
+++ b/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java
@@ -19,8 +19,12 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.view.animation.Interpolator;
+@RequiresApi(12)
+@TargetApi(12)
class ValueAnimatorCompatImplHoneycombMr1 extends ValueAnimatorCompat.Impl {
private final ValueAnimator mValueAnimator;
diff --git a/design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java b/design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java
index 96b15a8..49a07cd 100644
--- a/design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java
+++ b/design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java
@@ -16,13 +16,17 @@
package android.support.design.widget;
+import android.annotation.TargetApi;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.support.annotation.RequiresApi;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
+@RequiresApi(11)
+@TargetApi(11)
class ViewGroupUtilsHoneycomb {
private static final ThreadLocal<Matrix> sMatrix = new ThreadLocal<>();
private static final ThreadLocal<RectF> sRectF = new ThreadLocal<>();
diff --git a/design/ics/android/support/design/internal/TextScale.java b/design/ics/android/support/design/internal/TextScale.java
index 219353e..c017223 100644
--- a/design/ics/android/support/design/internal/TextScale.java
+++ b/design/ics/android/support/design/internal/TextScale.java
@@ -18,6 +18,8 @@
import android.animation.Animator;
import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.support.transition.Transition;
import android.support.transition.TransitionValues;
import android.view.ViewGroup;
@@ -28,6 +30,8 @@
/**
* @hide
*/
+@RequiresApi(14)
+@TargetApi(14)
public class TextScale extends Transition {
private static final String PROPNAME_SCALE = "android:textscale:scale";
diff --git a/design/ics/android/support/design/widget/FloatingActionButtonIcs.java b/design/ics/android/support/design/widget/FloatingActionButtonIcs.java
index 25f1f70..4825124 100644
--- a/design/ics/android/support/design/widget/FloatingActionButtonIcs.java
+++ b/design/ics/android/support/design/widget/FloatingActionButtonIcs.java
@@ -18,11 +18,15 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.annotation.TargetApi;
import android.os.Build;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.support.v4.view.ViewCompat;
import android.view.View;
+@RequiresApi(14)
+@TargetApi(14)
class FloatingActionButtonIcs extends FloatingActionButtonGingerbread {
private float mRotation;
diff --git a/design/lollipop/android/support/design/widget/CircularBorderDrawableLollipop.java b/design/lollipop/android/support/design/widget/CircularBorderDrawableLollipop.java
index 8b90361..ed03d35 100644
--- a/design/lollipop/android/support/design/widget/CircularBorderDrawableLollipop.java
+++ b/design/lollipop/android/support/design/widget/CircularBorderDrawableLollipop.java
@@ -16,11 +16,15 @@
package android.support.design.widget;
+import android.annotation.TargetApi;
import android.graphics.Outline;
+import android.support.annotation.RequiresApi;
/**
* Lollipop version of {@link CircularBorderDrawable}.
*/
+@RequiresApi(21)
+@TargetApi(21)
class CircularBorderDrawableLollipop extends CircularBorderDrawable {
@Override
diff --git a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
index 506f459..c61dc2a 100644
--- a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
+++ b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
@@ -27,11 +27,12 @@
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;
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+@RequiresApi(21)
+@TargetApi(21)
class FloatingActionButtonLollipop extends FloatingActionButtonIcs {
private InsetDrawable mInsetDrawable;
diff --git a/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java b/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java
index 047a828..8dfa926 100644
--- a/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java
+++ b/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java
@@ -19,13 +19,17 @@
import android.animation.AnimatorInflater;
import android.animation.ObjectAnimator;
import android.animation.StateListAnimator;
+import android.annotation.TargetApi;
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)
+@TargetApi(21)
class ViewUtilsLollipop {
private static final int[] STATE_LIST_ANIM_ATTRS = new int[] {android.R.attr.stateListAnimator};
diff --git a/design/res/values/attrs.xml b/design/res/values/attrs.xml
index 545a82e..41e0c0f 100644
--- a/design/res/values/attrs.xml
+++ b/design/res/values/attrs.xml
@@ -469,6 +469,7 @@
<attr name="itemIconTint"/>
<attr name="itemTextColor"/>
<attr name="itemBackground"/>
+ <attr name="elevation"/>
</declare-styleable>
</resources>
diff --git a/design/res/values/colors.xml b/design/res/values/colors.xml
index ebf6412..eb18f05 100644
--- a/design/res/values/colors.xml
+++ b/design/res/values/colors.xml
@@ -38,4 +38,6 @@
<color name="design_snackbar_background_color">#323232</color>
+ <color name="design_bottom_navigation_shadow_color">#14000000</color>
+
</resources>
\ No newline at end of file
diff --git a/design/res/values/dimens.xml b/design/res/values/dimens.xml
index 63e98b8..de715ce 100644
--- a/design/res/values/dimens.xml
+++ b/design/res/values/dimens.xml
@@ -59,6 +59,8 @@
<dimen name="design_bottom_sheet_peek_height_min">64dp</dimen>
<dimen name="design_bottom_navigation_height">56dp</dimen>
+ <dimen name="design_bottom_navigation_elevation">8dp</dimen>
+ <dimen name="design_bottom_navigation_shadow_height">1dp</dimen>
<dimen name="design_bottom_navigation_text_size">12sp</dimen>
<dimen name="design_bottom_navigation_active_text_size">14sp</dimen>
<dimen name="design_bottom_navigation_margin">8dp</dimen>
diff --git a/design/res/values/styles.xml b/design/res/values/styles.xml
index c8b9cd5..820742f 100644
--- a/design/res/values/styles.xml
+++ b/design/res/values/styles.xml
@@ -44,6 +44,7 @@
<style name="Widget.Design.BottomNavigationView" parent="">
<item name="itemBackground">?attr/selectableItemBackgroundBorderless</item>
+ <item name="elevation">@dimen/design_bottom_navigation_elevation</item>
</style>
<style name="Base.Widget.Design.TabLayout" parent="android:Widget">
diff --git a/design/src/android/support/design/internal/BottomNavigationMenuView.java b/design/src/android/support/design/internal/BottomNavigationMenuView.java
index df361e6..e6ae08f 100644
--- a/design/src/android/support/design/internal/BottomNavigationMenuView.java
+++ b/design/src/android/support/design/internal/BottomNavigationMenuView.java
@@ -86,8 +86,9 @@
public void onClick(View v) {
final BottomNavigationItemView itemView = (BottomNavigationItemView) v;
final int itemPosition = itemView.getItemPosition();
- activateNewButton(itemPosition);
- mMenu.performItemAction(itemView.getItemData(), mPresenter, 0);
+ if (!mMenu.performItemAction(itemView.getItemData(), mPresenter, 0)) {
+ activateNewButton(itemPosition);
+ }
}
};
mTempChildWidths = new int[BottomNavigationMenu.MAX_ITEM_COUNT];
@@ -96,10 +97,6 @@
@Override
public void initialize(MenuBuilder menu) {
mMenu = menu;
- if (mMenu == null) return;
- if (mMenu.size() > mActiveButton) {
- mMenu.getItem(mActiveButton).setChecked(true);
- }
}
@Override
@@ -124,7 +121,7 @@
}
}
} else {
- final int maxAvailable = width / count;
+ 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++) {
@@ -180,9 +177,9 @@
}
/**
- * Set the tint which is applied to the menu items' icons.
+ * Sets the tint which is applied to the menu items' icons.
*
- * @param tint the tint to apply.
+ * @param tint the tint to apply
*/
public void setIconTintList(ColorStateList tint) {
mItemIconTint = tint;
@@ -195,7 +192,7 @@
/**
* Returns the tint which is applied to menu items' icons.
*
- * @return The ColorStateList that is used to tint menu items' icons.
+ * @return the ColorStateList that is used to tint menu items' icons
*/
@Nullable
public ColorStateList getIconTintList() {
@@ -203,7 +200,7 @@
}
/**
- * Set the text color to be used on menu items.
+ * Sets the text color to be used on menu items.
*
* @param color the ColorStateList used for menu items' text.
*/
@@ -218,15 +215,16 @@
/**
* Returns the text color used on menu items.
*
- * @return the ColorStateList used for menu items' text.
+ * @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.
+ * 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;
@@ -237,9 +235,9 @@
}
/**
- * Returns the background resource of the menu items.
+ * Returns the resource ID for the background of the menu items.
*
- * @return the resource id of the background.
+ * @return the resource ID for the background
*/
public int getItemBackgroundRes() {
return mItemBackgroundRes;
@@ -256,6 +254,9 @@
}
}
removeAllViews();
+ if (mMenu.size() == 0) {
+ return;
+ }
mButtons = new BottomNavigationItemView[mMenu.size()];
mShiftingMode = mMenu.size() > 3;
for (int i = 0; i < mMenu.size(); i++) {
@@ -273,6 +274,8 @@
child.setOnClickListener(mOnClickListener);
addView(child);
}
+ mActiveButton = Math.min(mMenu.size() - 1, mActiveButton);
+ mMenu.getItem(mActiveButton).setChecked(true);
}
public void updateMenuView() {
diff --git a/design/src/android/support/design/internal/ForegroundLinearLayout.java b/design/src/android/support/design/internal/ForegroundLinearLayout.java
index 48a04a6..9e690d2 100644
--- a/design/src/android/support/design/internal/ForegroundLinearLayout.java
+++ b/design/src/android/support/design/internal/ForegroundLinearLayout.java
@@ -16,12 +16,14 @@
package android.support.design.internal;
+import android.annotation.TargetApi;
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;
@@ -118,6 +120,8 @@
return super.verifyDrawable(who) || (who == mForeground);
}
+ @RequiresApi(11)
+ @TargetApi(11)
@Override
public void jumpDrawablesToCurrentState() {
super.jumpDrawablesToCurrentState();
@@ -222,6 +226,8 @@
}
}
+ @RequiresApi(21)
+ @TargetApi(21)
@Override
public void drawableHotspotChanged(float x, float y) {
super.drawableHotspotChanged(x, y);
diff --git a/design/src/android/support/design/widget/AppBarLayout.java b/design/src/android/support/design/widget/AppBarLayout.java
index aa76a6b..4a1993d 100644
--- a/design/src/android/support/design/widget/AppBarLayout.java
+++ b/design/src/android/support/design/widget/AppBarLayout.java
@@ -16,6 +16,7 @@
package android.support.design.widget;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
@@ -25,6 +26,7 @@
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;
@@ -319,7 +321,7 @@
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- if (p instanceof LinearLayout.LayoutParams) {
+ 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);
@@ -681,11 +683,17 @@
super(source);
}
+ @RequiresApi(19)
+ @TargetApi(19)
public LayoutParams(LinearLayout.LayoutParams source) {
+ // The copy constructor called here only exists on API 19+.
super(source);
}
+ @RequiresApi(19)
+ @TargetApi(19)
public LayoutParams(LayoutParams source) {
+ // The copy constructor called here only exists on API 19+.
super(source);
mScrollFlags = source.mScrollFlags;
mScrollInterpolator = source.mScrollInterpolator;
diff --git a/design/src/android/support/design/widget/BottomNavigationView.java b/design/src/android/support/design/widget/BottomNavigationView.java
index e82f1fb..e3a81b3 100644
--- a/design/src/android/support/design/widget/BottomNavigationView.java
+++ b/design/src/android/support/design/widget/BottomNavigationView.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
+import android.os.Build;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -25,6 +26,8 @@
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.ViewCompat;
import android.support.v7.content.res.AppCompatResources;
import android.support.v7.view.SupportMenuInflater;
import android.support.v7.view.menu.MenuBuilder;
@@ -35,6 +38,7 @@
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -117,7 +121,7 @@
mPresenter.setBottomNavigationMenuView(mMenuView);
mMenuView.setPresenter(mPresenter);
mMenu.addMenuPresenter(mPresenter);
-
+ mPresenter.initForMenu(getContext(), mMenu);
// Custom attributes
TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
@@ -138,6 +142,10 @@
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);
@@ -148,11 +156,14 @@
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) {
- return mListener != null && mListener.onNavigationItemSelected(item);
+ return mListener != null && !mListener.onNavigationItemSelected(item);
}
@Override
@@ -188,7 +199,6 @@
public void inflateMenu(int resId) {
mPresenter.setUpdateSuspended(true);
getMenuInflater().inflate(resId, mMenu);
- mPresenter.initForMenu(getContext(), mMenu);
mPresenter.setUpdateSuspended(false);
mPresenter.updateMenuView(true);
}
@@ -224,10 +234,13 @@
}
/**
- * Returns the text color used on menu items.
+ * 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
@@ -236,7 +249,8 @@
}
/**
- * Set the text color to be used on menu items.
+ * Set the colors to use for the different states (normal, selected, focused, etc.) of the menu
+ * item text.
*
* @see #getItemTextColor()
*
@@ -279,11 +293,25 @@
*
* @param item The selected item
*
- * @return true to display the item as 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);
}
+ 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());
diff --git a/design/src/android/support/design/widget/CollapsingToolbarLayout.java b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
index 39ad03b..40f2d5e 100644
--- a/design/src/android/support/design/widget/CollapsingToolbarLayout.java
+++ b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
@@ -16,6 +16,7 @@
package android.support.design.widget;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
@@ -30,6 +31,7 @@
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;
@@ -1183,7 +1185,10 @@
super(source);
}
+ @RequiresApi(19)
+ @TargetApi(19)
public LayoutParams(FrameLayout.LayoutParams source) {
+ // The copy constructor called here only exists on API 19+.
super(source);
}
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index 6aafb4f..94b4be3 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -78,6 +78,16 @@
* {@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:
@@ -222,7 +232,7 @@
R.styleable.TextInputLayout_counterOverflowTextAppearance, 0);
mPasswordToggleEnabled = a.getBoolean(
- R.styleable.TextInputLayout_passwordToggleEnabled, true);
+ R.styleable.TextInputLayout_passwordToggleEnabled, false);
mPasswordToggleDrawable = a.getDrawable(R.styleable.TextInputLayout_passwordToggleDrawable);
mPasswordToggleContentDesc = a.getText(
R.styleable.TextInputLayout_passwordToggleContentDescription);
@@ -1056,11 +1066,15 @@
mPasswordToggleView.setVisibility(View.GONE);
}
- // Make sure that we remove the dummy end compound drawable
- final Drawable[] compounds = TextViewCompat.getCompoundDrawablesRelative(mEditText);
- if (compounds[2] == mPasswordToggleDummyDrawable) {
- TextViewCompat.setCompoundDrawablesRelative(mEditText, compounds[0], compounds[1],
- mOriginalEditTextEndDrawable, compounds[3]);
+ 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;
+ }
}
}
}
diff --git a/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java b/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
index 2c9a2a7..9c1c6ca 100644
--- a/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
+++ b/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
@@ -27,21 +27,27 @@
import static org.hamcrest.core.AllOf.allOf;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
import android.content.res.Resources;
import android.support.annotation.ColorInt;
import android.support.design.test.R;
import android.support.design.testutils.TestDrawable;
import android.support.design.testutils.TestUtilsMatchers;
+import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.v4.content.res.ResourcesCompat;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.ViewGroup;
import org.junit.Before;
import org.junit.Test;
@@ -73,6 +79,20 @@
mMenuStringContent.put(R.id.destination_people, res.getString(R.string.navigate_people));
}
+ @UiThreadTest
+ @Test
+ @SmallTest
+ public void testAddItemsWithoutMenuInflation() {
+ BottomNavigationView navigation = new BottomNavigationView(mActivityTestRule.getActivity());
+ mActivityTestRule.getActivity().setContentView(navigation);
+ navigation.getMenu().add("Item1");
+ navigation.getMenu().add("Item2");
+ assertEquals(2, navigation.getMenu().size());
+ navigation.getMenu().removeItem(0);
+ navigation.getMenu().removeItem(0);
+ assertEquals(0, navigation.getMenu().size());
+ }
+
@Test
@SmallTest
public void testBasics() {
@@ -95,22 +115,38 @@
mock(BottomNavigationView.OnNavigationItemSelectedListener.class);
mBottomNavigation.setOnNavigationItemSelectedListener(mockedListener);
- // Click one of our items
+ // Make the listener return true to allow selecting the item.
+ when(mockedListener.onNavigationItemSelected(any(MenuItem.class))).thenReturn(true);
onView(allOf(withText(mMenuStringContent.get(R.id.destination_profile)),
isDescendantOfA(withId(R.id.bottom_navigation)), isDisplayed())).perform(click());
- // And that our listener has been notified of the click
+ // Verify our listener has been notified of the click
verify(mockedListener, times(1)).onNavigationItemSelected(
mBottomNavigation.getMenu().findItem(R.id.destination_profile));
+ // Verify the item is now selected
+ assertTrue(mBottomNavigation.getMenu().findItem(R.id.destination_profile).isChecked());
+
+ // Make the listener return false to disallow selecting the item.
+ when(mockedListener.onNavigationItemSelected(any(MenuItem.class))).thenReturn(false);
+ onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
+ isDescendantOfA(withId(R.id.bottom_navigation)), isDisplayed())).perform(click());
+ // Verify our listener has been notified of the click
+ verify(mockedListener, times(1)).onNavigationItemSelected(
+ mBottomNavigation.getMenu().findItem(R.id.destination_people));
+ // Verify the previous item is still selected
+ assertFalse(mBottomNavigation.getMenu().findItem(R.id.destination_people).isChecked());
+ assertTrue(mBottomNavigation.getMenu().findItem(R.id.destination_profile).isChecked());
// Set null listener to test that the next click is not going to notify the
- // previously set listener
+ // previously set listener and will allow selecting items.
mBottomNavigation.setOnNavigationItemSelectedListener(null);
// Click one of our items
- onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
+ onView(allOf(withText(mMenuStringContent.get(R.id.destination_home)),
isDescendantOfA(withId(R.id.bottom_navigation)), isDisplayed())).perform(click());
// And that our previous listener has not been notified of the click
verifyNoMoreInteractions(mockedListener);
+ // Verify the correct item is now selected.
+ assertTrue(mBottomNavigation.getMenu().findItem(R.id.destination_home).isChecked());
}
@Test
diff --git a/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java b/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
index 9a5d7ed..f38a03f 100644
--- a/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
+++ b/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
@@ -46,6 +46,7 @@
import android.support.test.espresso.matcher.ViewMatchers;
import android.support.test.filters.MediumTest;
import android.support.test.filters.SmallTest;
+import android.support.test.filters.Suppress;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.NestedScrollView;
import android.view.LayoutInflater;
@@ -362,6 +363,7 @@
}
@Test
+ @MediumTest
public void testSkipCollapsed() throws Throwable {
getBehavior().setSkipCollapsed(true);
checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed());
@@ -630,7 +632,10 @@
});
}
+ // Test disabled because it is consistently failing.
+ @Suppress
@Test
+ @MediumTest
public void testFabVisibility() {
withFabVisibilityChange(false, new Runnable() {
@Override
@@ -655,6 +660,7 @@
}
@Test
+ @MediumTest
public void testAutoPeekHeight() throws Throwable {
mActivityTestRule.runOnUiThread(new Runnable() {
@Override
@@ -688,6 +694,7 @@
}
@Test
+ @MediumTest
public void testDynamicContent() throws Throwable {
registerIdlingResourceCallback();
try {
diff --git a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
index 5125f5d..a0fec58 100755
--- a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
@@ -37,6 +37,7 @@
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import android.app.Activity;
@@ -49,7 +50,8 @@
import android.support.test.annotation.UiThreadTest;
import android.support.test.espresso.NoMatchingViewException;
import android.support.test.espresso.ViewAssertion;
-import android.support.test.filters.SmallTest;
+import android.support.v4.widget.TextViewCompat;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.View;
@@ -307,6 +309,68 @@
1, layout.animateToExpansionFractionCount);
}
+ @Test
+ public void testMaintainsLeftRightCompoundDrawables() throws Throwable {
+ final Activity activity = mActivityTestRule.getActivity();
+
+ // Set a known set of test compound drawables on the EditText
+ final Drawable left = new ColorDrawable(Color.RED);
+ final Drawable top = new ColorDrawable(Color.GREEN);
+ final Drawable right = new ColorDrawable(Color.BLUE);
+ final Drawable bottom = new ColorDrawable(Color.BLACK);
+
+ final TextInputEditText editText = new TextInputEditText(activity);
+ editText.setCompoundDrawables(left, top, right, bottom);
+
+ // Now add the EditText to a TextInputLayout
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TextInputLayout til = (TextInputLayout)
+ activity.findViewById(R.id.textinput_noedittext);
+ til.addView(editText);
+ }
+ });
+
+ // Finally assert that all of the drawables are untouched
+ final Drawable[] compoundDrawables = editText.getCompoundDrawables();
+ assertSame(left, compoundDrawables[0]);
+ assertSame(top, compoundDrawables[1]);
+ assertSame(right, compoundDrawables[2]);
+ assertSame(bottom, compoundDrawables[3]);
+ }
+
+ @Test
+ public void testMaintainsStartEndCompoundDrawables() throws Throwable {
+ final Activity activity = mActivityTestRule.getActivity();
+
+ // Set a known set of test compound drawables on the EditText
+ final Drawable start = new ColorDrawable(Color.RED);
+ final Drawable top = new ColorDrawable(Color.GREEN);
+ final Drawable end = new ColorDrawable(Color.BLUE);
+ final Drawable bottom = new ColorDrawable(Color.BLACK);
+
+ final TextInputEditText editText = new TextInputEditText(activity);
+ TextViewCompat.setCompoundDrawablesRelative(editText, start, top, end, bottom);
+
+ // Now add the EditText to a TextInputLayout
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TextInputLayout til = (TextInputLayout)
+ activity.findViewById(R.id.textinput_noedittext);
+ til.addView(editText);
+ }
+ });
+
+ // Finally assert that all of the drawables are untouched
+ final Drawable[] compoundDrawables = TextViewCompat.getCompoundDrawablesRelative(editText);
+ assertSame(start, compoundDrawables[0]);
+ assertSame(top, compoundDrawables[1]);
+ assertSame(end, compoundDrawables[2]);
+ assertSame(bottom, compoundDrawables[3]);
+ }
+
static ViewAssertion isHintExpanded(final boolean expanded) {
return new ViewAssertion() {
@Override
diff --git a/documents-archive/Android.mk b/documents-archive/Android.mk
deleted file mode 100644
index 32ec7d6..0000000
--- a/documents-archive/Android.mk
+++ /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.
-
-LOCAL_PATH := $(call my-dir)
-
-# Here is the final static library that apps can link against.
-# Applications that use this library must specify
-#
-# LOCAL_STATIC_ANDROID_LIBRARIES := \
-# android-support-documents-archive \
-# android-support-v4 \
-# android-support-annotations
-#
-# in their makefiles to include the resources and their dependencies in their package.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-documents-archive
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/src
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations \
- android-support-v4
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/documents-archive/src/android/support/provider/DocumentArchive.java b/documents-archive/src/android/support/provider/DocumentArchive.java
deleted file mode 100644
index 91cd5e7..0000000
--- a/documents-archive/src/android/support/provider/DocumentArchive.java
+++ /dev/null
@@ -1,538 +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.provider;
-
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.graphics.Point;
-import android.media.ExifInterface;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.OperationCanceledException;
-import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsProvider;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.util.Log;
-import android.webkit.MimeTypeMap;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.lang.IllegalArgumentException;
-import java.lang.IllegalStateException;
-import java.lang.UnsupportedOperationException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Stack;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipInputStream;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
-/**
- * Provides basic implementation for creating, extracting and accessing
- * files within archives exposed by a document provider. The id delimiter
- * must be a character which is not used in document ids generated by the
- * document provider.
- *
- * <p>This class is thread safe.
- *
- * @hide
- */
-@RestrictTo(GROUP_ID)
-public class DocumentArchive implements Closeable {
- private static final String TAG = "DocumentArchive";
-
- private static final String[] DEFAULT_PROJECTION = new String[] {
- Document.COLUMN_DOCUMENT_ID,
- Document.COLUMN_DISPLAY_NAME,
- Document.COLUMN_MIME_TYPE,
- Document.COLUMN_SIZE,
- Document.COLUMN_FLAGS
- };
-
- private final Context mContext;
- private final String mDocumentId;
- private final char mIdDelimiter;
- private final Uri mNotificationUri;
- private final ZipFile mZipFile;
- private final ExecutorService mExecutor;
- private final Map<String, ZipEntry> mEntries;
- private final Map<String, List<ZipEntry>> mTree;
-
- private DocumentArchive(
- Context context,
- File file,
- String documentId,
- char idDelimiter,
- @Nullable Uri notificationUri)
- throws IOException {
- mContext = context;
- mDocumentId = documentId;
- mIdDelimiter = idDelimiter;
- mNotificationUri = notificationUri;
- mZipFile = new ZipFile(file);
- mExecutor = Executors.newSingleThreadExecutor();
-
- // Build the tree structure in memory.
- mTree = new HashMap<String, List<ZipEntry>>();
- mTree.put("/", new ArrayList<ZipEntry>());
-
- mEntries = new HashMap<String, ZipEntry>();
- ZipEntry entry;
- final List<? extends ZipEntry> entries = Collections.list(mZipFile.entries());
- final Stack<ZipEntry> stack = new Stack<>();
- for (int i = entries.size() - 1; i >= 0; i--) {
- entry = entries.get(i);
- if (entry.isDirectory() != entry.getName().endsWith("/")) {
- throw new IOException(
- "Directories must have a trailing slash, and files must not.");
- }
- if (mEntries.containsKey(entry.getName())) {
- throw new IOException("Multiple entries with the same name are not supported.");
- }
- mEntries.put(entry.getName(), entry);
- if (entry.isDirectory()) {
- mTree.put(entry.getName(), new ArrayList<ZipEntry>());
- }
- stack.push(entry);
- }
-
- int delimiterIndex;
- String parentPath;
- ZipEntry parentEntry;
- List<ZipEntry> parentList;
-
- while (stack.size() > 0) {
- entry = stack.pop();
-
- delimiterIndex = entry.getName().lastIndexOf('/', entry.isDirectory()
- ? entry.getName().length() - 2 : entry.getName().length() - 1);
- parentPath =
- delimiterIndex != -1 ? entry.getName().substring(0, delimiterIndex) + "/" : "/";
- parentList = mTree.get(parentPath);
-
- if (parentList == null) {
- parentEntry = mEntries.get(parentPath);
- if (parentEntry == null) {
- // The ZIP file doesn't contain all directories leading to the entry.
- // It's rare, but can happen in a valid ZIP archive. In such case create a
- // fake ZipEntry and add it on top of the stack to process it next.
- parentEntry = new ZipEntry(parentPath);
- parentEntry.setSize(0);
- parentEntry.setTime(entry.getTime());
- mEntries.put(parentPath, parentEntry);
- stack.push(parentEntry);
- }
- parentList = new ArrayList<ZipEntry>();
- mTree.put(parentPath, parentList);
- }
-
- parentList.add(entry);
- }
- }
-
- /**
- * Creates a DocumentsArchive instance for opening, browsing and accessing
- * documents within the archive passed as a local file.
- *
- * @param context Context of the provider.
- * @param File Local file containing the archive.
- * @param documentId ID of the archive document.
- * @param idDelimiter Delimiter for constructing IDs of documents within the archive.
- * The delimiter must never be used for IDs of other documents.
- * @param Uri notificationUri Uri for notifying that the archive file has changed.
- * @see createForParcelFileDescriptor(DocumentsProvider, ParcelFileDescriptor, String, char,
- * Uri)
- */
- public static DocumentArchive createForLocalFile(
- Context context, File file, String documentId, char idDelimiter,
- @Nullable Uri notificationUri)
- throws IOException {
- return new DocumentArchive(context, file, documentId, idDelimiter, notificationUri);
- }
-
- /**
- * Creates a DocumentsArchive instance for opening, browsing and accessing
- * documents within the archive passed as a file descriptor.
- *
- * <p>Note, that this method should be used only if the document does not exist
- * on the local storage. A snapshot file will be created, which may be slower
- * and consume significant resources, in contrast to using
- * {@see createForLocalFile(Context, File, String, char, Uri}.
- *
- * @param context Context of the provider.
- * @param descriptor File descriptor for the archive's contents.
- * @param documentId ID of the archive document.
- * @param idDelimiter Delimiter for constructing IDs of documents within the archive.
- * The delimiter must never be used for IDs of other documents.
- * @param Uri notificationUri Uri for notifying that the archive file has changed.
- * @see createForLocalFile(Context, File, String, char, Uri)
- */
- public static DocumentArchive createForParcelFileDescriptor(
- Context context, ParcelFileDescriptor descriptor, String documentId,
- char idDelimiter, @Nullable Uri notificationUri)
- throws IOException {
- File snapshotFile = null;
- try {
- // Create a copy of the archive, as ZipFile doesn't operate on streams.
- // Moreover, ZipInputStream would be inefficient for large files on
- // pipes.
- snapshotFile = File.createTempFile("android.support.provider.snapshot{",
- "}.zip", context.getCacheDir());
-
- try (
- final FileOutputStream outputStream =
- new ParcelFileDescriptor.AutoCloseOutputStream(
- ParcelFileDescriptor.open(
- snapshotFile, ParcelFileDescriptor.MODE_WRITE_ONLY));
- final ParcelFileDescriptor.AutoCloseInputStream inputStream =
- new ParcelFileDescriptor.AutoCloseInputStream(descriptor);
- ) {
- final byte[] buffer = new byte[32 * 1024];
- int bytes;
- while ((bytes = inputStream.read(buffer)) != -1) {
- outputStream.write(buffer, 0, bytes);
- }
- outputStream.flush();
- return new DocumentArchive(context, snapshotFile, documentId, idDelimiter,
- notificationUri);
- }
- } finally {
- // On UNIX the file will be still available for processes which opened it, even
- // after deleting it. Remove it ASAP, as it won't be used by anyone else.
- if (snapshotFile != null) {
- snapshotFile.delete();
- }
- }
- }
-
- /**
- * Lists child documents of an archive or a directory within an
- * archive. Must be called only for archives with supported mime type,
- * or for documents within archives.
- *
- * @see DocumentsProvider.queryChildDocuments(String, String[], String)
- */
- public Cursor queryChildDocuments(String documentId, @Nullable String[] projection,
- @Nullable String sortOrder) throws FileNotFoundException {
- final ParsedDocumentId parsedParentId = ParsedDocumentId.fromDocumentId(
- documentId, mIdDelimiter);
- Preconditions.checkArgumentEquals(mDocumentId, parsedParentId.mArchiveId,
- "Mismatching document ID. Expected: %s, actual: %s.");
-
- final String parentPath = parsedParentId.mPath != null ? parsedParentId.mPath : "/";
- final MatrixCursor result = new MatrixCursor(
- projection != null ? projection : DEFAULT_PROJECTION);
- if (mNotificationUri != null) {
- result.setNotificationUri(mContext.getContentResolver(), mNotificationUri);
- }
-
- final List<ZipEntry> parentList = mTree.get(parentPath);
- if (parentList == null) {
- throw new FileNotFoundException();
- }
- for (final ZipEntry entry : parentList) {
- addCursorRow(result, entry);
- }
- return result;
- }
-
- /**
- * Returns a MIME type of a document within an archive.
- *
- * @see DocumentsProvider.getDocumentType(String)
- */
- public String getDocumentType(String documentId) throws FileNotFoundException {
- final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(
- documentId, mIdDelimiter);
- Preconditions.checkArgumentEquals(mDocumentId, parsedId.mArchiveId,
- "Mismatching document ID. Expected: %s, actual: %s.");
- Preconditions.checkArgumentNotNull(parsedId.mPath, "Not a document within an archive.");
-
- final ZipEntry entry = mEntries.get(parsedId.mPath);
- if (entry == null) {
- throw new FileNotFoundException();
- }
- return getMimeTypeForEntry(entry);
- }
-
- /**
- * Returns true if a document within an archive is a child or any descendant of the archive
- * document or another document within the archive.
- *
- * @see DocumentsProvider.isChildDocument(String, String)
- */
- public boolean isChildDocument(String parentDocumentId, String documentId) {
- final ParsedDocumentId parsedParentId = ParsedDocumentId.fromDocumentId(
- parentDocumentId, mIdDelimiter);
- final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(
- documentId, mIdDelimiter);
- Preconditions.checkArgumentEquals(mDocumentId, parsedParentId.mArchiveId,
- "Mismatching document ID. Expected: %s, actual: %s.");
- Preconditions.checkArgumentNotNull(parsedId.mPath,
- "Not a document within an archive.");
-
- final ZipEntry entry = mEntries.get(parsedId.mPath);
- if (entry == null) {
- return false;
- }
-
- if (parsedParentId.mPath == null) {
- // No need to compare paths. Every file in the archive is a child of the archive
- // file.
- return true;
- }
-
- final ZipEntry parentEntry = mEntries.get(parsedParentId.mPath);
- if (parentEntry == null || !parentEntry.isDirectory()) {
- return false;
- }
-
- final String parentPath = entry.getName();
-
- // Add a trailing slash even if it's not a directory, so it's easy to check if the
- // entry is a descendant.
- final String pathWithSlash = entry.isDirectory() ? entry.getName() : entry.getName() + "/";
- return pathWithSlash.startsWith(parentPath) && !parentPath.equals(pathWithSlash);
- }
-
- /**
- * Returns metadata of a document within an archive.
- *
- * @see DocumentsProvider.queryDocument(String, String[])
- */
- public Cursor queryDocument(String documentId, @Nullable String[] projection)
- throws FileNotFoundException {
- final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(
- documentId, mIdDelimiter);
- Preconditions.checkArgumentEquals(mDocumentId, parsedId.mArchiveId,
- "Mismatching document ID. Expected: %s, actual: %s.");
- Preconditions.checkArgumentNotNull(parsedId.mPath, "Not a document within an archive.");
-
- final ZipEntry entry = mEntries.get(parsedId.mPath);
- if (entry == null) {
- throw new FileNotFoundException();
- }
-
- final MatrixCursor result = new MatrixCursor(
- projection != null ? projection : DEFAULT_PROJECTION);
- if (mNotificationUri != null) {
- result.setNotificationUri(mContext.getContentResolver(), mNotificationUri);
- }
- addCursorRow(result, entry);
- return result;
- }
-
- /**
- * Opens a file within an archive.
- *
- * @see DocumentsProvider.openDocument(String, String, CancellationSignal))
- */
- public ParcelFileDescriptor openDocument(
- String documentId, String mode, @Nullable final CancellationSignal signal)
- throws FileNotFoundException {
- Preconditions.checkArgumentEquals("r", mode,
- "Invalid mode. Only reading \"r\" supported, but got: \"%s\".");
- final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(
- documentId, mIdDelimiter);
- Preconditions.checkArgumentEquals(mDocumentId, parsedId.mArchiveId,
- "Mismatching document ID. Expected: %s, actual: %s.");
- Preconditions.checkArgumentNotNull(parsedId.mPath, "Not a document within an archive.");
-
- final ZipEntry entry = mEntries.get(parsedId.mPath);
- if (entry == null) {
- throw new FileNotFoundException();
- }
-
- ParcelFileDescriptor[] pipe;
- InputStream inputStream = null;
- try {
- pipe = ParcelFileDescriptor.createReliablePipe();
- inputStream = mZipFile.getInputStream(entry);
- } catch (IOException e) {
- if (inputStream != null) {
- IoUtils.closeQuietly(inputStream);
- }
- // Ideally we'd simply throw IOException to the caller, but for consistency
- // with DocumentsProvider::openDocument, converting it to IllegalStateException.
- throw new IllegalStateException("Failed to open the document.", e);
- }
- final ParcelFileDescriptor outputPipe = pipe[1];
- final InputStream finalInputStream = inputStream;
- mExecutor.execute(
- new Runnable() {
- @Override
- public void run() {
- try (final ParcelFileDescriptor.AutoCloseOutputStream outputStream =
- new ParcelFileDescriptor.AutoCloseOutputStream(outputPipe)) {
- try {
- final byte buffer[] = new byte[32 * 1024];
- int bytes;
- while ((bytes = finalInputStream.read(buffer)) != -1) {
- if (Thread.interrupted()) {
- throw new InterruptedException();
- }
- if (signal != null) {
- signal.throwIfCanceled();
- }
- outputStream.write(buffer, 0, bytes);
- }
- } catch (IOException | InterruptedException e) {
- // Catch the exception before the outer try-with-resource closes the
- // pipe with close() instead of closeWithError().
- try {
- outputPipe.closeWithError(e.getMessage());
- } catch (IOException e2) {
- Log.e(TAG, "Failed to close the pipe after an error.", e2);
- }
- }
- } catch (OperationCanceledException e) {
- // Cancelled gracefully.
- } catch (IOException e) {
- Log.e(TAG, "Failed to close the output stream gracefully.", e);
- } finally {
- IoUtils.closeQuietly(finalInputStream);
- }
- }
- });
-
- return pipe[0];
- }
-
- /**
- * Opens a thumbnail of a file within an archive.
- *
- * @see DocumentsProvider.openDocumentThumbnail(String, Point, CancellationSignal))
- */
- public AssetFileDescriptor openDocumentThumbnail(
- String documentId, Point sizeHint, final CancellationSignal signal)
- throws FileNotFoundException {
- final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(documentId, mIdDelimiter);
- Preconditions.checkArgumentEquals(mDocumentId, parsedId.mArchiveId,
- "Mismatching document ID. Expected: %s, actual: %s.");
- Preconditions.checkArgumentNotNull(parsedId.mPath, "Not a document within an archive.");
- Preconditions.checkArgument(getDocumentType(documentId).startsWith("image/"),
- "Thumbnails only supported for image/* MIME type.");
-
- final ZipEntry entry = mEntries.get(parsedId.mPath);
- if (entry == null) {
- throw new FileNotFoundException();
- }
-
- InputStream inputStream = null;
- try {
- inputStream = mZipFile.getInputStream(entry);
- final ExifInterface exif = new ExifInterface(inputStream);
- if (exif.hasThumbnail()) {
- Bundle extras = null;
- switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1)) {
- case ExifInterface.ORIENTATION_ROTATE_90:
- extras = new Bundle(1);
- extras.putInt(DocumentsContract.EXTRA_ORIENTATION, 90);
- break;
- case ExifInterface.ORIENTATION_ROTATE_180:
- extras = new Bundle(1);
- extras.putInt(DocumentsContract.EXTRA_ORIENTATION, 180);
- break;
- case ExifInterface.ORIENTATION_ROTATE_270:
- extras = new Bundle(1);
- extras.putInt(DocumentsContract.EXTRA_ORIENTATION, 270);
- break;
- }
- final long[] range = exif.getThumbnailRange();
- return new AssetFileDescriptor(
- openDocument(documentId, "r", signal), range[0], range[1], extras);
- }
- } catch (IOException e) {
- // Ignore the exception, as reading the EXIF may legally fail.
- Log.e(TAG, "Failed to obtain thumbnail from EXIF.", e);
- } finally {
- IoUtils.closeQuietly(inputStream);
- }
-
- return new AssetFileDescriptor(
- openDocument(documentId, "r", signal), 0, entry.getSize(), null);
- }
-
- /**
- * Schedules a gracefully close of the archive after any opened files are closed.
- *
- * <p>This method does not block until shutdown. Once called, other methods should not be
- * called.
- */
- @Override
- public void close() {
- mExecutor.execute(new Runnable() {
- @Override
- public void run() {
- IoUtils.closeQuietly(mZipFile);
- }
- });
- mExecutor.shutdown();
- }
-
- private void addCursorRow(MatrixCursor cursor, ZipEntry entry) {
- final MatrixCursor.RowBuilder row = cursor.newRow();
- final ParsedDocumentId parsedId = new ParsedDocumentId(mDocumentId, entry.getName());
- row.add(Document.COLUMN_DOCUMENT_ID, parsedId.toDocumentId(mIdDelimiter));
-
- final File file = new File(entry.getName());
- row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
- row.add(Document.COLUMN_SIZE, entry.getSize());
-
- final String mimeType = getMimeTypeForEntry(entry);
- row.add(Document.COLUMN_MIME_TYPE, mimeType);
-
- final int flags = mimeType.startsWith("image/") ? Document.FLAG_SUPPORTS_THUMBNAIL : 0;
- row.add(Document.COLUMN_FLAGS, flags);
- }
-
- private String getMimeTypeForEntry(ZipEntry entry) {
- if (entry.isDirectory()) {
- return Document.MIME_TYPE_DIR;
- }
-
- final int lastDot = entry.getName().lastIndexOf('.');
- if (lastDot >= 0) {
- final String extension = entry.getName().substring(lastDot + 1).toLowerCase(Locale.US);
- final String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
- if (mimeType != null) {
- return mimeType;
- }
- }
-
- return "application/octet-stream";
- }
-};
diff --git a/documents-archive/src/android/support/provider/DocumentArchiveHelper.java b/documents-archive/src/android/support/provider/DocumentArchiveHelper.java
deleted file mode 100644
index fd5ff94..0000000
--- a/documents-archive/src/android/support/provider/DocumentArchiveHelper.java
+++ /dev/null
@@ -1,373 +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.provider;
-
-import android.content.res.AssetFileDescriptor;
-import android.content.res.Configuration;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.graphics.Point;
-import android.net.Uri;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsProvider;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.util.Log;
-import android.util.LruCache;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
-/**
- * Provides basic implementation for creating, extracting and accessing
- * files within archives exposed by a document provider.
- *
- * <p>This class is thread safe. All methods can be called on any thread without
- * synchronization.
- *
- * TODO: Update the documentation. b/26047732
- * @hide
- */
-@RestrictTo(GROUP_ID)
-public class DocumentArchiveHelper implements Closeable {
- /**
- * Cursor column to be used for passing the local file path for documents.
- * If it's not specified, then a snapshot will be created, which is slower
- * and consumes more resources.
- *
- * <p>Type: STRING
- */
- public static final String COLUMN_LOCAL_FILE_PATH = "local_file_path";
-
- private static final String TAG = "DocumentArchiveHelper";
- private static final int OPENED_ARCHIVES_CACHE_SIZE = 4;
- private static final String[] ZIP_MIME_TYPES = {
- "application/zip", "application/x-zip", "application/x-zip-compressed"
- };
-
- private final DocumentsProvider mProvider;
- private final char mIdDelimiter;
-
- // @GuardedBy("mArchives")
- private final LruCache<String, Loader> mArchives =
- new LruCache<String, Loader>(OPENED_ARCHIVES_CACHE_SIZE) {
- @Override
- public void entryRemoved(boolean evicted, String key,
- Loader oldValue, Loader newValue) {
- oldValue.getWriteLock().lock();
- try {
- oldValue.get().close();
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Failed to close an archive as it no longer exists.");
- } finally {
- oldValue.getWriteLock().unlock();
- }
- }
- };
-
- /**
- * Creates a helper for handling archived documents.
- *
- * @param provider Instance of a documents provider which provides archived documents.
- * @param idDelimiter A character used to create document IDs within archives. Can be any
- * character which is not used in any other document ID. If your provider uses
- * numbers as document IDs, the delimiter can be eg. a colon. However if your
- * provider uses paths, then a delimiter can be any character not allowed in the
- * path, which is often \0.
- */
- public DocumentArchiveHelper(DocumentsProvider provider, char idDelimiter) {
- mProvider = provider;
- mIdDelimiter = idDelimiter;
- }
-
- /**
- * Lists child documents of an archive or a directory within an
- * archive. Must be called only for archives with supported mime type,
- * or for documents within archives.
- *
- * @see DocumentsProvider.queryChildDocuments(String, String[], String)
- */
- public Cursor queryChildDocuments(String documentId, @Nullable String[] projection,
- @Nullable String sortOrder)
- throws FileNotFoundException {
- Loader loader = null;
- try {
- loader = obtainInstance(documentId);
- return loader.get().queryChildDocuments(documentId, projection, sortOrder);
- } finally {
- releaseInstance(loader);
- }
- }
-
- /**
- * Returns a MIME type of a document within an archive.
- *
- * @see DocumentsProvider.getDocumentType(String)
- */
- public String getDocumentType(String documentId) throws FileNotFoundException {
- Loader loader = null;
- try {
- loader = obtainInstance(documentId);
- return loader.get().getDocumentType(documentId);
- } finally {
- releaseInstance(loader);
- }
- }
-
- /**
- * Returns true if a document within an archive is a child or any descendant of the archive
- * document or another document within the archive.
- *
- * @see DocumentsProvider.isChildDocument(String, String)
- */
- public boolean isChildDocument(String parentDocumentId, String documentId) {
- Loader loader = null;
- try {
- loader = obtainInstance(documentId);
- return loader.get().isChildDocument(parentDocumentId, documentId);
- } catch (FileNotFoundException e) {
- throw new IllegalStateException(e);
- } finally {
- releaseInstance(loader);
- }
- }
-
- /**
- * Returns metadata of a document within an archive.
- *
- * @see DocumentsProvider.queryDocument(String, String[])
- */
- public Cursor queryDocument(String documentId, @Nullable String[] projection)
- throws FileNotFoundException {
- Loader loader = null;
- try {
- loader = obtainInstance(documentId);
- return loader.get().queryDocument(documentId, projection);
- } finally {
- releaseInstance(loader);
- }
- }
-
- /**
- * Opens a file within an archive.
- *
- * @see DocumentsProvider.openDocument(String, String, CancellationSignal))
- */
- public ParcelFileDescriptor openDocument(
- String documentId, String mode, final CancellationSignal signal)
- throws FileNotFoundException {
- Loader loader = null;
- try {
- loader = obtainInstance(documentId);
- return loader.get().openDocument(documentId, mode, signal);
- } finally {
- releaseInstance(loader);
- }
- }
-
- /**
- * Opens a thumbnail of a file within an archive.
- *
- * @see DocumentsProvider.openDocumentThumbnail(String, Point, CancellationSignal))
- */
- public AssetFileDescriptor openDocumentThumbnail(
- String documentId, Point sizeHint, final CancellationSignal signal)
- throws FileNotFoundException {
- Loader loader = null;
- try {
- loader = obtainInstance(documentId);
- return loader.get().openDocumentThumbnail(documentId, sizeHint, signal);
- } finally {
- releaseInstance(loader);
- }
- }
-
- /**
- * Returns true if the passed document ID is for a document within an archive.
- */
- public boolean isArchivedDocument(String documentId) {
- return ParsedDocumentId.hasPath(documentId, mIdDelimiter);
- }
-
- /**
- * Returns true if the passed mime type is supported by the helper.
- */
- public boolean isSupportedArchiveType(String mimeType) {
- for (final String zipMimeType : ZIP_MIME_TYPES) {
- if (zipMimeType.equals(mimeType)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Closes the helper and disposes all existing archives. It will block until all ongoing
- * operations on each opened archive are finished.
- */
- @Override
- public void close() {
- synchronized (mArchives) {
- mArchives.evictAll();
- }
- }
-
- /**
- * Releases resources for an archive with the specified document ID. It will block until all
- * operations on the archive are finished. If not opened, the method does nothing.
- *
- * <p>Calling this method is optional. The helper automatically closes the least recently used
- * archives if too many archives are opened.
- *
- * @param archiveDocumentId ID of the archive file.
- */
- public void closeArchive(String documentId) {
- synchronized (mArchives) {
- mArchives.remove(documentId);
- }
- }
-
- private Loader obtainInstance(String documentId) throws FileNotFoundException {
- Loader loader;
- synchronized (mArchives) {
- loader = getInstanceUncheckedLocked(documentId);
- loader.getReadLock().lock();
- }
- return loader;
- }
-
- private void releaseInstance(@Nullable Loader loader) {
- if (loader != null) {
- loader.getReadLock().unlock();
- }
- }
-
- private Loader getInstanceUncheckedLocked(String documentId)
- throws FileNotFoundException {
- try {
- final ParsedDocumentId id = ParsedDocumentId.fromDocumentId(documentId, mIdDelimiter);
- if (mArchives.get(id.mArchiveId) != null) {
- return mArchives.get(id.mArchiveId);
- }
-
- final Cursor cursor = mProvider.queryDocument(id.mArchiveId, new String[]
- { Document.COLUMN_MIME_TYPE, COLUMN_LOCAL_FILE_PATH });
- cursor.moveToFirst();
- final String mimeType = cursor.getString(cursor.getColumnIndex(
- Document.COLUMN_MIME_TYPE));
- Preconditions.checkArgument(isSupportedArchiveType(mimeType),
- "Unsupported archive type.");
- final int columnIndex = cursor.getColumnIndex(COLUMN_LOCAL_FILE_PATH);
- final String localFilePath = columnIndex != -1 ? cursor.getString(columnIndex) : null;
- final File localFile = localFilePath != null ? new File(localFilePath) : null;
- final Uri notificationUri = cursor.getNotificationUri();
- final Loader loader = new Loader(mProvider, localFile, id, mIdDelimiter,
- notificationUri);
-
- // Remove the instance from mArchives collection once the archive file changes.
- if (notificationUri != null) {
- final LruCache<String, Loader> finalArchives = mArchives;
- mProvider.getContext().getContentResolver().registerContentObserver(notificationUri,
- false,
- new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- synchronized (mArchives) {
- final Loader currentLoader = mArchives.get(id.mArchiveId);
- if (currentLoader == loader) {
- mArchives.remove(id.mArchiveId);
- }
- }
- }
- });
- }
-
- mArchives.put(id.mArchiveId, loader);
- return loader;
- } catch (IOException e) {
- // DocumentsProvider doesn't use IOException. For consistency convert it to
- // IllegalStateException.
- throw new IllegalStateException(e);
- }
- }
-
- /**
- * Loads an instance of DocumentArchive lazily.
- */
- private static final class Loader {
- private final DocumentsProvider mProvider;
- private final File mLocalFile;
- private final ParsedDocumentId mId;
- private final char mIdDelimiter;
- private final Uri mNotificationUri;
- private final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
- private DocumentArchive mArchive = null;
-
- Loader(DocumentsProvider provider, @Nullable File localFile, ParsedDocumentId id,
- char idDelimiter, Uri notificationUri) {
- this.mProvider = provider;
- this.mLocalFile = localFile;
- this.mId = id;
- this.mIdDelimiter = idDelimiter;
- this.mNotificationUri = notificationUri;
- }
-
- synchronized DocumentArchive get() throws FileNotFoundException {
- if (mArchive != null) {
- return mArchive;
- }
-
- try {
- if (mLocalFile != null) {
- mArchive = DocumentArchive.createForLocalFile(
- mProvider.getContext(), mLocalFile, mId.mArchiveId, mIdDelimiter,
- mNotificationUri);
- } else {
- mArchive = DocumentArchive.createForParcelFileDescriptor(
- mProvider.getContext(),
- mProvider.openDocument(mId.mArchiveId, "r", null /* signal */),
- mId.mArchiveId, mIdDelimiter, mNotificationUri);
- }
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
-
- return mArchive;
- }
-
- Lock getReadLock() {
- return mLock.readLock();
- }
-
- Lock getWriteLock() {
- return mLock.writeLock();
- }
- }
-}
diff --git a/documents-archive/src/android/support/provider/IoUtils.java b/documents-archive/src/android/support/provider/IoUtils.java
deleted file mode 100644
index 4806575..0000000
--- a/documents-archive/src/android/support/provider/IoUtils.java
+++ /dev/null
@@ -1,57 +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.provider;
-
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-
-import java.io.Closeable;
-import java.io.InputStream;
-import java.util.Collection;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
-/**
- * Simple static methods to perform common IO operations.
- * @hide
- */
-@RestrictTo(GROUP_ID)
-final class IoUtils {
- static void closeQuietly(@Nullable Closeable closeable) {
- if (closeable != null) {
- try {
- closeable.close();
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- // Ignore.
- }
- }
- }
-
- static void closeQuietly(@Nullable InputStream stream) {
- if (stream != null) {
- try {
- stream.close();
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- // Ignore.
- }
- }
- }
-}
diff --git a/documents-archive/src/android/support/provider/ParsedDocumentId.java b/documents-archive/src/android/support/provider/ParsedDocumentId.java
deleted file mode 100644
index 2834455..0000000
--- a/documents-archive/src/android/support/provider/ParsedDocumentId.java
+++ /dev/null
@@ -1,57 +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.provider;
-
-import android.support.annotation.RestrictTo;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
-/**
- * @hide
- */
-@RestrictTo(GROUP_ID)
-class ParsedDocumentId {
- public final String mArchiveId;
- public final String mPath;
-
- public ParsedDocumentId(String archiveId, String path) {
- mArchiveId = archiveId;
- mPath = path;
- }
-
- static public ParsedDocumentId fromDocumentId(String documentId, char idDelimiter) {
- final int delimiterPosition = documentId.indexOf(idDelimiter);
- if (delimiterPosition == -1) {
- return new ParsedDocumentId(documentId, null);
- } else {
- return new ParsedDocumentId(documentId.substring(0, delimiterPosition),
- documentId.substring((delimiterPosition + 1)));
- }
- }
-
- static public boolean hasPath(String documentId, char idDelimiter) {
- return documentId.indexOf(idDelimiter) != -1;
- }
-
- public String toDocumentId(char idDelimiter) {
- if (mPath == null) {
- return mArchiveId;
- } else {
- return mArchiveId + idDelimiter + mPath;
- }
- }
-};
diff --git a/documents-archive/src/android/support/provider/Preconditions.java b/documents-archive/src/android/support/provider/Preconditions.java
deleted file mode 100644
index 050ca9a..0000000
--- a/documents-archive/src/android/support/provider/Preconditions.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.provider;
-
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.text.TextUtils;
-
-import java.util.Collection;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
-/**
- * Simple static methods to be called at the start of your own methods to verify
- * correct arguments and state.
- * @hide
- */
-@RestrictTo(GROUP_ID)
-final class Preconditions {
- static void checkArgument(boolean expression, String message) {
- if (!expression) {
- throw new IllegalArgumentException(message);
- }
- }
-
- static void checkArgumentNotNull(Object object, String message) {
- if (object == null) {
- throw new IllegalArgumentException(message);
- }
- }
-
- static void checkArgumentEquals(String expected, @Nullable String actual, String message) {
- if (!TextUtils.equals(expected, actual)) {
- throw new IllegalArgumentException(String.format(message, String.valueOf(expected),
- String.valueOf(actual)));
- }
- }
-
- static void checkState(boolean expression, String message) {
- if (!expression) {
- throw new IllegalStateException(message);
- }
- }
-}
diff --git a/documents-archive/tests/Android.mk b/documents-archive/tests/Android.mk
deleted file mode 100644
index 84cc3c3..0000000
--- a/documents-archive/tests/Android.mk
+++ /dev/null
@@ -1,29 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
- android-support-documents-archive
-LOCAL_AAPT_FLAGS := --auto-add-overlay -0 zip
-LOCAL_PACKAGE_NAME := AndroidSupportDocumentsArchiveTests
-
-include $(BUILD_PACKAGE)
diff --git a/documents-archive/tests/AndroidManifest.xml b/documents-archive/tests/AndroidManifest.xml
deleted file mode 100644
index 47da733..0000000
--- a/documents-archive/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.support.provider.tests">
- <application>
- <uses-library android:name="android.test.runner" />
- <provider
- android:name="android.support.provider.tests.StubProvider"
- android:authorities="android.support.provider.tests.mystubprovider"
- android:grantUriPermissions="true"
- android:exported="true"
- android:permission="android.permission.MANAGE_DOCUMENTS" />
- </application>
-
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="android.support.provider.tests"
- android:label="Tests for android.support.provider." />
-</manifest>
diff --git a/documents-archive/tests/NO_DOCS b/documents-archive/tests/NO_DOCS
deleted file mode 100644
index e69de29..0000000
--- a/documents-archive/tests/NO_DOCS
+++ /dev/null
diff --git a/documents-archive/tests/res/raw/archive.zip b/documents-archive/tests/res/raw/archive.zip
deleted file mode 100644
index c3b8d22..0000000
--- a/documents-archive/tests/res/raw/archive.zip
+++ /dev/null
Binary files differ
diff --git a/documents-archive/tests/res/raw/empty_dirs.zip b/documents-archive/tests/res/raw/empty_dirs.zip
deleted file mode 100644
index 1dd2251..0000000
--- a/documents-archive/tests/res/raw/empty_dirs.zip
+++ /dev/null
Binary files differ
diff --git a/documents-archive/tests/res/raw/no_dirs.zip b/documents-archive/tests/res/raw/no_dirs.zip
deleted file mode 100644
index e178ae1..0000000
--- a/documents-archive/tests/res/raw/no_dirs.zip
+++ /dev/null
Binary files differ
diff --git a/documents-archive/tests/src/android/support/provider/DocumentArchiveTest.java b/documents-archive/tests/src/android/support/provider/DocumentArchiveTest.java
deleted file mode 100644
index 9845412..0000000
--- a/documents-archive/tests/src/android/support/provider/DocumentArchiveTest.java
+++ /dev/null
@@ -1,273 +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.provider.tests;
-
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract.Document;
-import android.support.provider.DocumentArchive;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Scanner;
-
-/**
- * Tests for DocumentArchive.
- */
-public class DocumentArchiveTest extends AndroidTestCase {
- private static final String DOCUMENT_ID = "document-id";
- private static final char DELIMITER = ':';
- private static final String NOTIFICATION_URI = "content://notification-uri";
- private DocumentArchive mArchive = null;
-
- public void loadArchive(int resource) {
- // Extract the file from resources.
- File file = null;
- try {
- file = File.createTempFile("android.support.provider.tests{",
- "}.zip", mContext.getCacheDir());
- try (
- final FileOutputStream outputStream =
- new ParcelFileDescriptor.AutoCloseOutputStream(
- ParcelFileDescriptor.open(
- file, ParcelFileDescriptor.MODE_WRITE_ONLY));
- final InputStream inputStream =
- mContext.getResources().openRawResource(resource);
- ) {
- final byte[] buffer = new byte[32 * 1024];
- int bytes;
- while ((bytes = inputStream.read(buffer)) != -1) {
- outputStream.write(buffer, 0, bytes);
- }
- outputStream.flush();
- mArchive = DocumentArchive.createForLocalFile(
- mContext,
- file,
- DOCUMENT_ID,
- DELIMITER,
- Uri.parse(NOTIFICATION_URI));
-
- }
- } catch (IOException e) {
- fail(String.valueOf(e));
- } finally {
- // On UNIX the file will be still available for processes which opened it, even
- // after deleting it. Remove it ASAP, as it won't be used by anyone else.
- if (file != null) {
- file.delete();
- }
- }
- }
-
- @Override
- public void tearDown() {
- if (mArchive != null) {
- mArchive.close();
- }
- }
-
- public void testQueryChildDocument() throws IOException {
- loadArchive(R.raw.archive);
- final Cursor cursor = mArchive.queryChildDocuments(DOCUMENT_ID, null, null);
-
- assertTrue(cursor.moveToFirst());
- assertEquals("document-id:dir1/",
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
- assertEquals("dir1",
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
- assertEquals(Document.MIME_TYPE_DIR,
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
- assertEquals(0,
- cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-
- assertTrue(cursor.moveToNext());
- assertEquals("document-id:dir2/",
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
- assertEquals("dir2",
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
- assertEquals(Document.MIME_TYPE_DIR,
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
- assertEquals(0,
- cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-
- assertTrue(cursor.moveToNext());
- assertEquals("document-id:file1.txt",
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
- assertEquals("file1.txt",
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
- assertEquals("text/plain",
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
- assertEquals(13,
- cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-
- assertFalse(cursor.moveToNext());
-
- // Check if querying children works too.
- final Cursor childCursor = mArchive.queryChildDocuments("document-id:dir1/", null, null);
-
- assertTrue(childCursor.moveToFirst());
- assertEquals("document-id:dir1/cherries.txt",
- childCursor.getString(childCursor.getColumnIndexOrThrow(
- Document.COLUMN_DOCUMENT_ID)));
- assertEquals("cherries.txt",
- childCursor.getString(childCursor.getColumnIndexOrThrow(
- Document.COLUMN_DISPLAY_NAME)));
- assertEquals("text/plain",
- childCursor.getString(childCursor.getColumnIndexOrThrow(
- Document.COLUMN_MIME_TYPE)));
- assertEquals(17,
- childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
- }
-
- public void testQueryChildDocument_NoDirs() throws IOException {
- loadArchive(R.raw.no_dirs);
- final Cursor cursor = mArchive.queryChildDocuments(DOCUMENT_ID, null, null);
-
- assertTrue(cursor.moveToFirst());
- assertEquals("document-id:dir1/",
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
- assertEquals("dir1",
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
- assertEquals(Document.MIME_TYPE_DIR,
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
- assertEquals(0,
- cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
- assertFalse(cursor.moveToNext());
-
- final Cursor childCursor = mArchive.queryChildDocuments("document-id:dir1/", null, null);
-
- assertTrue(childCursor.moveToFirst());
- assertEquals("document-id:dir1/dir2/",
- childCursor.getString(childCursor.getColumnIndexOrThrow(
- Document.COLUMN_DOCUMENT_ID)));
- assertEquals("dir2",
- childCursor.getString(childCursor.getColumnIndexOrThrow(
- Document.COLUMN_DISPLAY_NAME)));
- assertEquals(Document.MIME_TYPE_DIR,
- childCursor.getString(childCursor.getColumnIndexOrThrow(
- Document.COLUMN_MIME_TYPE)));
- assertEquals(0,
- childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
- assertFalse(childCursor.moveToNext());
-
- final Cursor childCursor2 = mArchive.queryChildDocuments(
- "document-id:dir1/dir2/", null, null);
-
- assertTrue(childCursor2.moveToFirst());
- assertEquals("document-id:dir1/dir2/cherries.txt",
- childCursor2.getString(childCursor.getColumnIndexOrThrow(
- Document.COLUMN_DOCUMENT_ID)));
- assertFalse(childCursor2.moveToNext());
- }
-
- public void testQueryChildDocument_EmptyDirs() throws IOException {
- loadArchive(R.raw.empty_dirs);
- final Cursor cursor = mArchive.queryChildDocuments(DOCUMENT_ID, null, null);
-
- assertTrue(cursor.moveToFirst());
- assertEquals("document-id:dir1/",
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
- assertEquals("dir1",
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
- assertEquals(Document.MIME_TYPE_DIR,
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
- assertEquals(0,
- cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
- assertFalse(cursor.moveToNext());
-
- final Cursor childCursor = mArchive.queryChildDocuments("document-id:dir1/", null, null);
-
- assertTrue(childCursor.moveToFirst());
- assertEquals("document-id:dir1/dir2/",
- childCursor.getString(childCursor.getColumnIndexOrThrow(
- Document.COLUMN_DOCUMENT_ID)));
- assertEquals("dir2",
- childCursor.getString(childCursor.getColumnIndexOrThrow(
- Document.COLUMN_DISPLAY_NAME)));
- assertEquals(Document.MIME_TYPE_DIR,
- childCursor.getString(childCursor.getColumnIndexOrThrow(
- Document.COLUMN_MIME_TYPE)));
- assertEquals(0,
- childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-
- assertTrue(childCursor.moveToNext());
- assertEquals("document-id:dir1/dir3/",
- childCursor.getString(childCursor.getColumnIndexOrThrow(
- Document.COLUMN_DOCUMENT_ID)));
- assertEquals("dir3",
- childCursor.getString(childCursor.getColumnIndexOrThrow(
- Document.COLUMN_DISPLAY_NAME)));
- assertEquals(Document.MIME_TYPE_DIR,
- childCursor.getString(childCursor.getColumnIndexOrThrow(
- Document.COLUMN_MIME_TYPE)));
- assertEquals(0,
- childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
- assertFalse(cursor.moveToNext());
-
- final Cursor childCursor2 = mArchive.queryChildDocuments(
- "document-id:dir1/dir2/", null, null);
- assertFalse(childCursor2.moveToFirst());
-
- final Cursor childCursor3 = mArchive.queryChildDocuments(
- "document-id:dir1/dir3/", null, null);
- assertFalse(childCursor3.moveToFirst());
- }
-
- public void testGetDocumentType() throws IOException {
- loadArchive(R.raw.archive);
- assertEquals(Document.MIME_TYPE_DIR, mArchive.getDocumentType("document-id:dir1/"));
- assertEquals("text/plain", mArchive.getDocumentType("document-id:file1.txt"));
- }
-
- public void testIsChildDocument() throws IOException {
- loadArchive(R.raw.archive);
- assertTrue(mArchive.isChildDocument(DOCUMENT_ID, "document-id:dir1/"));
- assertFalse(mArchive.isChildDocument(DOCUMENT_ID, "document-id:this-does-not-exist"));
- assertTrue(mArchive.isChildDocument("document-id:dir1/", "document-id:dir1/cherries.txt"));
- assertTrue(mArchive.isChildDocument(DOCUMENT_ID, "document-id:dir1/cherries.txt"));
- }
-
- public void testQueryDocument() throws IOException {
- loadArchive(R.raw.archive);
- final Cursor cursor = mArchive.queryDocument("document-id:dir2/strawberries.txt", null);
-
- assertTrue(cursor.moveToFirst());
- assertEquals("document-id:dir2/strawberries.txt",
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
- assertEquals("strawberries.txt",
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
- assertEquals("text/plain",
- cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
- assertEquals(21,
- cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
- }
-
- public void testOpenDocument() throws IOException {
- loadArchive(R.raw.archive);
- final ParcelFileDescriptor descriptor = mArchive.openDocument(
- "document-id:dir2/strawberries.txt", "r", null /* signal */);
- try (final ParcelFileDescriptor.AutoCloseInputStream inputStream =
- new ParcelFileDescriptor.AutoCloseInputStream(descriptor)) {
- assertEquals("I love strawberries!", new Scanner(inputStream).nextLine());
- }
- }
-}
diff --git a/documents-archive/tests/src/android/support/provider/IntegrationTest.java b/documents-archive/tests/src/android/support/provider/IntegrationTest.java
deleted file mode 100644
index 0445d82..0000000
--- a/documents-archive/tests/src/android/support/provider/IntegrationTest.java
+++ /dev/null
@@ -1,136 +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.provider.tests;
-
-import android.content.ContentProviderClient;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsContract;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.IllegalArgumentException;
-import java.util.Scanner;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Integration tests for DocumentsProvider and DocumentArchiveHelper.
- *
- * <p>Only checks if the provider, then helper are forwarding the calls to the
- * underlying {@code ArchiveDocument} correctly. More detailed output testing is
- * done in {@code DocumentArchiveTest}.
- */
-public class IntegrationTest extends AndroidTestCase {
- private ContentProviderClient mClient;
-
- @Override
- public void setUp() throws RemoteException {
- mClient = getContext().getContentResolver().acquireContentProviderClient(
- StubProvider.AUTHORITY);
- assertNotNull(mClient);
- mClient.call("reset", null, null);
- }
-
- @Override
- public void tearDown() {
- if (mClient != null) {
- mClient.release();
- mClient = null;
- }
- }
-
- public void testQueryForChildren() throws IOException {
- final Cursor cursor = mContext.getContentResolver().query(
- DocumentsContract.buildChildDocumentsUri(
- StubProvider.AUTHORITY, StubProvider.DOCUMENT_ID),
- null, null, null, null);
- assertEquals(3, cursor.getCount());
- }
-
- public void testQueryForDocument_Archive()
- throws IOException, RemoteException, InterruptedException {
- final Cursor cursor = mContext.getContentResolver().query(
- DocumentsContract.buildDocumentUri(
- StubProvider.AUTHORITY, StubProvider.DOCUMENT_ID),
- null, null, null, null);
- assertEquals(1, cursor.getCount());
- assertTrue(cursor.moveToFirst());
- assertEquals(Document.FLAG_ARCHIVE,
- cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_FLAGS)));
- }
-
- public void testQueryForDocument_ArchiveDescendant()
- throws IOException, RemoteException, InterruptedException {
- final Cursor cursor = mContext.getContentResolver().query(
- DocumentsContract.buildDocumentUri(
- StubProvider.AUTHORITY, StubProvider.FILE_DOCUMENT_ID),
- null, null, null, null);
- assertEquals(1, cursor.getCount());
- assertEquals(StubProvider.NOTIFY_URI, cursor.getNotificationUri());
-
- final CountDownLatch changeSignal = new CountDownLatch(1);
- final ContentObserver observer = new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange) {
- changeSignal.countDown();
- }
- };
-
- try {
- getContext().getContentResolver().registerContentObserver(
- cursor.getNotificationUri(), false /* notifyForDescendants */, observer);
-
- // Simulate deleting the archive file, then confirm that the notification is
- // propagated and the archive closed.
- mClient.call("delete", null, null);
- changeSignal.await();
-
- mContext.getContentResolver().query(
- DocumentsContract.buildChildDocumentsUri(
- StubProvider.AUTHORITY, StubProvider.FILE_DOCUMENT_ID),
- null, null, null, null);
- fail("Expected IllegalStateException, but succeeded.");
- } catch (IllegalStateException e) {
- // Expected, as the file is gone.
- } finally {
- getContext().getContentResolver().unregisterContentObserver(observer);
- }
- }
-
- public void testGetType() throws IOException {
- assertEquals("text/plain", mContext.getContentResolver().getType(
- DocumentsContract.buildDocumentUri(
- StubProvider.AUTHORITY, StubProvider.FILE_DOCUMENT_ID)));
- }
-
- public void testOpenFileDescriptor() throws IOException {
- final ParcelFileDescriptor descriptor = mContext.getContentResolver().openFileDescriptor(
- DocumentsContract.buildDocumentUri(
- StubProvider.AUTHORITY, StubProvider.FILE_DOCUMENT_ID),
- "r", null);
- assertNotNull(descriptor);
- }
-}
diff --git a/documents-archive/tests/src/android/support/provider/StubProvider.java b/documents-archive/tests/src/android/support/provider/StubProvider.java
deleted file mode 100644
index 3f72cd2..0000000
--- a/documents-archive/tests/src/android/support/provider/StubProvider.java
+++ /dev/null
@@ -1,154 +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.provider.tests;
-
-import android.database.Cursor;
-import android.database.MatrixCursor.RowBuilder;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsProvider;
-import android.support.provider.DocumentArchiveHelper;
-import android.util.Log;
-
-import java.io.FileNotFoundException;
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Stub provider for testing support for archives.
- */
-public class StubProvider extends DocumentsProvider {
- public static final String AUTHORITY = "android.support.provider.tests.mystubprovider";
- public static final String DOCUMENT_ID = "document-id";
- public static final String FILE_DOCUMENT_ID = "document-id:dir1/cherries.txt";
- public static final Uri NOTIFY_URI = DocumentsContract.buildRootsUri(AUTHORITY);
-
- private static final String TAG = "StubProvider";
- private static final String[] DEFAULT_PROJECTION = new String[] {
- Document.COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME, Document.COLUMN_SIZE,
- Document.COLUMN_MIME_TYPE, Document.COLUMN_FLAGS,
- DocumentArchiveHelper.COLUMN_LOCAL_FILE_PATH
- };
-
- public File file;
- public DocumentArchiveHelper archiveHelper;
- public boolean simulatedDelete = false;
-
- @Override
- public Bundle call(String method, String args, Bundle extras) {
- switch (method) {
- case "reset":
- simulatedDelete = false;
- getContext().getContentResolver().notifyChange(NOTIFY_URI, null);
- return null;
- case "delete":
- simulatedDelete = true;
- getContext().getContentResolver().notifyChange(NOTIFY_URI, null);
- return null;
- default:
- return super.call(method, args, extras);
- }
- }
-
- @Override
- public boolean onCreate() {
- try {
- archiveHelper = new DocumentArchiveHelper(this, ':');
- file = TestUtils.createFileFromResource(getContext(), R.raw.archive);
- return true;
- } catch (IOException e) {
- Log.e(TAG, "Failed to initialize StubProvider.");
- return false;
- }
- }
-
- @Override
- public ParcelFileDescriptor openDocument(
- String documentId, String mode, CancellationSignal signal)
- throws FileNotFoundException {
- if (archiveHelper.isArchivedDocument(documentId)) {
- return archiveHelper.openDocument(documentId, mode, signal);
- }
-
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Cursor queryChildDocuments(
- String parentDocumentId, String[] projection, String sortOrder)
- throws FileNotFoundException {
- if (archiveHelper.isArchivedDocument(parentDocumentId) ||
- archiveHelper.isSupportedArchiveType(getDocumentType(parentDocumentId))) {
- return archiveHelper.queryChildDocuments(parentDocumentId, projection, sortOrder);
- }
-
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Cursor queryDocument(String documentId, String[] projection)
- throws FileNotFoundException {
- if (archiveHelper.isArchivedDocument(documentId)) {
- return archiveHelper.queryDocument(documentId, projection);
- }
-
- if (DOCUMENT_ID.equals(documentId)) {
- if (simulatedDelete) {
- throw new FileNotFoundException();
- }
-
- final MatrixCursor result = new MatrixCursor(
- projection != null ? projection : DEFAULT_PROJECTION);
- result.setNotificationUri(getContext().getContentResolver(), NOTIFY_URI);
- final RowBuilder row = result.newRow();
- row.add(Document.COLUMN_DOCUMENT_ID, DOCUMENT_ID);
- row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
- row.add(Document.COLUMN_SIZE, file.length());
- row.add(Document.COLUMN_MIME_TYPE, "application/zip");
- final int flags = archiveHelper.isSupportedArchiveType("application/zip")
- ? Document.FLAG_ARCHIVE : 0;
- row.add(Document.COLUMN_FLAGS, flags);
- row.add(DocumentArchiveHelper.COLUMN_LOCAL_FILE_PATH, file.getPath());
- return result;
- }
-
- throw new FileNotFoundException();
- }
-
- @Override
- public Cursor queryRoots(String[] projection) throws FileNotFoundException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String getDocumentType(String documentId) throws FileNotFoundException {
- if (archiveHelper.isArchivedDocument(documentId)) {
- return archiveHelper.getDocumentType(documentId);
- }
-
- if (DOCUMENT_ID.equals(documentId)) {
- return "application/zip";
- }
-
- throw new UnsupportedOperationException();
- }
-}
diff --git a/documents-archive/tests/src/android/support/provider/TestUtils.java b/documents-archive/tests/src/android/support/provider/TestUtils.java
deleted file mode 100644
index 17ec3e1..0000000
--- a/documents-archive/tests/src/android/support/provider/TestUtils.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.provider.tests;
-
-import android.content.Context;
-import android.os.ParcelFileDescriptor;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Utilities for tests.
- */
-final class TestUtils {
- /**
- * Saves a file from resources to a temporary location and returns a File instance for it.
- *
- * @param id Resource ID
- */
- static File createFileFromResource(Context context, int id) throws IOException {
- final File file = File.createTempFile("android.support.provider.tests{",
- "}.zip", context.getCacheDir());
- try (
- final FileOutputStream outputStream =
- new ParcelFileDescriptor.AutoCloseOutputStream(
- ParcelFileDescriptor.open(
- file, ParcelFileDescriptor.MODE_WRITE_ONLY));
- final InputStream inputStream = context.getResources().openRawResource(id);
- ) {
- final byte[] buffer = new byte[32 * 1024];
- int bytes;
- while ((bytes = inputStream.read(buffer)) != -1) {
- outputStream.write(buffer, 0, bytes);
- }
- outputStream.flush();
- return file;
- }
- }
-}
diff --git a/fragment/Android.mk b/fragment/Android.mk
index e5cb644..a41b0c2 100644
--- a/fragment/Android.mk
+++ b/fragment/Android.mk
@@ -14,81 +14,34 @@
LOCAL_PATH := $(call my-dir)
-# A helper sub-library that makes direct use of Gingerbread APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-fragment-gingerbread
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, gingerbread)
-LOCAL_JAVA_LIBRARIES := \
- android-support-annotations \
- android-support-compat \
- android-support-core-utils \
- android-support-media-compat \
- android-support-core-ui
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Honeycomb APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-fragment-honeycomb
-LOCAL_SDK_VERSION := 11
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-fragment-gingerbread
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JellyBean APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-fragment-jellybean
-LOCAL_SDK_VERSION := 16
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-fragment-honeycomb
-LOCAL_JAVA_LIBRARIES := \
- android-support-annotations \
- android-support-compat \
- android-support-media-compat \
- android-support-core-ui \
- android-support-core-utils
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Lollipop APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-fragment-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-fragment-jellybean
-LOCAL_JAVA_LIBRARIES := \
- android-support-annotations \
- android-support-compat \
- android-support-media-compat \
- android-support-core-ui \
- android-support-core-utils
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
# Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+# LOCAL_STATIC_ANDROID_LIBRARIES := \
+# android-support-fragment \
+# android-support-compat \
+# android-support-media-compat \
+# android-support-core-ui \
+# android-support-core-utils
+#
+# in their makefiles to include the resources and their dependencies in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-fragment
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, java)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, gingerbread) \
+ $(call all-java-files-under, honeycomb) \
+ $(call all-java-files-under, jellybean) \
+ $(call all-java-files-under, api21) \
+ $(call all-java-files-under, java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-fragment-api21
-LOCAL_JAVA_LIBRARIES := \
- android-support-annotations \
+LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-compat \
- android-support-media-compat \
android-support-core-ui \
- android-support-core-utils
+ android-support-core-utils \
+ android-support-media-compat \
+ android-support-annotations
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java b/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java
index 49f2ade..a8323dc 100644
--- a/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java
+++ b/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java
@@ -16,7 +16,9 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
import android.transition.Transition;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
@@ -28,232 +30,34 @@
import java.util.List;
import java.util.Map;
+@RequiresApi(21)
+@TargetApi(21)
class FragmentTransitionCompat21 {
- public static String getTransitionName(View view) {
- return view.getTransitionName();
- }
+ /**
+ * Returns a clone of a transition or null if it is null
+ */
public static Object cloneTransition(Object transition) {
+ Transition copy = null;
if (transition != null) {
- transition = ((Transition)transition).clone();
+ copy = ((Transition) transition).clone();
}
- return transition;
+ return copy;
}
- public static Object captureExitingViews(Object exitTransition, View root,
- ArrayList<View> viewList, Map<String, View> namedViews, View nonExistentView) {
- if (exitTransition != null) {
- captureTransitioningViews(viewList, root);
- if (namedViews != null) {
- viewList.removeAll(namedViews.values());
- }
- if (viewList.isEmpty()) {
- exitTransition = null;
- } else {
- viewList.add(nonExistentView);
- addTargets((Transition) exitTransition, viewList);
- }
- }
- return exitTransition;
- }
-
- public static void excludeTarget(Object transitionObject, View view, boolean exclude) {
- Transition transition = (Transition) transitionObject;
- transition.excludeTarget(view, exclude);
- }
-
- public static void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject) {
- Transition transition = (Transition) transitionObject;
- TransitionManager.beginDelayedTransition(sceneRoot, transition);
- }
-
- public static void setEpicenter(Object transitionObject, View view) {
- Transition transition = (Transition) transitionObject;
- final Rect epicenter = getBoundsOnScreen(view);
-
- transition.setEpicenterCallback(new Transition.EpicenterCallback() {
- @Override
- public Rect onGetEpicenter(Transition transition) {
- return epicenter;
- }
- });
- }
-
- public static Object wrapSharedElementTransition(Object transitionObj) {
- if (transitionObj == null) {
- return null;
- }
- Transition transition = (Transition) transitionObj;
+ /**
+ * Wraps a transition in a TransitionSet and returns the set. If transition is null, null is
+ * returned.
+ */
+ public static Object wrapTransitionInSet(Object transition) {
if (transition == null) {
return null;
}
TransitionSet transitionSet = new TransitionSet();
- transitionSet.addTransition(transition);
+ transitionSet.addTransition((Transition) transition);
return transitionSet;
}
- private static void excludeViews(Transition transition, Transition fromTransition,
- ArrayList<View> views, boolean exclude) {
- if (transition != null) {
- final int viewCount = fromTransition == null ? 0 : views.size();
- for (int i = 0; i < viewCount; i++) {
- transition.excludeTarget(views.get(i), exclude);
- }
- }
- }
-
- /**
- * Exclude (or remove the exclude) of shared element views from the enter and exit transitions.
- *
- * @param enterTransitionObj The enter transition
- * @param exitTransitionObj The exit transition
- * @param sharedElementTransitionObj The shared element transition
- * @param views The shared element target views.
- * @param exclude <code>true</code> to exclude or <code>false</code> to remove the excluded
- * views.
- */
- public static void excludeSharedElementViews(Object enterTransitionObj,
- Object exitTransitionObj, Object sharedElementTransitionObj, ArrayList<View> views,
- boolean exclude) {
- Transition enterTransition = (Transition) enterTransitionObj;
- Transition exitTransition = (Transition) exitTransitionObj;
- Transition sharedElementTransition = (Transition) sharedElementTransitionObj;
- excludeViews(enterTransition, sharedElementTransition, views, exclude);
- excludeViews(exitTransition, sharedElementTransition, views, exclude);
- }
-
- /**
- * Prepares the enter transition by adding a non-existent view to the transition's target list
- * and setting it epicenter callback. By adding a non-existent view to the target list,
- * we can prevent any view from being targeted at the beginning of the transition.
- * We will add to the views before the end state of the transition is captured so that the
- * views will appear. At the start of the transition, we clear the list of targets so that
- * we can restore the state of the transition and use it again.
- *
- * <p>The shared element transition maps its shared elements immediately prior to
- * capturing the final state of the Transition.</p>
- */
- public static void addTransitionTargets(Object enterTransitionObject,
- Object sharedElementTransitionObject, Object exitTransitionObject, final View container,
- final ViewRetriever inFragment, final View nonExistentView,
- EpicenterView epicenterView, final Map<String, String> nameOverrides,
- final ArrayList<View> enteringViews, final ArrayList<View> exitingViews,
- final Map<String, View> namedViews, final Map<String, View> renamedViews,
- final ArrayList<View> sharedElementTargets) {
- final Transition enterTransition = (Transition) enterTransitionObject;
- final Transition exitTransition = (Transition) exitTransitionObject;
- final Transition sharedElementTransition = (Transition) sharedElementTransitionObject;
- excludeViews(enterTransition, exitTransition, exitingViews, true);
- if (enterTransitionObject != null || sharedElementTransitionObject != null) {
- if (enterTransition != null) {
- enterTransition.addTarget(nonExistentView);
- }
- if (sharedElementTransitionObject != null) {
- setSharedElementTargets(sharedElementTransition, nonExistentView,
- namedViews, sharedElementTargets);
- excludeViews(enterTransition, sharedElementTransition, sharedElementTargets, true);
- excludeViews(exitTransition, sharedElementTransition, sharedElementTargets, true);
- }
-
- container.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- container.getViewTreeObserver().removeOnPreDrawListener(this);
- if (enterTransition != null) {
- enterTransition.removeTarget(nonExistentView);
- }
- if (inFragment != null) {
- View fragmentView = inFragment.getView();
- if (fragmentView != null) {
- if (!nameOverrides.isEmpty()) {
- findNamedViews(renamedViews, fragmentView);
- renamedViews.keySet().retainAll(nameOverrides.values());
- for (Map.Entry<String, String> entry : nameOverrides
- .entrySet()) {
- String to = entry.getValue();
- View view = renamedViews.get(to);
- if (view != null) {
- String from = entry.getKey();
- view.setTransitionName(from);
- }
- }
- }
- if (enterTransition != null) {
- captureTransitioningViews(enteringViews, fragmentView);
- enteringViews.removeAll(renamedViews.values());
- enteringViews.add(nonExistentView);
- addTargets(enterTransition, enteringViews);
- }
- }
- }
- excludeViews(exitTransition, enterTransition, enteringViews, true);
-
- return true;
- }
- });
- setSharedElementEpicenter(enterTransition, epicenterView);
- }
- }
-
- public static Object mergeTransitions(Object enterTransitionObject,
- Object exitTransitionObject, Object sharedElementTransitionObject,
- boolean allowOverlap) {
- boolean overlap = true;
- Transition enterTransition = (Transition) enterTransitionObject;
- Transition exitTransition = (Transition) exitTransitionObject;
- Transition sharedElementTransition = (Transition) sharedElementTransitionObject;
-
- if (enterTransition != null && exitTransition != null) {
- overlap = allowOverlap;
- }
-
- // Wrap the transitions. Explicit targets like in enter and exit will cause the
- // views to be targeted regardless of excluded views. If that happens, then the
- // excluded fragments views (hidden fragments) will still be in the transition.
-
- Transition transition;
- if (overlap) {
- // Regular transition -- do it all together
- TransitionSet transitionSet = new TransitionSet();
- if (enterTransition != null) {
- transitionSet.addTransition(enterTransition);
- }
- if (exitTransition != null) {
- transitionSet.addTransition(exitTransition);
- }
- if (sharedElementTransition != null) {
- transitionSet.addTransition(sharedElementTransition);
- }
- transition = transitionSet;
- } else {
- // First do exit, then enter, but allow shared element transition to happen
- // during both.
- Transition staggered = null;
- if (exitTransition != null && enterTransition != null) {
- staggered = new TransitionSet()
- .addTransition(exitTransition)
- .addTransition(enterTransition)
- .setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
- } else if (exitTransition != null) {
- staggered = exitTransition;
- } else if (enterTransition != null) {
- staggered = enterTransition;
- }
- if (sharedElementTransition != null) {
- TransitionSet together = new TransitionSet();
- if (staggered != null) {
- together.addTransition(staggered);
- }
- together.addTransition(sharedElementTransition);
- transition = together;
- } else {
- transition = staggered;
- }
- }
- return transition;
- }
-
/**
* Finds all children of the shared elements and sets the wrapping TransitionSet
* targets to point to those. It also limits transitions that have no targets to the
@@ -261,21 +65,18 @@
* shared elements specifically, but this doesn't happen by default.
*/
public static void setSharedElementTargets(Object transitionObj,
- View nonExistentView, Map<String, View> namedViews,
- ArrayList<View> sharedElementTargets) {
+ View nonExistentView, ArrayList<View> sharedViews) {
TransitionSet transition = (TransitionSet) transitionObj;
- sharedElementTargets.clear();
- sharedElementTargets.addAll(namedViews.values());
-
final List<View> views = transition.getTargets();
views.clear();
- final int count = sharedElementTargets.size();
+ final int count = sharedViews.size();
for (int i = 0; i < count; i++) {
- final View view = sharedElementTargets.get(i);
+ final View view = sharedViews.get(i);
bfsAddViewChildren(views, view);
}
- sharedElementTargets.add(nonExistentView);
- addTargets(transition, sharedElementTargets);
+ views.add(nonExistentView);
+ sharedViews.add(nonExistentView);
+ addTargets(transition, sharedViews);
}
/**
@@ -316,139 +117,32 @@
return false;
}
- private static void setSharedElementEpicenter(Transition transition,
- final EpicenterView epicenterView) {
- if (transition != null) {
- transition.setEpicenterCallback(new Transition.EpicenterCallback() {
- private Rect mEpicenter;
+ /**
+ * Sets a transition epicenter to the rectangle of a given View.
+ */
+ public static void setEpicenter(Object transitionObj, View view) {
+ if (view != null) {
+ Transition transition = (Transition) transitionObj;
+ final Rect epicenter = new Rect();
+ getBoundsOnScreen(view, epicenter);
+ transition.setEpicenterCallback(new Transition.EpicenterCallback() {
@Override
public Rect onGetEpicenter(Transition transition) {
- if (mEpicenter == null && epicenterView.epicenter != null) {
- mEpicenter = getBoundsOnScreen(epicenterView.epicenter);
- }
- return mEpicenter;
- }
- });
- }
- }
-
- private static Rect getBoundsOnScreen(View view) {
- Rect epicenter = new Rect();
- int[] loc = new int[2];
- view.getLocationOnScreen(loc);
- // not as good as View.getBoundsOnScreen, but that's not public
- epicenter.set(loc[0], loc[1], loc[0] + view.getWidth(), loc[1] + view.getHeight());
- return epicenter;
- }
-
- private static void captureTransitioningViews(ArrayList<View> transitioningViews, View view) {
- if (view.getVisibility() == View.VISIBLE) {
- if (view instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) view;
- if (viewGroup.isTransitionGroup()) {
- transitioningViews.add(viewGroup);
- } else {
- int count = viewGroup.getChildCount();
- for (int i = 0; i < count; i++) {
- View child = viewGroup.getChildAt(i);
- captureTransitioningViews(transitioningViews, child);
- }
- }
- } else {
- transitioningViews.add(view);
- }
- }
- }
-
- public static void findNamedViews(Map<String, View> namedViews, View view) {
- if (view.getVisibility() == View.VISIBLE) {
- String transitionName = view.getTransitionName();
- if (transitionName != null) {
- namedViews.put(transitionName, view);
- }
- if (view instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) view;
- int count = viewGroup.getChildCount();
- for (int i = 0; i < count; i++) {
- View child = viewGroup.getChildAt(i);
- findNamedViews(namedViews, child);
- }
- }
- }
- }
-
- public static void cleanupTransitions(final View sceneRoot, final View nonExistentView,
- Object enterTransitionObject, final ArrayList<View> enteringViews,
- Object exitTransitionObject, final ArrayList<View> exitingViews,
- Object sharedElementTransitionObject, final ArrayList<View> sharedElementTargets,
- Object overallTransitionObject, final ArrayList<View> hiddenViews,
- final Map<String, View> renamedViews) {
- final Transition enterTransition = (Transition) enterTransitionObject;
- final Transition exitTransition = (Transition) exitTransitionObject;
- final Transition sharedElementTransition = (Transition) sharedElementTransitionObject;
- final Transition overallTransition = (Transition) overallTransitionObject;
- if (overallTransition != null) {
- sceneRoot.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
- if (enterTransition != null) {
- removeTargets(enterTransition, enteringViews);
- excludeViews(enterTransition, exitTransition, exitingViews, false);
- excludeViews(enterTransition, sharedElementTransition, sharedElementTargets,
- false);
- }
- if (exitTransition != null) {
- removeTargets(exitTransition, exitingViews);
- excludeViews(exitTransition, enterTransition, enteringViews, false);
- excludeViews(exitTransition, sharedElementTransition, sharedElementTargets,
- false);
- }
- if (sharedElementTransition != null) {
- removeTargets(sharedElementTransition, sharedElementTargets);
- }
- for (Map.Entry<String, View> entry : renamedViews.entrySet()) {
- View view = entry.getValue();
- String name = entry.getKey();
- view.setTransitionName(name);
- }
- int numViews = hiddenViews.size();
- for (int i = 0; i < numViews; i++) {
- overallTransition.excludeTarget(hiddenViews.get(i), false);
- }
- overallTransition.excludeTarget(nonExistentView, false);
- return true;
+ return epicenter;
}
});
}
}
/**
- * This method removes the views from transitions that target ONLY those views.
- * The views list should match those added in addTargets and should contain
- * one view that is not in the view hierarchy (state.nonExistentView).
+ * Replacement for view.getBoundsOnScreen because that is not public. This returns a rect
+ * containing the bounds relative to the screen that the view is in.
*/
- public static void removeTargets(Object transitionObject, ArrayList<View> views) {
- Transition transition = (Transition) transitionObject;
- if (transition instanceof TransitionSet) {
- TransitionSet set = (TransitionSet) transition;
- int numTransitions = set.getTransitionCount();
- for (int i = 0; i < numTransitions; i++) {
- Transition child = set.getTransitionAt(i);
- removeTargets(child, views);
- }
- } else if (!hasSimpleTarget(transition)) {
- List<View> targets = transition.getTargets();
- if (targets != null && targets.size() == views.size() &&
- targets.containsAll(views)) {
- // We have an exact match. We must have added these earlier in addTargets
- for (int i = views.size() - 1; i >= 0; i--) {
- transition.removeTarget(views.get(i));
- }
- }
- }
+ public static void getBoundsOnScreen(View view, Rect epicenter) {
+ int[] loc = new int[2];
+ view.getLocationOnScreen(loc);
+ epicenter.set(loc[0], loc[1], loc[0] + view.getWidth(), loc[1] + view.getHeight());
}
/**
@@ -457,10 +151,13 @@
* that does not exist in the view hierarchy (state.nonExistentView) so that
* when they are removed later, a list match will suffice to remove the targets.
* Otherwise, if you happened to have targeted the exact views for the transition,
- * the removeTargets call will remove them unexpectedly.
+ * the replaceTargets call will remove them unexpectedly.
*/
- public static void addTargets(Object transitionObject, ArrayList<View> views) {
- Transition transition = (Transition) transitionObject;
+ public static void addTargets(Object transitionObj, ArrayList<View> views) {
+ Transition transition = (Transition) transitionObj;
+ if (transition == null) {
+ return;
+ }
if (transition instanceof TransitionSet) {
TransitionSet set = (TransitionSet) transition;
int numTransitions = set.getTransitionCount();
@@ -480,21 +177,409 @@
}
}
+ /**
+ * Returns true if there are any targets based on ID, transition or type.
+ */
private static boolean hasSimpleTarget(Transition transition) {
- return !isNullOrEmpty(transition.getTargetIds()) ||
- !isNullOrEmpty(transition.getTargetNames()) ||
- !isNullOrEmpty(transition.getTargetTypes());
+ return !isNullOrEmpty(transition.getTargetIds())
+ || !isNullOrEmpty(transition.getTargetNames())
+ || !isNullOrEmpty(transition.getTargetTypes());
}
+ /**
+ * Simple utility to detect if a list is null or has no elements.
+ */
private static boolean isNullOrEmpty(List list) {
return list == null || list.isEmpty();
}
- public interface ViewRetriever {
- View getView();
+ /**
+ * Creates a TransitionSet that plays all passed transitions together. Any null
+ * transitions passed will not be added to the set. If all are null, then an empty
+ * TransitionSet will be returned.
+ */
+ public static Object mergeTransitionsTogether(Object transition1, Object transition2,
+ Object transition3) {
+ TransitionSet transitionSet = new TransitionSet();
+ if (transition1 != null) {
+ transitionSet.addTransition((Transition) transition1);
+ }
+ if (transition2 != null) {
+ transitionSet.addTransition((Transition) transition2);
+ }
+ if (transition3 != null) {
+ transitionSet.addTransition((Transition) transition3);
+ }
+ return transitionSet;
}
- public static class EpicenterView {
- public View epicenter;
+ /**
+ * After the transition completes, the fragment's view is set to GONE and the exiting
+ * views are set to VISIBLE.
+ */
+ public static void scheduleHideFragmentView(Object exitTransitionObj, final View fragmentView,
+ final ArrayList<View> exitingViews) {
+ Transition exitTransition = (Transition) exitTransitionObj;
+ exitTransition.addListener(new Transition.TransitionListener() {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ transition.removeListener(this);
+ fragmentView.setVisibility(View.GONE);
+ final int numViews = exitingViews.size();
+ for (int i = 0; i < numViews; i++) {
+ exitingViews.get(i).setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void onTransitionCancel(Transition transition) {
+ }
+
+ @Override
+ public void onTransitionPause(Transition transition) {
+ }
+
+ @Override
+ public void onTransitionResume(Transition transition) {
+ }
+ });
+ }
+
+ /**
+ * Combines enter, exit, and shared element transition so that they play in the proper
+ * sequence. First the exit transition plays along with the shared element transition.
+ * When the exit transition completes, the enter transition starts. The shared element
+ * transition can continue running while the enter transition plays.
+ *
+ * @return A TransitionSet with all of enter, exit, and shared element transitions in
+ * it (modulo null values), ordered such that they play in the proper sequence.
+ */
+ public static Object mergeTransitionsInSequence(Object exitTransitionObj,
+ Object enterTransitionObj, Object sharedElementTransitionObj) {
+ // First do exit, then enter, but allow shared element transition to happen
+ // during both.
+ Transition staggered = null;
+ final Transition exitTransition = (Transition) exitTransitionObj;
+ final Transition enterTransition = (Transition) enterTransitionObj;
+ final Transition sharedElementTransition = (Transition) sharedElementTransitionObj;
+ if (exitTransition != null && enterTransition != null) {
+ staggered = new TransitionSet()
+ .addTransition(exitTransition)
+ .addTransition(enterTransition)
+ .setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+ } else if (exitTransition != null) {
+ staggered = exitTransition;
+ } else if (enterTransition != null) {
+ staggered = enterTransition;
+ }
+ if (sharedElementTransition != null) {
+ TransitionSet together = new TransitionSet();
+ if (staggered != null) {
+ together.addTransition(staggered);
+ }
+ together.addTransition(sharedElementTransition);
+ return together;
+ } else {
+ return staggered;
+ }
+ }
+
+ /**
+ * Calls {@link TransitionManager#beginDelayedTransition(ViewGroup, Transition)}.
+ */
+ public static void beginDelayedTransition(ViewGroup sceneRoot, Object transition) {
+ TransitionManager.beginDelayedTransition(sceneRoot, (Transition) transition);
+ }
+
+ /**
+ * Prepares for setting the shared element names by gathering the names of the incoming
+ * shared elements and clearing them. {@link #setNameOverridesOptimized(View, ArrayList,
+ * ArrayList, ArrayList, Map)} must be called after this to complete setting the shared element
+ * name overrides. This must be called before
+ * {@link #beginDelayedTransition(ViewGroup, Object)}.
+ */
+ public static ArrayList<String> prepareSetNameOverridesOptimized(
+ final ArrayList<View> sharedElementsIn) {
+ final ArrayList<String> names = new ArrayList<>();
+ final int numSharedElements = sharedElementsIn.size();
+ for (int i = 0; i < numSharedElements; i++) {
+ final View view = sharedElementsIn.get(i);
+ names.add(view.getTransitionName());
+ view.setTransitionName(null);
+ }
+ return names;
+ }
+
+ /**
+ * Changes the shared element names for the incoming shared eleemnts to match those of the
+ * outgoing shared elements. This also temporarily clears the shared element names of the
+ * outgoing shared elements. Must be called after
+ * {@link #beginDelayedTransition(ViewGroup, Object)}.
+ */
+ public static void setNameOverridesOptimized(final View sceneRoot,
+ final ArrayList<View> sharedElementsOut, final ArrayList<View> sharedElementsIn,
+ final ArrayList<String> inNames, final Map<String, String> nameOverrides) {
+ final int numSharedElements = sharedElementsIn.size();
+ final ArrayList<String> outNames = new ArrayList<>();
+
+ for (int i = 0; i < numSharedElements; i++) {
+ final View view = sharedElementsOut.get(i);
+ final String name = view.getTransitionName();
+ outNames.add(name);
+ if (name == null) {
+ continue;
+ }
+ view.setTransitionName(null);
+ final String inName = nameOverrides.get(name);
+ for (int j = 0; j < numSharedElements; j++) {
+ if (inName.equals(inNames.get(j))) {
+ sharedElementsIn.get(j).setTransitionName(name);
+ break;
+ }
+ }
+ }
+
+ sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ for (int i = 0; i < numSharedElements; i++) {
+ sharedElementsIn.get(i).setTransitionName(inNames.get(i));
+ sharedElementsOut.get(i).setTransitionName(outNames.get(i));
+ }
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Gets the Views in the hierarchy affected by entering and exiting Activity Scene transitions.
+ * @param transitioningViews This View will be added to transitioningViews if it is VISIBLE and
+ * a normal View or a ViewGroup with
+ * {@link android.view.ViewGroup#isTransitionGroup()} true.
+ * @param view The base of the view hierarchy to look in.
+ */
+ public static void captureTransitioningViews(ArrayList<View> transitioningViews, View view) {
+ if (view.getVisibility() == View.VISIBLE) {
+ if (view instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) view;
+ if (viewGroup.isTransitionGroup()) {
+ transitioningViews.add(viewGroup);
+ } else {
+ int count = viewGroup.getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = viewGroup.getChildAt(i);
+ captureTransitioningViews(transitioningViews, child);
+ }
+ }
+ } else {
+ transitioningViews.add(view);
+ }
+ }
+ }
+
+ /**
+ * Finds all views that have transition names in the hierarchy under the given view and
+ * stores them in {@code namedViews} map with the name as the key.
+ */
+ public static void findNamedViews(Map<String, View> namedViews, View view) {
+ if (view.getVisibility() == View.VISIBLE) {
+ String transitionName = view.getTransitionName();
+ if (transitionName != null) {
+ namedViews.put(transitionName, view);
+ }
+ if (view instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) view;
+ int count = viewGroup.getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = viewGroup.getChildAt(i);
+ findNamedViews(namedViews, child);
+ }
+ }
+ }
+ }
+
+ public static void setNameOverridesUnoptimized(final View sceneRoot,
+ final ArrayList<View> sharedElementsIn, final Map<String, String> nameOverrides) {
+ sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ final int numSharedElements = sharedElementsIn.size();
+ for (int i = 0; i < numSharedElements; i++) {
+ View view = sharedElementsIn.get(i);
+ String name = view.getTransitionName();
+ if (name != null) {
+ String inName = findKeyForValue(nameOverrides, name);
+ view.setTransitionName(inName);
+ }
+ }
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Utility to find the String key in {@code map} that maps to {@code value}.
+ */
+ private static String findKeyForValue(Map<String, String> map, String value) {
+ for (Map.Entry<String, String> entry : map.entrySet()) {
+ if (value.equals(entry.getValue())) {
+ return entry.getKey();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * After the transition has started, remove all targets that we added to the transitions
+ * so that the transitions are left in a clean state.
+ */
+ public static void scheduleRemoveTargets(final Object overallTransitionObj,
+ final Object enterTransition, final ArrayList<View> enteringViews,
+ final Object exitTransition, final ArrayList<View> exitingViews,
+ final Object sharedElementTransition, final ArrayList<View> sharedElementsIn) {
+ final Transition overallTransition = (Transition) overallTransitionObj;
+ overallTransition.addListener(new Transition.TransitionListener() {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ if (enterTransition != null) {
+ replaceTargets(enterTransition, enteringViews, null);
+ }
+ if (exitTransition != null) {
+ replaceTargets(exitTransition, exitingViews, null);
+ }
+ if (sharedElementTransition != null) {
+ replaceTargets(sharedElementTransition, sharedElementsIn, null);
+ }
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ }
+
+ @Override
+ public void onTransitionCancel(Transition transition) {
+ }
+
+ @Override
+ public void onTransitionPause(Transition transition) {
+ }
+
+ @Override
+ public void onTransitionResume(Transition transition) {
+ }
+ });
+ }
+
+ /**
+ * Swap the targets for the shared element transition from those Views in sharedElementsOut
+ * to those in sharedElementsIn
+ */
+ public static void swapSharedElementTargets(Object sharedElementTransitionObj,
+ ArrayList<View> sharedElementsOut, ArrayList<View> sharedElementsIn) {
+ TransitionSet sharedElementTransition = (TransitionSet) sharedElementTransitionObj;
+ if (sharedElementTransition != null) {
+ sharedElementTransition.getTargets().clear();
+ sharedElementTransition.getTargets().addAll(sharedElementsIn);
+ replaceTargets(sharedElementTransition, sharedElementsOut, sharedElementsIn);
+ }
+ }
+
+
+ /**
+ * This method removes the views from transitions that target ONLY those views and
+ * replaces them with the new targets list.
+ * The views list should match those added in addTargets and should contain
+ * one view that is not in the view hierarchy (state.nonExistentView).
+ */
+ public static void replaceTargets(Object transitionObj, ArrayList<View> oldTargets,
+ ArrayList<View> newTargets) {
+ Transition transition = (Transition) transitionObj;
+ if (transition instanceof TransitionSet) {
+ TransitionSet set = (TransitionSet) transition;
+ int numTransitions = set.getTransitionCount();
+ for (int i = 0; i < numTransitions; i++) {
+ Transition child = set.getTransitionAt(i);
+ replaceTargets(child, oldTargets, newTargets);
+ }
+ } else if (!hasSimpleTarget(transition)) {
+ List<View> targets = transition.getTargets();
+ if (targets != null && targets.size() == oldTargets.size()
+ && targets.containsAll(oldTargets)) {
+ // We have an exact match. We must have added these earlier in addTargets
+ final int targetCount = newTargets == null ? 0 : newTargets.size();
+ for (int i = 0; i < targetCount; i++) {
+ transition.addTarget(newTargets.get(i));
+ }
+ for (int i = oldTargets.size() - 1; i >= 0; i--) {
+ transition.removeTarget(oldTargets.get(i));
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds a View target to a transition. If transitionObj is null, nothing is done.
+ */
+ public static void addTarget(Object transitionObj, View view) {
+ if (transitionObj != null) {
+ Transition transition = (Transition) transitionObj;
+ transition.addTarget(view);
+ }
+ }
+
+ /**
+ * Remove a View target to a transition. If transitionObj is null, nothing is done.
+ */
+ public static void removeTarget(Object transitionObj, View view) {
+ if (transitionObj != null) {
+ Transition transition = (Transition) transitionObj;
+ transition.removeTarget(view);
+ }
+ }
+
+ /**
+ * Sets the epicenter of a transition to a rect object. The object can be modified until
+ * the transition runs.
+ */
+ public static void setEpicenter(Object transitionObj, final Rect epicenter) {
+ if (transitionObj != null) {
+ Transition transition = (Transition) transitionObj;
+ transition.setEpicenterCallback(new Transition.EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ if (epicenter == null || epicenter.isEmpty()) {
+ return null;
+ }
+ return epicenter;
+ }
+ });
+ }
+ }
+
+ public static void scheduleNameReset(final ViewGroup sceneRoot,
+ final ArrayList<View> sharedElementsIn, final Map<String, String> nameOverrides) {
+ sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ final int numSharedElements = sharedElementsIn.size();
+ for (int i = 0; i < numSharedElements; i++) {
+ final View view = sharedElementsIn.get(i);
+ final String name = view.getTransitionName();
+ final String inName = nameOverrides.get(name);
+ view.setTransitionName(inName);
+ }
+ return true;
+ }
+ });
}
}
diff --git a/fragment/build.gradle b/fragment/build.gradle
index 592c856..0a862d4 100644
--- a/fragment/build.gradle
+++ b/fragment/build.gradle
@@ -1,8 +1,6 @@
apply plugin: 'com.android.library'
archivesBaseName = 'support-fragment'
-
-createApiSourceSets(project, gradle.ext.studioCompat.modules.fragment.apiTargets)
dependencies {
compile project(':support-compat')
compile project(':support-media-compat')
@@ -22,22 +20,25 @@
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
-setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.fragment.dependencies)
android {
- compileSdkVersion 9
+ compileSdkVersion project.ext.currentSdk
defaultConfig {
minSdkVersion 9
- // TODO: get target from branch
- //targetSdkVersion 19
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = ['java']
+ main.java.srcDirs = [
+ 'gingerbread',
+ 'honeycomb',
+ 'jellybean',
+ 'api21',
+ 'java'
+ ]
androidTest.setRoot('tests')
androidTest.java.srcDir 'tests/java'
@@ -92,11 +93,6 @@
exclude('android/service/media/**')
}
- project.ext.allSS.each { ss ->
- javadocTask.source ss.java
- sourcesJarTask.from ss.java.srcDirs
- }
-
artifacts.add('archives', javadocJarTask);
artifacts.add('archives', sourcesJarTask);
}
diff --git a/fragment/gingerbread/android/support/v4/app/BaseFragmentActivityGingerbread.java b/fragment/gingerbread/android/support/v4/app/BaseFragmentActivityGingerbread.java
index 890b802..f2f5154 100644
--- a/fragment/gingerbread/android/support/v4/app/BaseFragmentActivityGingerbread.java
+++ b/fragment/gingerbread/android/support/v4/app/BaseFragmentActivityGingerbread.java
@@ -16,6 +16,7 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -23,6 +24,7 @@
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
@@ -31,6 +33,8 @@
*
* @hide
*/
+@RequiresApi(9)
+@TargetApi(9)
abstract class BaseFragmentActivityGingerbread extends Activity {
// We need to keep track of whether startIntentSenderForResult originated from a Fragment, so we
diff --git a/fragment/honeycomb/android/support/v4/app/BaseFragmentActivityHoneycomb.java b/fragment/honeycomb/android/support/v4/app/BaseFragmentActivityHoneycomb.java
index 95f3d8d..a266fba 100644
--- a/fragment/honeycomb/android/support/v4/app/BaseFragmentActivityHoneycomb.java
+++ b/fragment/honeycomb/android/support/v4/app/BaseFragmentActivityHoneycomb.java
@@ -16,8 +16,10 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
+import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
@@ -26,6 +28,8 @@
*
* @hide
*/
+@RequiresApi(11)
+@TargetApi(11)
abstract class BaseFragmentActivityHoneycomb extends BaseFragmentActivityGingerbread {
@Override
diff --git a/fragment/java/android/support/v4/app/BackStackRecord.java b/fragment/java/android/support/v4/app/BackStackRecord.java
index 2e3116f..19f949c 100644
--- a/fragment/java/android/support/v4/app/BackStackRecord.java
+++ b/fragment/java/android/support/v4/app/BackStackRecord.java
@@ -19,14 +19,11 @@
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
-import android.support.v4.util.ArrayMap;
import android.support.v4.util.LogWriter;
+import android.support.v4.view.ViewCompat;
import android.text.TextUtils;
import android.util.Log;
-import android.util.SparseArray;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -45,39 +42,25 @@
final CharSequence mBreadCrumbShortTitleText;
final ArrayList<String> mSharedElementSourceNames;
final ArrayList<String> mSharedElementTargetNames;
+ final boolean mAllowOptimization;
public BackStackState(BackStackRecord bse) {
- int numRemoved = 0;
- BackStackRecord.Op op = bse.mHead;
- while (op != null) {
- if (op.removed != null) numRemoved += op.removed.size();
- op = op.next;
- }
- mOps = new int[bse.mNumOp*7 + numRemoved];
+ final int numOps = bse.mOps.size();
+ mOps = new int[numOps * 6];
if (!bse.mAddToBackStack) {
throw new IllegalStateException("Not on back stack");
}
- op = bse.mHead;
int pos = 0;
- while (op != null) {
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ final BackStackRecord.Op op = bse.mOps.get(opNum);
mOps[pos++] = op.cmd;
mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1;
mOps[pos++] = op.enterAnim;
mOps[pos++] = op.exitAnim;
mOps[pos++] = op.popEnterAnim;
mOps[pos++] = op.popExitAnim;
- if (op.removed != null) {
- final int N = op.removed.size();
- mOps[pos++] = N;
- for (int i=0; i<N; i++) {
- mOps[pos++] = op.removed.get(i).mIndex;
- }
- } else {
- mOps[pos++] = 0;
- }
- op = op.next;
}
mTransition = bse.mTransition;
mTransitionStyle = bse.mTransitionStyle;
@@ -89,6 +72,7 @@
mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
mSharedElementSourceNames = bse.mSharedElementSourceNames;
mSharedElementTargetNames = bse.mSharedElementTargetNames;
+ mAllowOptimization = bse.mAllowOptimization;
}
public BackStackState(Parcel in) {
@@ -103,6 +87,7 @@
mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mSharedElementSourceNames = in.createStringArrayList();
mSharedElementTargetNames = in.createStringArrayList();
+ mAllowOptimization = in.readInt() != 0;
}
public BackStackRecord instantiate(FragmentManagerImpl fm) {
@@ -125,16 +110,6 @@
op.exitAnim = mOps[pos++];
op.popEnterAnim = mOps[pos++];
op.popExitAnim = mOps[pos++];
- final int N = mOps[pos++];
- if (N > 0) {
- op.removed = new ArrayList<Fragment>(N);
- for (int i=0; i<N; i++) {
- if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
- "Instantiate " + bse + " set remove fragment #" + mOps[pos]);
- Fragment r = fm.mActive.get(mOps[pos++]);
- op.removed.add(r);
- }
- }
bse.mEnterAnim = op.enterAnim;
bse.mExitAnim = op.exitAnim;
bse.mPopEnterAnim = op.popEnterAnim;
@@ -153,6 +128,7 @@
bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
bse.mSharedElementSourceNames = mSharedElementSourceNames;
bse.mSharedElementTargetNames = mSharedElementTargetNames;
+ bse.mAllowOptimization = mAllowOptimization;
bse.bumpBackStackNesting(1);
return bse;
}
@@ -175,6 +151,7 @@
TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
dest.writeStringList(mSharedElementSourceNames);
dest.writeStringList(mSharedElementTargetNames);
+ dest.writeInt(mAllowOptimization ? 1 : 0);
}
public static final Parcelable.Creator<BackStackState> CREATOR
@@ -195,7 +172,7 @@
* Entry of an operation on the fragment back stack.
*/
final class BackStackRecord extends FragmentTransaction implements
- FragmentManager.BackStackEntry, Runnable {
+ FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {
static final String TAG = FragmentManagerImpl.TAG;
static final boolean SUPPORTS_TRANSITIONS = Build.VERSION.SDK_INT >= 21;
@@ -211,20 +188,15 @@
static final int OP_ATTACH = 7;
static final class Op {
- Op next;
- Op prev;
int cmd;
Fragment fragment;
int enterAnim;
int exitAnim;
int popEnterAnim;
int popExitAnim;
- ArrayList<Fragment> removed;
}
- Op mHead;
- Op mTail;
- int mNumOp;
+ ArrayList<Op> mOps = new ArrayList<>();
int mEnterAnim;
int mExitAnim;
int mPopEnterAnim;
@@ -244,6 +216,7 @@
ArrayList<String> mSharedElementSourceNames;
ArrayList<String> mSharedElementTargetNames;
+ boolean mAllowOptimization = true;
@Override
public String toString() {
@@ -303,12 +276,12 @@
}
}
- if (mHead != null) {
+ if (!mOps.isEmpty()) {
writer.print(prefix); writer.println("Operations:");
String innerPrefix = prefix + " ";
- Op op = mHead;
- int num = 0;
- while (op != null) {
+ final int numOps = mOps.size();
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ final Op op = mOps.get(opNum);
String cmdStr;
switch (op.cmd) {
case OP_NULL: cmdStr="NULL"; break;
@@ -321,7 +294,7 @@
case OP_ATTACH: cmdStr="ATTACH"; break;
default: cmdStr="cmd=" + op.cmd; break;
}
- writer.print(prefix); writer.print(" Op #"); writer.print(num);
+ writer.print(prefix); writer.print(" Op #"); writer.print(opNum);
writer.print(": "); writer.print(cmdStr);
writer.print(" "); writer.println(op.fragment);
if (full) {
@@ -338,23 +311,6 @@
writer.println(Integer.toHexString(op.popExitAnim));
}
}
- if (op.removed != null && op.removed.size() > 0) {
- for (int i=0; i<op.removed.size(); i++) {
- writer.print(innerPrefix);
- if (op.removed.size() == 1) {
- writer.print("Removed: ");
- } else {
- if (i == 0) {
- writer.println("Removed:");
- }
- writer.print(innerPrefix); writer.print(" #"); writer.print(i);
- writer.print(": ");
- }
- writer.println(op.removed.get(i));
- }
- }
- op = op.next;
- num++;
}
}
}
@@ -395,18 +351,11 @@
}
void addOp(Op op) {
- if (mHead == null) {
- mHead = mTail = op;
- } else {
- op.prev = mTail;
- mTail.next = op;
- mTail = op;
- }
+ mOps.add(op);
op.enterAnim = mEnterAnim;
op.exitAnim = mExitAnim;
op.popEnterAnim = mPopEnterAnim;
op.popExitAnim = mPopExitAnim;
- mNumOp++;
}
@Override
@@ -556,7 +505,7 @@
@Override
public FragmentTransaction addSharedElement(View sharedElement, String name) {
if (SUPPORTS_TRANSITIONS) {
- String transitionName = FragmentTransitionCompat21.getTransitionName(sharedElement);
+ String transitionName = ViewCompat.getTransitionName(sharedElement);
if (transitionName == null) {
throw new IllegalArgumentException("Unique transitionNames are required for all" +
" sharedElements");
@@ -638,22 +587,14 @@
}
if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting in " + this
+ " by " + amt);
- Op op = mHead;
- while (op != null) {
+ final int numOps = mOps.size();
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ final Op op = mOps.get(opNum);
if (op.fragment != null) {
op.fragment.mBackStackNesting += amt;
if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
+ op.fragment + " to " + op.fragment.mBackStackNesting);
}
- if (op.removed != null) {
- for (int i=op.removed.size()-1; i>=0; i--) {
- Fragment r = op.removed.get(i);
- r.mBackStackNesting += amt;
- if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
- + r + " to " + r.mBackStackNesting);
- }
- }
- op = op.next;
}
}
@@ -679,6 +620,12 @@
mManager.execSingleAction(this, true);
}
+ @Override
+ public FragmentTransaction setAllowOptimization(boolean allowOptimization) {
+ mAllowOptimization = allowOptimization;
+ return this;
+ }
+
int commitInternal(boolean allowStateLoss) {
if (mCommitted) throw new IllegalStateException("commit already called");
if (FragmentManagerImpl.DEBUG) {
@@ -697,354 +644,240 @@
return mIndex;
}
+ /**
+ * Implementation of {@link FragmentManagerImpl.OpGenerator}.
+ * This operation is added to the list of pending actions during {@link #commit()}, and
+ * will be executed on the UI thread to run this FragmentTransaction.
+ *
+ * @param records Modified to add this BackStackRecord
+ * @param isRecordPop Modified to add a false (this isn't a pop)
+ * @return true always because the records and isRecordPop will always be changed
+ */
@Override
- public void run() {
- if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);
-
- if (mAddToBackStack) {
- if (mIndex < 0) {
- throw new IllegalStateException("addToBackStack() called after commit()");
- }
+ public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
+ if (FragmentManagerImpl.DEBUG) {
+ Log.v(TAG, "Run: " + this);
}
- bumpBackStackNesting(1);
-
- TransitionState state = null;
- SparseArray<Fragment> firstOutFragments = null;
- SparseArray<Fragment> lastInFragments = null;
- if (SUPPORTS_TRANSITIONS && mManager.mCurState >= Fragment.CREATED) {
- firstOutFragments = new SparseArray<Fragment>();
- lastInFragments = new SparseArray<Fragment>();
-
- calculateFragments(firstOutFragments, lastInFragments);
-
- state = beginTransition(firstOutFragments, lastInFragments, false);
- }
-
- int transitionStyle = state != null ? 0 : mTransitionStyle;
- int transition = state != null ? 0 : mTransition;
- Op op = mHead;
- while (op != null) {
- int enterAnim = state != null ? 0 : op.enterAnim;
- int exitAnim = state != null ? 0 : op.exitAnim;
- switch (op.cmd) {
- case OP_ADD: {
- Fragment f = op.fragment;
- f.mNextAnim = enterAnim;
- mManager.addFragment(f, false);
- } break;
- case OP_REPLACE: {
- Fragment f = op.fragment;
- int containerId = f.mContainerId;
- if (mManager.mAdded != null) {
- for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {
- Fragment old = mManager.mAdded.get(i);
- if (FragmentManagerImpl.DEBUG) Log.v(TAG,
- "OP_REPLACE: adding=" + f + " old=" + old);
- if (old.mContainerId == containerId) {
- if (old == f) {
- op.fragment = f = null;
- } else {
- if (op.removed == null) {
- op.removed = new ArrayList<Fragment>();
- }
- op.removed.add(old);
- old.mNextAnim = exitAnim;
- if (mAddToBackStack) {
- old.mBackStackNesting += 1;
- if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
- + old + " to " + old.mBackStackNesting);
- }
- mManager.removeFragment(old, transition, transitionStyle);
- }
- }
- }
- }
- if (f != null) {
- f.mNextAnim = enterAnim;
- mManager.addFragment(f, false);
- }
- } break;
- case OP_REMOVE: {
- Fragment f = op.fragment;
- f.mNextAnim = exitAnim;
- mManager.removeFragment(f, transition, transitionStyle);
- } break;
- case OP_HIDE: {
- Fragment f = op.fragment;
- f.mNextAnim = exitAnim;
- mManager.hideFragment(f, transition, transitionStyle);
- } break;
- case OP_SHOW: {
- Fragment f = op.fragment;
- f.mNextAnim = enterAnim;
- mManager.showFragment(f, transition, transitionStyle);
- } break;
- case OP_DETACH: {
- Fragment f = op.fragment;
- f.mNextAnim = exitAnim;
- mManager.detachFragment(f, transition, transitionStyle);
- } break;
- case OP_ATTACH: {
- Fragment f = op.fragment;
- f.mNextAnim = enterAnim;
- mManager.attachFragment(f, transition, transitionStyle);
- } break;
- default: {
- throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
- }
- }
-
- op = op.next;
- }
-
- mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
-
+ records.add(this);
+ isRecordPop.add(false);
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
+ return true;
}
- private static void setFirstOut(SparseArray<Fragment> firstOutFragments,
- SparseArray<Fragment> lastInFragments, Fragment fragment) {
- if (fragment != null) {
- int containerId = fragment.mContainerId;
- if (containerId != 0 && !fragment.isHidden()) {
- if (fragment.isAdded() && fragment.getView() != null
- && firstOutFragments.get(containerId) == null) {
- firstOutFragments.put(containerId, fragment);
- }
- if (lastInFragments.get(containerId) == fragment) {
- lastInFragments.remove(containerId);
+ boolean interactsWith(int containerId) {
+ final int numOps = mOps.size();
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ final Op op = mOps.get(opNum);
+ if (op.fragment.mContainerId == containerId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean interactsWith(ArrayList<BackStackRecord> records, int startIndex, int endIndex) {
+ if (endIndex == startIndex) {
+ return false;
+ }
+ final int numOps = mOps.size();
+ int lastContainer = -1;
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ final Op op = mOps.get(opNum);
+ final int container = op.fragment.mContainerId;
+ if (container != 0 && container != lastContainer) {
+ lastContainer = container;
+ for (int i = startIndex; i < endIndex; i++) {
+ BackStackRecord record = records.get(i);
+ final int numThoseOps = record.mOps.size();
+ for (int thoseOpIndex = 0; thoseOpIndex < numThoseOps; thoseOpIndex++) {
+ final Op thatOp = record.mOps.get(thoseOpIndex);
+ if (thatOp.fragment.mContainerId == container) {
+ return true;
+ }
+ }
}
}
}
+ return false;
}
- private void setLastIn(SparseArray<Fragment> firstOutFragments,
- SparseArray<Fragment> lastInFragments, Fragment fragment) {
- if (fragment != null) {
- int containerId = fragment.mContainerId;
- if (containerId != 0) {
- if (!fragment.isAdded()) {
- lastInFragments.put(containerId, fragment);
- }
- if (firstOutFragments.get(containerId) == fragment) {
- firstOutFragments.remove(containerId);
- }
+ /**
+ * Executes the operations contained within this transaction. The Fragment states will only
+ * be modified if optimizations are not allowed.
+ */
+ void executeOps() {
+ final int numOps = mOps.size();
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ final Op op = mOps.get(opNum);
+ final Fragment f = op.fragment;
+ f.setNextTransition(mTransition, mTransitionStyle);
+ switch (op.cmd) {
+ case OP_ADD:
+ f.setNextAnim(op.enterAnim);
+ mManager.addFragment(f, false);
+ break;
+ case OP_REMOVE:
+ f.setNextAnim(op.exitAnim);
+ mManager.removeFragment(f);
+ break;
+ case OP_HIDE:
+ f.setNextAnim(op.exitAnim);
+ mManager.hideFragment(f);
+ break;
+ case OP_SHOW:
+ f.setNextAnim(op.enterAnim);
+ mManager.showFragment(f);
+ break;
+ case OP_DETACH:
+ f.setNextAnim(op.exitAnim);
+ mManager.detachFragment(f);
+ break;
+ case OP_ATTACH:
+ f.setNextAnim(op.enterAnim);
+ mManager.attachFragment(f);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
- if (fragment.mState < Fragment.CREATED && mManager.mCurState >= Fragment.CREATED) {
- mManager.makeActive(fragment);
- mManager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
+ if (!mAllowOptimization && op.cmd != OP_ADD) {
+ mManager.moveFragmentToExpectedState(f);
}
}
+ if (!mAllowOptimization) {
+ // Added fragments are added at the end to comply with prior behavior.
+ mManager.moveToState(mManager.mCurState);
+ }
}
/**
- * Finds the first removed fragment and last added fragments when going forward.
- * If none of the fragments have transitions, then both lists will be empty.
- *
- * @param firstOutFragments The list of first fragments to be removed, keyed on the
- * container ID. This list will be modified by the method.
- * @param lastInFragments The list of last fragments to be added, keyed on the
- * container ID. This list will be modified by the method.
+ * Reverses the execution of the operations within this transaction. The Fragment states will
+ * only be modified if optimizations are not allowed.
*/
- private void calculateFragments(SparseArray<Fragment> firstOutFragments,
- SparseArray<Fragment> lastInFragments) {
- if (!mManager.mContainer.onHasView()) {
- return; // nothing to see, so no transitions
- }
- Op op = mHead;
- while (op != null) {
+ void executePopOps() {
+ for (int opNum = mOps.size() - 1; opNum >= 0; opNum--) {
+ final Op op = mOps.get(opNum);
+ Fragment f = op.fragment;
+ f.setNextTransition(FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
switch (op.cmd) {
case OP_ADD:
- setLastIn(firstOutFragments, lastInFragments, op.fragment);
+ f.setNextAnim(op.popExitAnim);
+ mManager.removeFragment(f);
+ break;
+ case OP_REMOVE:
+ f.setNextAnim(op.popEnterAnim);
+ mManager.addFragment(f, false);
+ break;
+ case OP_HIDE:
+ f.setNextAnim(op.popEnterAnim);
+ mManager.showFragment(f);
+ break;
+ case OP_SHOW:
+ f.setNextAnim(op.popExitAnim);
+ mManager.hideFragment(f);
+ break;
+ case OP_DETACH:
+ f.setNextAnim(op.popEnterAnim);
+ mManager.attachFragment(f);
+ break;
+ case OP_ATTACH:
+ f.setNextAnim(op.popExitAnim);
+ mManager.detachFragment(f);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
+ }
+ if (!mAllowOptimization && op.cmd != OP_ADD) {
+ mManager.moveFragmentToExpectedState(f);
+ }
+ }
+ if (!mAllowOptimization) {
+ mManager.moveToState(mManager.mCurState);
+ }
+ }
+
+ /**
+ * Removes all OP_REPLACE ops and replaces them with the proper add and remove
+ * operations that are equivalent to the replace. This must be called prior to
+ * {@link #executeOps()} or any other call that operations on mOps.
+ *
+ * @param added Initialized to the fragments that are in the mManager.mAdded, this
+ * will be modified to contain the fragments that will be in mAdded
+ * after the execution ({@link #executeOps()}.
+ */
+ void expandReplaceOps(ArrayList<Fragment> added) {
+ for (int opNum = 0; opNum < mOps.size(); opNum++) {
+ final Op op = mOps.get(opNum);
+ switch (op.cmd) {
+ case OP_ADD:
+ case OP_ATTACH:
+ added.add(op.fragment);
+ break;
+ case OP_REMOVE:
+ case OP_DETACH:
+ added.remove(op.fragment);
break;
case OP_REPLACE: {
Fragment f = op.fragment;
- if (mManager.mAdded != null) {
- for (int i = 0; i < mManager.mAdded.size(); i++) {
- Fragment old = mManager.mAdded.get(i);
- if (f == null || old.mContainerId == f.mContainerId) {
- if (old == f) {
- f = null;
- lastInFragments.remove(old.mContainerId);
- } else {
- setFirstOut(firstOutFragments, lastInFragments, old);
- }
+ int containerId = f.mContainerId;
+ boolean alreadyAdded = false;
+ for (int i = added.size() - 1; i >= 0; i--) {
+ Fragment old = added.get(i);
+ if (old.mContainerId == containerId) {
+ if (old == f) {
+ alreadyAdded = true;
+ } else {
+ Op removeOp = new Op();
+ removeOp.cmd = OP_REMOVE;
+ removeOp.fragment = old;
+ removeOp.enterAnim = op.enterAnim;
+ removeOp.popEnterAnim = op.popEnterAnim;
+ removeOp.exitAnim = op.exitAnim;
+ removeOp.popExitAnim = op.popExitAnim;
+ mOps.add(opNum, removeOp);
+ added.remove(old);
+ opNum++;
}
}
}
- setLastIn(firstOutFragments, lastInFragments, op.fragment);
- break;
+ if (alreadyAdded) {
+ mOps.remove(opNum);
+ opNum--;
+ } else {
+ op.cmd = OP_ADD;
+ added.add(f);
+ }
}
- case OP_REMOVE:
- setFirstOut(firstOutFragments, lastInFragments, op.fragment);
- break;
- case OP_HIDE:
- setFirstOut(firstOutFragments, lastInFragments, op.fragment);
- break;
- case OP_SHOW:
- setLastIn(firstOutFragments, lastInFragments, op.fragment);
- break;
- case OP_DETACH:
- setFirstOut(firstOutFragments, lastInFragments, op.fragment);
- break;
- case OP_ATTACH:
- setLastIn(firstOutFragments, lastInFragments, op.fragment);
- break;
+ break;
}
-
- op = op.next;
}
}
- /**
- * Finds the first removed fragment and last added fragments when popping the back stack.
- * If none of the fragments have transitions, then both lists will be empty.
- *
- * @param firstOutFragments The list of first fragments to be removed, keyed on the
- * container ID. This list will be modified by the method.
- * @param lastInFragments The list of last fragments to be added, keyed on the
- * container ID. This list will be modified by the method.
- */
- public void calculateBackFragments(SparseArray<Fragment> firstOutFragments,
- SparseArray<Fragment> lastInFragments) {
- if (!mManager.mContainer.onHasView()) {
- return; // nothing to see, so no transitions
- }
- Op op = mTail;
- while (op != null) {
- switch (op.cmd) {
- case OP_ADD:
- setFirstOut(firstOutFragments, lastInFragments, op.fragment);
- break;
- case OP_REPLACE:
- if (op.removed != null) {
- for (int i = op.removed.size() - 1; i >= 0; i--) {
- setLastIn(firstOutFragments, lastInFragments, op.removed.get(i));
- }
- }
- setFirstOut(firstOutFragments, lastInFragments, op.fragment);
- break;
- case OP_REMOVE:
- setLastIn(firstOutFragments, lastInFragments, op.fragment);
- break;
- case OP_HIDE:
- setLastIn(firstOutFragments, lastInFragments, op.fragment);
- break;
- case OP_SHOW:
- setFirstOut(firstOutFragments, lastInFragments, op.fragment);
- break;
- case OP_DETACH:
- setLastIn(firstOutFragments, lastInFragments, op.fragment);
- break;
- case OP_ATTACH:
- setFirstOut(firstOutFragments, lastInFragments, op.fragment);
- break;
+ boolean isPostponed() {
+ for (int opNum = 0; opNum < mOps.size(); opNum++) {
+ final Op op = mOps.get(opNum);
+ if (isFragmentPostponed(op)) {
+ return true;
}
+ }
+ return false;
+ }
- op = op.prev;
+ void setOnStartPostponedListener(Fragment.OnStartEnterTransitionListener listener) {
+ for (int opNum = 0; opNum < mOps.size(); opNum++) {
+ final Op op = mOps.get(opNum);
+ if (isFragmentPostponed(op)) {
+ op.fragment.setOnStartEnterTransitionListener(listener);
+ }
}
}
- public TransitionState popFromBackStack(boolean doStateMove, TransitionState state,
- SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
- if (FragmentManagerImpl.DEBUG) {
- Log.v(TAG, "popFromBackStack: " + this);
- LogWriter logw = new LogWriter(TAG);
- PrintWriter pw = new PrintWriter(logw);
- dump(" ", null, pw, null);
- }
-
- if (SUPPORTS_TRANSITIONS && mManager.mCurState >= Fragment.CREATED) {
- if (state == null) {
- if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) {
- state = beginTransition(firstOutFragments, lastInFragments, true);
- }
- } else if (!doStateMove) {
- setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
- }
- }
-
- bumpBackStackNesting(-1);
-
- int transitionStyle = state != null ? 0 : mTransitionStyle;
- int transition = state != null ? 0 : mTransition;
- Op op = mTail;
- while (op != null) {
- int popEnterAnim = state != null ? 0 : op.popEnterAnim;
- int popExitAnim= state != null ? 0 : op.popExitAnim;
- switch (op.cmd) {
- case OP_ADD: {
- Fragment f = op.fragment;
- f.mNextAnim = popExitAnim;
- mManager.removeFragment(f,
- FragmentManagerImpl.reverseTransit(transition), transitionStyle);
- } break;
- case OP_REPLACE: {
- Fragment f = op.fragment;
- if (f != null) {
- f.mNextAnim = popExitAnim;
- mManager.removeFragment(f,
- FragmentManagerImpl.reverseTransit(transition), transitionStyle);
- }
- if (op.removed != null) {
- for (int i=0; i<op.removed.size(); i++) {
- Fragment old = op.removed.get(i);
- old.mNextAnim = popEnterAnim;
- mManager.addFragment(old, false);
- }
- }
- } break;
- case OP_REMOVE: {
- Fragment f = op.fragment;
- f.mNextAnim = popEnterAnim;
- mManager.addFragment(f, false);
- } break;
- case OP_HIDE: {
- Fragment f = op.fragment;
- f.mNextAnim = popEnterAnim;
- mManager.showFragment(f,
- FragmentManagerImpl.reverseTransit(transition), transitionStyle);
- } break;
- case OP_SHOW: {
- Fragment f = op.fragment;
- f.mNextAnim = popExitAnim;
- mManager.hideFragment(f,
- FragmentManagerImpl.reverseTransit(transition), transitionStyle);
- } break;
- case OP_DETACH: {
- Fragment f = op.fragment;
- f.mNextAnim = popEnterAnim;
- mManager.attachFragment(f,
- FragmentManagerImpl.reverseTransit(transition), transitionStyle);
- } break;
- case OP_ATTACH: {
- Fragment f = op.fragment;
- f.mNextAnim = popEnterAnim;
- mManager.detachFragment(f,
- FragmentManagerImpl.reverseTransit(transition), transitionStyle);
- } break;
- default: {
- throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
- }
- }
-
- op = op.prev;
- }
-
- if (doStateMove) {
- mManager.moveToState(mManager.mCurState,
- FragmentManagerImpl.reverseTransit(transition), transitionStyle, true);
- state = null;
- }
-
- if (mIndex >= 0) {
- mManager.freeBackStackIndex(mIndex);
- mIndex = -1;
- }
- return state;
+ private static boolean isFragmentPostponed(Op op) {
+ final Fragment fragment = op.fragment;
+ return (fragment.mAdded && fragment.mView != null && !fragment.mDetached
+ && !fragment.mHidden && fragment.isPostponed());
}
@Override
@@ -1062,487 +895,6 @@
@Override
public boolean isEmpty() {
- return mNumOp == 0;
- }
-
- /**
- * When custom fragment transitions are used, this sets up the state for each transition
- * and begins the transition. A different transition is started for each fragment container
- * and consists of up to 3 different transitions: the exit transition, a shared element
- * transition and an enter transition.
- *
- * <p>The exit transition operates against the leaf nodes of the first fragment
- * with a view that was removed. If no such fragment was removed, then no exit
- * transition is executed. The exit transition comes from the outgoing fragment.</p>
- *
- * <p>The enter transition operates against the last fragment that was added. If
- * that fragment does not have a view or no fragment was added, then no enter
- * transition is executed. The enter transition comes from the incoming fragment.</p>
- *
- * <p>The shared element transition operates against all views and comes either
- * from the outgoing fragment or the incoming fragment, depending on whether this
- * is going forward or popping the back stack. When going forward, the incoming
- * fragment's enter shared element transition is used, but when going back, the
- * outgoing fragment's return shared element transition is used. Shared element
- * transitions only operate if there is both an incoming and outgoing fragment.</p>
- *
- * @param firstOutFragments The list of first fragments to be removed, keyed on the
- * container ID.
- * @param lastInFragments The list of last fragments to be added, keyed on the
- * container ID.
- * @param isBack true if this is popping the back stack or false if this is a
- * forward operation.
- * @return The TransitionState used to complete the operation of the transition
- * in {@link #setNameOverrides(BackStackRecord.TransitionState, java.util.ArrayList,
- * java.util.ArrayList)}.
- */
- private TransitionState beginTransition(SparseArray<Fragment> firstOutFragments,
- SparseArray<Fragment> lastInFragments, boolean isBack) {
- TransitionState state = new TransitionState();
-
- // Adding a non-existent target view makes sure that the transitions don't target
- // any views by default. They'll only target the views we tell add. If we don't
- // add any, then no views will be targeted.
- state.nonExistentView = new View(mManager.mHost.getContext());
-
- boolean anyTransitionStarted = false;
- // Go over all leaving fragments.
- for (int i = 0; i < firstOutFragments.size(); i++) {
- int containerId = firstOutFragments.keyAt(i);
- if (configureTransitions(containerId, state, isBack, firstOutFragments,
- lastInFragments)) {
- anyTransitionStarted = true;
- }
- }
-
- // Now go over all entering fragments that didn't have a leaving fragment.
- for (int i = 0; i < lastInFragments.size(); i++) {
- int containerId = lastInFragments.keyAt(i);
- if (firstOutFragments.get(containerId) == null &&
- configureTransitions(containerId, state, isBack, firstOutFragments,
- lastInFragments)) {
- anyTransitionStarted = true;
- }
- }
-
- if (!anyTransitionStarted) {
- state = null;
- }
-
- return state;
- }
-
- private static Object getEnterTransition(Fragment inFragment, boolean isBack) {
- if (inFragment == null) {
- return null;
- }
- return FragmentTransitionCompat21.cloneTransition(isBack ?
- inFragment.getReenterTransition() : inFragment.getEnterTransition());
- }
-
- private static Object getExitTransition(Fragment outFragment, boolean isBack) {
- if (outFragment == null) {
- return null;
- }
- return FragmentTransitionCompat21.cloneTransition(isBack ?
- outFragment.getReturnTransition() : outFragment.getExitTransition());
- }
-
- private static Object getSharedElementTransition(Fragment inFragment, Fragment outFragment,
- boolean isBack) {
- if (inFragment == null || outFragment == null) {
- return null;
- }
- return FragmentTransitionCompat21.wrapSharedElementTransition(isBack ?
- outFragment.getSharedElementReturnTransition() :
- inFragment.getSharedElementEnterTransition());
- }
-
- private static Object captureExitingViews(Object exitTransition, Fragment outFragment,
- ArrayList<View> exitingViews, ArrayMap<String, View> namedViews, View nonExistentView) {
- if (exitTransition != null) {
- exitTransition = FragmentTransitionCompat21.captureExitingViews(exitTransition,
- outFragment.getView(), exitingViews, namedViews, nonExistentView);
- }
- return exitTransition;
- }
-
- private ArrayMap<String, View> remapSharedElements(TransitionState state, Fragment outFragment,
- boolean isBack) {
- ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
- if (mSharedElementSourceNames != null) {
- FragmentTransitionCompat21.findNamedViews(namedViews, outFragment.getView());
- if (isBack) {
- namedViews.retainAll(mSharedElementTargetNames);
- } else {
- namedViews = remapNames(mSharedElementSourceNames, mSharedElementTargetNames,
- namedViews);
- }
- }
-
- if (isBack) {
- if (outFragment.mEnterTransitionCallback != null) {
- outFragment.mEnterTransitionCallback.onMapSharedElements(
- mSharedElementTargetNames, namedViews);
- }
- setBackNameOverrides(state, namedViews, false);
- } else {
- if (outFragment.mExitTransitionCallback != null) {
- outFragment.mExitTransitionCallback.onMapSharedElements(
- mSharedElementTargetNames, namedViews);
- }
- setNameOverrides(state, namedViews, false);
- }
-
- return namedViews;
- }
-
- /**
- * Configures custom transitions for a specific fragment container.
- *
- * @param containerId The container ID of the fragments to configure the transition for.
- * @param state The Transition State keeping track of the executing transitions.
- * @param firstOutFragments The list of first fragments to be removed, keyed on the
- * container ID.
- * @param lastInFragments The list of last fragments to be added, keyed on the
- * container ID.
- * @param isBack true if this is popping the back stack or false if this is a
- * forward operation.
- */
- private boolean configureTransitions(int containerId, TransitionState state, boolean isBack,
- SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
- ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.onFindViewById(containerId);
- if (sceneRoot == null) {
- return false;
- }
- final Fragment inFragment = lastInFragments.get(containerId);
- Fragment outFragment = firstOutFragments.get(containerId);
-
- Object enterTransition = getEnterTransition(inFragment, isBack);
- Object sharedElementTransition = getSharedElementTransition(inFragment, outFragment,
- isBack);
- Object exitTransition = getExitTransition(outFragment, isBack);
- ArrayMap<String, View> namedViews = null;
- ArrayList<View> sharedElementTargets = new ArrayList<View>();
- if (sharedElementTransition != null) {
- namedViews = remapSharedElements(state, outFragment, isBack);
- if (namedViews.isEmpty()) {
- sharedElementTransition = null;
- namedViews = null;
- } else {
- // Notify the start of the transition.
- SharedElementCallback callback = isBack ?
- outFragment.mEnterTransitionCallback :
- inFragment.mEnterTransitionCallback;
- if (callback != null) {
- ArrayList<String> names = new ArrayList<String>(namedViews.keySet());
- ArrayList<View> views = new ArrayList<View>(namedViews.values());
- callback.onSharedElementStart(names, views, null);
- }
- prepareSharedElementTransition(state, sceneRoot, sharedElementTransition,
- inFragment, outFragment, isBack, sharedElementTargets, enterTransition,
- exitTransition);
- }
- }
- if (enterTransition == null && sharedElementTransition == null &&
- exitTransition == null) {
- return false; // no transitions!
- }
-
- ArrayList<View> exitingViews = new ArrayList<View>();
- exitTransition = captureExitingViews(exitTransition, outFragment, exitingViews,
- namedViews, state.nonExistentView);
-
- // Set the epicenter of the exit transition
- if (mSharedElementTargetNames != null && namedViews != null) {
- View epicenterView = namedViews.get(mSharedElementTargetNames.get(0));
- if (epicenterView != null) {
- if (exitTransition != null) {
- FragmentTransitionCompat21.setEpicenter(exitTransition, epicenterView);
- }
- if (sharedElementTransition != null) {
- FragmentTransitionCompat21.setEpicenter(sharedElementTransition,
- epicenterView);
- }
- }
- }
-
- FragmentTransitionCompat21.ViewRetriever viewRetriever =
- new FragmentTransitionCompat21.ViewRetriever() {
- @Override
- public View getView() {
- return inFragment.getView();
- }
- };
-
- ArrayList<View> enteringViews = new ArrayList<View>();
- ArrayMap<String, View> renamedViews = new ArrayMap<String, View>();
-
- boolean allowOverlap = true;
- if (inFragment != null) {
- allowOverlap = isBack ? inFragment.getAllowReturnTransitionOverlap() :
- inFragment.getAllowEnterTransitionOverlap();
- }
- Object transition = FragmentTransitionCompat21.mergeTransitions(enterTransition,
- exitTransition, sharedElementTransition, allowOverlap);
-
- if (transition != null) {
- FragmentTransitionCompat21.addTransitionTargets(enterTransition,
- sharedElementTransition, exitTransition, sceneRoot, viewRetriever,
- state.nonExistentView, state.enteringEpicenterView, state.nameOverrides,
- enteringViews, exitingViews, namedViews, renamedViews, sharedElementTargets);
- excludeHiddenFragmentsAfterEnter(sceneRoot, state, containerId, transition);
-
- // We want to exclude hidden views later, so we need a non-null list in the
- // transition now.
- FragmentTransitionCompat21.excludeTarget(transition, state.nonExistentView, true);
- // Now exclude all currently hidden fragments.
- excludeHiddenFragments(state, containerId, transition);
-
- FragmentTransitionCompat21.beginDelayedTransition(sceneRoot, transition);
-
- FragmentTransitionCompat21.cleanupTransitions(sceneRoot, state.nonExistentView,
- enterTransition, enteringViews, exitTransition, exitingViews,
- sharedElementTransition, sharedElementTargets,
- transition, state.hiddenFragmentViews, renamedViews);
- }
- return transition != null;
- }
-
- private void prepareSharedElementTransition(final TransitionState state, final View sceneRoot,
- final Object sharedElementTransition, final Fragment inFragment,
- final Fragment outFragment, final boolean isBack,
- final ArrayList<View> sharedElementTargets, final Object enterTransition,
- final Object exitTransition) {
- if (sharedElementTransition != null) {
- sceneRoot.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
-
- // Remove the exclude for the shared elements from the exiting fragment.
- FragmentTransitionCompat21.removeTargets(sharedElementTransition,
- sharedElementTargets);
- // keep the nonExistentView as excluded so the list doesn't get emptied
- sharedElementTargets.remove(state.nonExistentView);
- FragmentTransitionCompat21.excludeSharedElementViews(enterTransition,
- exitTransition, sharedElementTransition, sharedElementTargets, false);
- sharedElementTargets.clear();
-
- ArrayMap<String, View> namedViews = mapSharedElementsIn(
- state, isBack, inFragment);
- FragmentTransitionCompat21.setSharedElementTargets(sharedElementTransition,
- state.nonExistentView, namedViews, sharedElementTargets);
-
- setEpicenterIn(namedViews, state);
-
- callSharedElementEnd(state, inFragment, outFragment, isBack,
- namedViews);
-
- // Exclude the shared elements from the entering fragment.
- FragmentTransitionCompat21.excludeSharedElementViews(enterTransition,
- exitTransition, sharedElementTransition, sharedElementTargets, true);
- return true;
- }
- });
- }
- }
-
- void callSharedElementEnd(TransitionState state, Fragment inFragment,
- Fragment outFragment, boolean isBack, ArrayMap<String, View> namedViews) {
- SharedElementCallback sharedElementCallback = isBack ?
- outFragment.mEnterTransitionCallback :
- inFragment.mEnterTransitionCallback;
- if (sharedElementCallback != null) {
- ArrayList<String> names = new ArrayList<String>(namedViews.keySet());
- ArrayList<View> views = new ArrayList<View>(namedViews.values());
- sharedElementCallback.onSharedElementEnd(names, views, null);
- }
- }
-
- void setEpicenterIn(ArrayMap<String, View> namedViews, TransitionState state) {
- if (mSharedElementTargetNames != null && !namedViews.isEmpty()) {
- // now we know the epicenter of the entering transition.
- View epicenter = namedViews
- .get(mSharedElementTargetNames.get(0));
- if (epicenter != null) {
- state.enteringEpicenterView.epicenter = epicenter;
- }
- }
- }
-
- ArrayMap<String, View> mapSharedElementsIn(TransitionState state,
- boolean isBack, Fragment inFragment) {
- // Now map the shared elements in the incoming fragment
- ArrayMap<String, View> namedViews = mapEnteringSharedElements(state, inFragment, isBack);
-
- // remap shared elements and set the name mapping used
- // in the shared element transition.
- if (isBack) {
- if (inFragment.mExitTransitionCallback != null) {
- inFragment.mExitTransitionCallback.onMapSharedElements(
- mSharedElementTargetNames, namedViews);
- }
- setBackNameOverrides(state, namedViews, true);
- } else {
- if (inFragment.mEnterTransitionCallback != null) {
- inFragment.mEnterTransitionCallback.onMapSharedElements(
- mSharedElementTargetNames, namedViews);
- }
- setNameOverrides(state, namedViews, true);
- }
- return namedViews;
- }
-
- /**
- * Remaps a name-to-View map, substituting different names for keys.
- *
- * @param inMap A list of keys found in the map, in the order in toGoInMap
- * @param toGoInMap A list of keys to use for the new map, in the order of inMap
- * @param namedViews The current mapping
- * @return A copy of namedViews with the keys coming from toGoInMap.
- */
- private static ArrayMap<String, View> remapNames(ArrayList<String> inMap,
- ArrayList<String> toGoInMap, ArrayMap<String, View> namedViews) {
- if (namedViews.isEmpty()) {
- return namedViews;
- }
- ArrayMap<String, View> remappedViews = new ArrayMap<String, View>();
- int numKeys = inMap.size();
- for (int i = 0; i < numKeys; i++) {
- View view = namedViews.get(inMap.get(i));
- if (view != null) {
- remappedViews.put(toGoInMap.get(i), view);
- }
- }
- return remappedViews;
- }
-
- /**
- * Maps shared elements to views in the entering fragment.
- *
- * @param state The transition State as returned from {@link #beginTransition(
- * android.util.SparseArray, android.util.SparseArray, boolean)}.
- * @param inFragment The last fragment to be added.
- * @param isBack true if this is popping the back stack or false if this is a
- * forward operation.
- */
- private ArrayMap<String, View> mapEnteringSharedElements(TransitionState state,
- Fragment inFragment, boolean isBack) {
- ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
- View root = inFragment.getView();
- if (root != null) {
- if (mSharedElementSourceNames != null) {
- FragmentTransitionCompat21.findNamedViews(namedViews, root);
- if (isBack) {
- namedViews = remapNames(mSharedElementSourceNames,
- mSharedElementTargetNames, namedViews);
- } else {
- namedViews.retainAll(mSharedElementTargetNames);
- }
- }
- }
- return namedViews;
- }
-
- private void excludeHiddenFragmentsAfterEnter(final View sceneRoot, final TransitionState state,
- final int containerId, final Object transition) {
- sceneRoot.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
- excludeHiddenFragments(state, containerId, transition);
- return true;
- }
- });
- }
-
- void excludeHiddenFragments(TransitionState state, int containerId, Object transition) {
- if (mManager.mAdded != null) {
- for (int i = 0; i < mManager.mAdded.size(); i++) {
- Fragment fragment = mManager.mAdded.get(i);
- if (fragment.mView != null && fragment.mContainer != null &&
- fragment.mContainerId == containerId) {
- if (fragment.mHidden) {
- if (!state.hiddenFragmentViews.contains(fragment.mView)) {
- FragmentTransitionCompat21.excludeTarget(transition, fragment.mView,
- true);
- state.hiddenFragmentViews.add(fragment.mView);
- }
- } else {
- FragmentTransitionCompat21.excludeTarget(transition, fragment.mView,
- false);
- state.hiddenFragmentViews.remove(fragment.mView);
- }
- }
- }
- }
- }
-
- private static void setNameOverride(ArrayMap<String, String> overrides,
- String source, String target) {
- if (source != null && target != null) {
- for (int index = 0; index < overrides.size(); index++) {
- if (source.equals(overrides.valueAt(index))) {
- overrides.setValueAt(index, target);
- return;
- }
- }
- overrides.put(source, target);
- }
- }
-
- private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames,
- ArrayList<String> targetNames) {
- if (sourceNames != null) {
- for (int i = 0; i < sourceNames.size(); i++) {
- String source = sourceNames.get(i);
- String target = targetNames.get(i);
- setNameOverride(state.nameOverrides, source, target);
- }
- }
- }
-
- private void setBackNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
- boolean isEnd) {
- int count = mSharedElementTargetNames == null ? 0 : mSharedElementTargetNames.size();
- for (int i = 0; i < count; i++) {
- String source = mSharedElementSourceNames.get(i);
- String originalTarget = mSharedElementTargetNames.get(i);
- View view = namedViews.get(originalTarget);
- if (view != null) {
- String target = FragmentTransitionCompat21.getTransitionName(view);
- if (isEnd) {
- setNameOverride(state.nameOverrides, source, target);
- } else {
- setNameOverride(state.nameOverrides, target, source);
- }
- }
- }
- }
-
- private void setNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
- boolean isEnd) {
- int count = namedViews.size();
- for (int i = 0; i < count; i++) {
- String source = namedViews.keyAt(i);
- String target = FragmentTransitionCompat21.getTransitionName(namedViews.valueAt(i));
- if (isEnd) {
- setNameOverride(state.nameOverrides, source, target);
- } else {
- setNameOverride(state.nameOverrides, target, source);
- }
- }
- }
-
- public class TransitionState {
- public ArrayMap<String, String> nameOverrides = new ArrayMap<String, String>();
- public ArrayList<View> hiddenFragmentViews = new ArrayList<View>();
-
- public FragmentTransitionCompat21.EpicenterView enteringEpicenterView =
- new FragmentTransitionCompat21.EpicenterView();
- public View nonExistentView;
+ return mOps.isEmpty();
}
}
diff --git a/fragment/java/android/support/v4/app/Fragment.java b/fragment/java/android/support/v4/app/Fragment.java
index f9fcc45..0adde93 100644
--- a/fragment/java/android/support/v4/app/Fragment.java
+++ b/fragment/java/android/support/v4/app/Fragment.java
@@ -16,6 +16,8 @@
package android.support.v4.app;
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
import android.app.Activity;
import android.content.ComponentCallbacks;
import android.content.Context;
@@ -24,6 +26,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Bundle;
+import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.CallSuper;
@@ -52,8 +55,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
final class FragmentState implements Parcelable {
final String mClassName;
final int mIndex;
@@ -193,15 +194,6 @@
int mState = INITIALIZING;
- // Non-null if the fragment's view hierarchy is currently animating away,
- // meaning we need to wait a bit on completely destroying it. This is the
- // view that is animating.
- View mAnimatingAway;
-
- // If mAnimatingAway != null, this is the state we should move to once the
- // animation is done.
- int mStateAfterAnimating;
-
// When instantiated from saved state, this is the saved state.
Bundle mSavedFragmentState;
SparseArray<Parcelable> mSavedViewState;
@@ -296,9 +288,6 @@
// Used to verify that subclasses call through to super class.
boolean mCalled;
- // If app has requested a specific animation, this is the one to use.
- int mNextAnim;
-
// The parent container of the fragment after dynamically added to UI.
ViewGroup mContainer;
@@ -319,17 +308,18 @@
boolean mLoadersStarted;
boolean mCheckedForLoaderManager;
- Object mEnterTransition = null;
- Object mReturnTransition = USE_DEFAULT_TRANSITION;
- Object mExitTransition = null;
- Object mReenterTransition = USE_DEFAULT_TRANSITION;
- Object mSharedElementEnterTransition = null;
- Object mSharedElementReturnTransition = USE_DEFAULT_TRANSITION;
- Boolean mAllowReturnTransitionOverlap;
- Boolean mAllowEnterTransitionOverlap;
+ // The animation and transition information for the fragment. This will be null
+ // unless the elements are explicitly accessed and should remain null for Fragments
+ // without Views.
+ AnimationInfo mAnimationInfo;
- SharedElementCallback mEnterTransitionCallback = null;
- SharedElementCallback mExitTransitionCallback = null;
+ // True if the View was added, and its animation has yet to be run. This could
+ // also indicate that the fragment view hasn't been made visible, even if there is no
+ // animation for this fragment.
+ boolean mIsNewlyAdded;
+
+ // True if mHidden has been changed and the animation should be scheduled.
+ boolean mHiddenChanged;
/**
* State information that has been retrieved from a fragment instance
@@ -721,6 +711,14 @@
}
/**
+ * Return this fragment's child FragmentManager one has been previously created,
+ * otherwise null.
+ */
+ FragmentManager peekChildFragmentManager() {
+ return mChildFragmentManager;
+ }
+
+ /**
* Returns the parent Fragment containing this Fragment. If this Fragment
* is attached directly to an Activity, returns null.
*/
@@ -1702,7 +1700,7 @@
* when added not as a pop from the back stack.
*/
public void setEnterSharedElementCallback(SharedElementCallback callback) {
- mEnterTransitionCallback = callback;
+ ensureAnimationInfo().mEnterTransitionCallback = callback;
}
/**
@@ -1713,7 +1711,7 @@
* when added as a pop from the back stack.
*/
public void setExitSharedElementCallback(SharedElementCallback callback) {
- mExitTransitionCallback = callback;
+ ensureAnimationInfo().mExitTransitionCallback = callback;
}
/**
@@ -1727,7 +1725,7 @@
* @param transition The Transition to use to move Views into the initial Scene.
*/
public void setEnterTransition(Object transition) {
- mEnterTransition = transition;
+ ensureAnimationInfo().mEnterTransition = transition;
}
/**
@@ -1740,7 +1738,10 @@
* @return the Transition to use to move Views into the initial Scene.
*/
public Object getEnterTransition() {
- return mEnterTransition;
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mEnterTransition;
}
/**
@@ -1758,7 +1759,7 @@
* android.transition.Transition.
*/
public void setReturnTransition(Object transition) {
- mReturnTransition = transition;
+ ensureAnimationInfo().mReturnTransition = transition;
}
/**
@@ -1774,8 +1775,11 @@
* is preparing to close.
*/
public Object getReturnTransition() {
- return mReturnTransition == USE_DEFAULT_TRANSITION ? getEnterTransition()
- : mReturnTransition;
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mReturnTransition == USE_DEFAULT_TRANSITION ? getEnterTransition()
+ : mAnimationInfo.mReturnTransition;
}
/**
@@ -1792,7 +1796,7 @@
* must be an android.transition.Transition.
*/
public void setExitTransition(Object transition) {
- mExitTransition = transition;
+ ensureAnimationInfo().mExitTransition = transition;
}
/**
@@ -1808,7 +1812,10 @@
* is being closed not due to popping the back stack.
*/
public Object getExitTransition() {
- return mExitTransition;
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mExitTransition;
}
/**
@@ -1825,7 +1832,7 @@
* must be an android.transition.Transition.
*/
public void setReenterTransition(Object transition) {
- mReenterTransition = transition;
+ ensureAnimationInfo().mReenterTransition = transition;
}
/**
@@ -1841,8 +1848,11 @@
* previously-started Activity.
*/
public Object getReenterTransition() {
- return mReenterTransition == USE_DEFAULT_TRANSITION ? getExitTransition()
- : mReenterTransition;
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mReenterTransition == USE_DEFAULT_TRANSITION ? getExitTransition()
+ : mAnimationInfo.mReenterTransition;
}
/**
@@ -1855,7 +1865,7 @@
* Scene. <code>transition</code> must be an android.transition.Transition.
*/
public void setSharedElementEnterTransition(Object transition) {
- mSharedElementEnterTransition = transition;
+ ensureAnimationInfo().mSharedElementEnterTransition = transition;
}
/**
@@ -1868,7 +1878,10 @@
* Scene.
*/
public Object getSharedElementEnterTransition() {
- return mSharedElementEnterTransition;
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mSharedElementEnterTransition;
}
/**
@@ -1884,7 +1897,7 @@
* Scene. <code>transition</code> must be an android.transition.Transition.
*/
public void setSharedElementReturnTransition(Object transition) {
- mSharedElementReturnTransition = transition;
+ ensureAnimationInfo().mSharedElementReturnTransition = transition;
}
/**
@@ -1900,8 +1913,12 @@
* Scene.
*/
public Object getSharedElementReturnTransition() {
- return mSharedElementReturnTransition == USE_DEFAULT_TRANSITION ?
- getSharedElementEnterTransition() : mSharedElementReturnTransition;
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mSharedElementReturnTransition == USE_DEFAULT_TRANSITION
+ ? getSharedElementEnterTransition()
+ : mAnimationInfo.mSharedElementReturnTransition;
}
/**
@@ -1913,7 +1930,7 @@
* wait until the exiting transition completes.
*/
public void setAllowEnterTransitionOverlap(boolean allow) {
- mAllowEnterTransitionOverlap = allow;
+ ensureAnimationInfo().mAllowEnterTransitionOverlap = allow;
}
/**
@@ -1925,7 +1942,8 @@
* when it should wait until the exiting transition completes.
*/
public boolean getAllowEnterTransitionOverlap() {
- return (mAllowEnterTransitionOverlap == null) ? true : mAllowEnterTransitionOverlap;
+ return (mAnimationInfo == null || mAnimationInfo.mAllowEnterTransitionOverlap == null)
+ ? true : mAnimationInfo.mAllowEnterTransitionOverlap;
}
/**
@@ -1937,7 +1955,7 @@
* return transition completes.
*/
public void setAllowReturnTransitionOverlap(boolean allow) {
- mAllowReturnTransitionOverlap = allow;
+ ensureAnimationInfo().mAllowReturnTransitionOverlap = allow;
}
/**
@@ -1949,7 +1967,83 @@
* return transition completes.
*/
public boolean getAllowReturnTransitionOverlap() {
- return (mAllowReturnTransitionOverlap == null) ? true : mAllowReturnTransitionOverlap;
+ return (mAnimationInfo == null || mAnimationInfo.mAllowReturnTransitionOverlap == null)
+ ? true : mAnimationInfo.mAllowReturnTransitionOverlap;
+ }
+
+ /**
+ * Postpone the entering Fragment transition until {@link #startPostponedEnterTransition()}
+ * or {@link FragmentManager#executePendingTransactions()} has been called.
+ * <p>
+ * This method gives the Fragment the ability to delay Fragment animations
+ * until all data is loaded. Until then, the added, shown, and
+ * attached Fragments will be INVISIBLE and removed, hidden, and detached Fragments won't
+ * be have their Views removed. The transaction runs when all postponed added Fragments in the
+ * transaction have called {@link #startPostponedEnterTransition()}.
+ * <p>
+ * This method should be called before being added to the FragmentTransaction or
+ * in {@link #onCreate(Bundle), {@link #onAttach(Context)}, or
+ * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}}.
+ * {@link #startPostponedEnterTransition()} must be called to allow the Fragment to
+ * start the transitions.
+ * <p>
+ * When a FragmentTransaction is started that may affect a postponed FragmentTransaction,
+ * based on which containers are in their operations, the postponed FragmentTransaction
+ * will have its start triggered. The early triggering may result in faulty or nonexistent
+ * animations in the postponed transaction. FragmentTransactions that operate only on
+ * independent containers will not interfere with each other's postponement.
+ * <p>
+ * Calling postponeEnterTransition on Fragments with a null View will not postpone the
+ * transition. Likewise, postponement only works if FragmentTransaction optimizations are
+ * enabled.
+ *
+ * @see Activity#postponeEnterTransition()
+ * @see FragmentTransaction#setAllowOptimization(boolean)
+ */
+ public void postponeEnterTransition() {
+ ensureAnimationInfo().mEnterTransitionPostponed = true;
+ }
+
+ /**
+ * Begin postponed transitions after {@link #postponeEnterTransition()} was called.
+ * If postponeEnterTransition() was called, you must call startPostponedEnterTransition()
+ * or {@link FragmentManager#executePendingTransactions()} to complete the FragmentTransaction.
+ * If postponement was interrupted with {@link FragmentManager#executePendingTransactions()},
+ * before {@code startPostponedEnterTransition()}, animations may not run or may execute
+ * improperly.
+ *
+ * @see Activity#startPostponedEnterTransition()
+ */
+ public void startPostponedEnterTransition() {
+ if (mFragmentManager == null || mFragmentManager.mHost == null) {
+ ensureAnimationInfo().mEnterTransitionPostponed = false;
+ } else if (Looper.myLooper() != mFragmentManager.mHost.getHandler().getLooper()) {
+ mFragmentManager.mHost.getHandler().postAtFrontOfQueue(new Runnable() {
+ @Override
+ public void run() {
+ callStartTransitionListener();
+ }
+ });
+ } else {
+ callStartTransitionListener();
+ }
+ }
+
+ /**
+ * Calls the start transition listener. This must be called on the UI thread.
+ */
+ private void callStartTransitionListener() {
+ final OnStartEnterTransitionListener listener;
+ if (mAnimationInfo == null) {
+ listener = null;
+ } else {
+ mAnimationInfo.mEnterTransitionPostponed = false;
+ listener = mAnimationInfo.mStartEnterTransitionListener;
+ mAnimationInfo.mStartEnterTransitionListener = null;
+ }
+ if (listener != null) {
+ listener.onStartEnterTransition();
+ }
}
/**
@@ -2010,8 +2104,8 @@
writer.print(" mTargetRequestCode=");
writer.println(mTargetRequestCode);
}
- if (mNextAnim != 0) {
- writer.print(prefix); writer.print("mNextAnim="); writer.println(mNextAnim);
+ if (getNextAnim() != 0) {
+ writer.print(prefix); writer.print("mNextAnim="); writer.println(getNextAnim());
}
if (mContainer != null) {
writer.print(prefix); writer.print("mContainer="); writer.println(mContainer);
@@ -2022,10 +2116,13 @@
if (mInnerView != null) {
writer.print(prefix); writer.print("mInnerView="); writer.println(mView);
}
- if (mAnimatingAway != null) {
- writer.print(prefix); writer.print("mAnimatingAway="); writer.println(mAnimatingAway);
- writer.print(prefix); writer.print("mStateAfterAnimating=");
- writer.println(mStateAfterAnimating);
+ if (getAnimatingAway() != null) {
+ writer.print(prefix);
+ writer.print("mAnimatingAway=");
+ writer.println(getAnimatingAway());
+ writer.print(prefix);
+ writer.print("mStateAfterAnimating=");
+ writer.println(getStateAfterAnimating());
}
if (mLoaderManager != null) {
writer.print(prefix); writer.println("Loader Manager:");
@@ -2355,4 +2452,175 @@
}
}
+ void setOnStartEnterTransitionListener(OnStartEnterTransitionListener listener) {
+ ensureAnimationInfo();
+ if (listener == mAnimationInfo.mStartEnterTransitionListener) {
+ return;
+ }
+ if (listener != null && mAnimationInfo.mStartEnterTransitionListener != null) {
+ throw new IllegalStateException("Trying to set a replacement "
+ + "startPostponedEnterTransition on " + this);
+ }
+ if (mAnimationInfo.mEnterTransitionPostponed) {
+ mAnimationInfo.mStartEnterTransitionListener = listener;
+ }
+ if (listener != null) {
+ listener.startListening();
+ }
+ }
+
+ private AnimationInfo ensureAnimationInfo() {
+ if (mAnimationInfo == null) {
+ mAnimationInfo = new AnimationInfo();
+ }
+ return mAnimationInfo;
+ }
+
+ int getNextAnim() {
+ if (mAnimationInfo == null) {
+ return 0;
+ }
+ return mAnimationInfo.mNextAnim;
+ }
+
+ void setNextAnim(int animResourceId) {
+ if (mAnimationInfo == null && animResourceId == 0) {
+ return; // no change!
+ }
+ ensureAnimationInfo().mNextAnim = animResourceId;
+ }
+
+ int getNextTransition() {
+ if (mAnimationInfo == null) {
+ return 0;
+ }
+ return mAnimationInfo.mNextTransition;
+ }
+
+ void setNextTransition(int nextTransition, int nextTransitionStyle) {
+ if (mAnimationInfo == null && nextTransition == 0 && nextTransitionStyle == 0) {
+ return; // no change!
+ }
+ ensureAnimationInfo();
+ mAnimationInfo.mNextTransition = nextTransition;
+ mAnimationInfo.mNextTransitionStyle = nextTransitionStyle;
+ }
+
+ int getNextTransitionStyle() {
+ if (mAnimationInfo == null) {
+ return 0;
+ }
+ return mAnimationInfo.mNextTransitionStyle;
+ }
+
+ SharedElementCallback getEnterTransitionCallback() {
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mEnterTransitionCallback;
+ }
+
+ SharedElementCallback getExitTransitionCallback() {
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mExitTransitionCallback;
+ }
+
+ View getAnimatingAway() {
+ if (mAnimationInfo == null) {
+ return null;
+ }
+ return mAnimationInfo.mAnimatingAway;
+ }
+
+ void setAnimatingAway(View view) {
+ ensureAnimationInfo().mAnimatingAway = view;
+ }
+
+ int getStateAfterAnimating() {
+ if (mAnimationInfo == null) {
+ return 0;
+ }
+ return mAnimationInfo.mStateAfterAnimating;
+ }
+
+ void setStateAfterAnimating(int state) {
+ ensureAnimationInfo().mStateAfterAnimating = state;
+ }
+
+ boolean isPostponed() {
+ if (mAnimationInfo == null) {
+ return false;
+ }
+ return mAnimationInfo.mEnterTransitionPostponed;
+ }
+
+ boolean isHideReplaced() {
+ if (mAnimationInfo == null) {
+ return false;
+ }
+ return mAnimationInfo.mIsHideReplaced;
+ }
+
+ void setHideReplaced(boolean replaced) {
+ ensureAnimationInfo().mIsHideReplaced = replaced;
+ }
+
+ /**
+ * Used internally to be notified when {@link #startPostponedEnterTransition()} has
+ * been called. This listener will only be called once and then be removed from the
+ * listeners.
+ */
+ interface OnStartEnterTransitionListener {
+ void onStartEnterTransition();
+ void startListening();
+ }
+
+ /**
+ * Contains all the animation and transition information for a fragment. This will only
+ * be instantiated for Fragments that have Views.
+ */
+ static class AnimationInfo {
+ // Non-null if the fragment's view hierarchy is currently animating away,
+ // meaning we need to wait a bit on completely destroying it. This is the
+ // view that is animating.
+ View mAnimatingAway;
+
+ // If mAnimatingAway != null, this is the state we should move to once the
+ // animation is done.
+ int mStateAfterAnimating;
+
+ // If app has requested a specific animation, this is the one to use.
+ int mNextAnim;
+
+ // If app has requested a specific transition, this is the one to use.
+ int mNextTransition;
+
+ // If app has requested a specific transition style, this is the one to use.
+ int mNextTransitionStyle;
+
+ private Object mEnterTransition = null;
+ private Object mReturnTransition = USE_DEFAULT_TRANSITION;
+ private Object mExitTransition = null;
+ private Object mReenterTransition = USE_DEFAULT_TRANSITION;
+ private Object mSharedElementEnterTransition = null;
+ private Object mSharedElementReturnTransition = USE_DEFAULT_TRANSITION;
+ private Boolean mAllowReturnTransitionOverlap;
+ private Boolean mAllowEnterTransitionOverlap;
+
+ SharedElementCallback mEnterTransitionCallback = null;
+ SharedElementCallback mExitTransitionCallback = null;
+
+ // True when postponeEnterTransition has been called and startPostponeEnterTransition
+ // hasn't been called yet.
+ boolean mEnterTransitionPostponed;
+
+ // Listener to wait for startPostponeEnterTransition. After being called, it will
+ // be set to null
+ OnStartEnterTransitionListener mStartEnterTransitionListener;
+
+ // True if the View was hidden, but the transition is handling the hide
+ boolean mIsHideReplaced;
+ }
}
diff --git a/fragment/java/android/support/v4/app/FragmentActivity.java b/fragment/java/android/support/v4/app/FragmentActivity.java
index 4542962..50fa584 100644
--- a/fragment/java/android/support/v4/app/FragmentActivity.java
+++ b/fragment/java/android/support/v4/app/FragmentActivity.java
@@ -16,12 +16,15 @@
package android.support.v4.app;
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -45,8 +48,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
/**
* Base class for activities that want to use the support-based
* {@link android.support.v4.app.Fragment} and
@@ -85,9 +86,6 @@
static final String REQUEST_FRAGMENT_WHO_TAG = "android:support:request_fragment_who";
static final int MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS = 0xffff - 1;
- // This is the SDK API version of Honeycomb (3.0).
- private static final int HONEYCOMB = 11;
-
static final int MSG_REALLY_STOPPED = 1;
static final int MSG_RESUME_PENDING = 2;
@@ -361,7 +359,7 @@
if (featureId == Window.FEATURE_OPTIONS_PANEL) {
boolean show = super.onCreatePanelMenu(featureId, menu);
show |= mFragments.dispatchCreateOptionsMenu(menu, getMenuInflater());
- if (android.os.Build.VERSION.SDK_INT >= HONEYCOMB) {
+ if (android.os.Build.VERSION.SDK_INT >= 11) {
return show;
}
// Prior to Honeycomb, the framework can't invalidate the options
@@ -658,7 +656,7 @@
* onPrepareOptionsMenu the next time the menu is requested.
*/
public void supportInvalidateOptionsMenu() {
- if (android.os.Build.VERSION.SDK_INT >= HONEYCOMB) {
+ if (android.os.Build.VERSION.SDK_INT >= 11) {
// If we are running on HC or greater, we can use the framework
// API to invalidate the options menu.
ActivityCompatHoneycomb.invalidateOptionsMenu(this);
@@ -681,7 +679,7 @@
* @param args additional arguments to the dump request.
*/
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
- if (android.os.Build.VERSION.SDK_INT >= HONEYCOMB) {
+ if (Build.VERSION.SDK_INT >= 11) {
// XXX This can only work if we can call the super-class impl. :/
//ActivityCompatHoneycomb.dump(this, prefix, fd, writer, args);
}
diff --git a/fragment/java/android/support/v4/app/FragmentHostCallback.java b/fragment/java/android/support/v4/app/FragmentHostCallback.java
index 75fde03..dad181a 100644
--- a/fragment/java/android/support/v4/app/FragmentHostCallback.java
+++ b/fragment/java/android/support/v4/app/FragmentHostCallback.java
@@ -55,7 +55,8 @@
private boolean mLoadersStarted;
public FragmentHostCallback(Context context, Handler handler, int windowAnimations) {
- this(null /*activity*/, context, handler, windowAnimations);
+ this(context instanceof Activity ? (Activity) context : null, context, handler,
+ windowAnimations);
}
FragmentHostCallback(FragmentActivity activity) {
diff --git a/fragment/java/android/support/v4/app/FragmentManager.java b/fragment/java/android/support/v4/app/FragmentManager.java
index 3baf4f9..6ef6ed1 100644
--- a/fragment/java/android/support/v4/app/FragmentManager.java
+++ b/fragment/java/android/support/v4/app/FragmentManager.java
@@ -22,7 +22,6 @@
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,25 +32,27 @@
import android.support.v4.os.BuildCompat;
import android.support.v4.util.DebugUtils;
import android.support.v4.util.LogWriter;
+import android.support.v4.util.Pair;
import android.support.v4.view.LayoutInflaterFactory;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.ScaleAnimation;
-import android.view.animation.Animation.AnimationListener;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.ScaleAnimation;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -59,6 +60,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
@@ -176,6 +178,9 @@
* {@link FragmentTransaction#commitNow()} instead. This can help avoid
* unwanted side effects when other code in your app has pending committed
* transactions that expect different timing.</p>
+ * <p>
+ * This also forces the start of any postponed Transactions where
+ * {@link Fragment#postponeEnterTransition()} has been called.
*
* @return Returns true if there were any pending transactions to be
* executed.
@@ -223,7 +228,7 @@
/**
* Like {@link #popBackStack()}, but performs the operation immediately
* inside of the call. This is like calling {@link #executePendingTransactions()}
- * afterwards.
+ * afterwards without forcing the start of postponed Transactions.
* @return Returns true if there was something popped, else false.
*/
public abstract boolean popBackStackImmediate();
@@ -246,7 +251,7 @@
/**
* Like {@link #popBackStack(String, int)}, but performs the operation immediately
* inside of the call. This is like calling {@link #executePendingTransactions()}
- * afterwards.
+ * afterwards without forcing the start of postponed Transactions.
* @return Returns true if there was something popped, else false.
*/
public abstract boolean popBackStackImmediate(String name, int flags);
@@ -270,7 +275,7 @@
/**
* Like {@link #popBackStack(int, int)}, but performs the operation immediately
* inside of the call. This is like calling {@link #executePendingTransactions()}
- * afterwards.
+ * afterwards without forcing the start of postponed Transactions.
* @return Returns true if there was something popped, else false.
*/
public abstract boolean popBackStackImmediate(int id, int flags);
@@ -360,6 +365,26 @@
public abstract boolean isDestroyed();
/**
+ * Registers a {@link FragmentLifecycleCallbacks} to listen to fragment lifecycle events
+ * happening in this FragmentManager. All registered callbacks will be automatically
+ * unregistered when this FragmentManager is destroyed.
+ *
+ * @param cb Callbacks to register
+ * @param recursive true to automatically register this callback for all child FragmentManagers
+ */
+ public abstract void registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb,
+ boolean recursive);
+
+ /**
+ * Unregisters a previously registered {@link FragmentLifecycleCallbacks}. If the callback
+ * was not previously registered this call has no effect. All registered callbacks will be
+ * automatically unregistered when this FragmentManager is destroyed.
+ *
+ * @param cb Callbacks to unregister
+ */
+ public abstract void unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb);
+
+ /**
* Print the FragmentManager's state into the given stream.
*
* @param prefix Text to print at the front of each line.
@@ -377,6 +402,141 @@
public static void enableDebugLogging(boolean enabled) {
FragmentManagerImpl.DEBUG = enabled;
}
+
+ /**
+ * Callback interface for listening to fragment state changes that happen
+ * within a given FragmentManager.
+ */
+ public abstract class FragmentLifecycleCallbacks {
+ /**
+ * Called right before the fragment's {@link Fragment#onAttach(Context)} method is called.
+ * This is a good time to inject any required dependencies for the fragment before any of
+ * the fragment's lifecycle methods are invoked.
+ *
+ * @param fm Host FragmentManager
+ * @param f Fragment changing state
+ * @param context Context that the Fragment is being attached to
+ */
+ public void onFragmentPreAttached(FragmentManager fm, Fragment f, Context context) {}
+
+ /**
+ * Called after the fragment has been attached to its host. Its host will have had
+ * <code>onAttachFragment</code> called before this call happens.
+ *
+ * @param fm Host FragmentManager
+ * @param f Fragment changing state
+ * @param context Context that the Fragment was attached to
+ */
+ public void onFragmentAttached(FragmentManager fm, Fragment f, Context context) {}
+
+ /**
+ * Called after the fragment has returned from the FragmentManager's call to
+ * {@link Fragment#onCreate(Bundle)}. This will only happen once for any given
+ * fragment instance, though the fragment may be attached and detached multiple times.
+ *
+ * @param fm Host FragmentManager
+ * @param f Fragment changing state
+ * @param savedInstanceState Saved instance bundle from a previous instance
+ */
+ public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {}
+
+ /**
+ * Called after the fragment has returned from the FragmentManager's call to
+ * {@link Fragment#onActivityCreated(Bundle)}. This will only happen once for any given
+ * fragment instance, though the fragment may be attached and detached multiple times.
+ *
+ * @param fm Host FragmentManager
+ * @param f Fragment changing state
+ * @param savedInstanceState Saved instance bundle from a previous instance
+ */
+ public void onFragmentActivityCreated(FragmentManager fm, Fragment f,
+ Bundle savedInstanceState) {}
+
+ /**
+ * Called after the fragment has returned a non-null view from the FragmentManager's
+ * request to {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}.
+ *
+ * @param fm Host FragmentManager
+ * @param f Fragment that created and owns the view
+ * @param v View returned by the fragment
+ * @param savedInstanceState Saved instance bundle from a previous instance
+ */
+ public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v,
+ Bundle savedInstanceState) {}
+
+ /**
+ * Called after the fragment has returned from the FragmentManager's call to
+ * {@link Fragment#onStart()}.
+ *
+ * @param fm Host FragmentManager
+ * @param f Fragment changing state
+ */
+ public void onFragmentStarted(FragmentManager fm, Fragment f) {}
+
+ /**
+ * Called after the fragment has returned from the FragmentManager's call to
+ * {@link Fragment#onResume()}.
+ *
+ * @param fm Host FragmentManager
+ * @param f Fragment changing state
+ */
+ public void onFragmentResumed(FragmentManager fm, Fragment f) {}
+
+ /**
+ * Called after the fragment has returned from the FragmentManager's call to
+ * {@link Fragment#onPause()}.
+ *
+ * @param fm Host FragmentManager
+ * @param f Fragment changing state
+ */
+ public void onFragmentPaused(FragmentManager fm, Fragment f) {}
+
+ /**
+ * Called after the fragment has returned from the FragmentManager's call to
+ * {@link Fragment#onStop()}.
+ *
+ * @param fm Host FragmentManager
+ * @param f Fragment changing state
+ */
+ public void onFragmentStopped(FragmentManager fm, Fragment f) {}
+
+ /**
+ * Called after the fragment has returned from the FragmentManager's call to
+ * {@link Fragment#onSaveInstanceState(Bundle)}.
+ *
+ * @param fm Host FragmentManager
+ * @param f Fragment changing state
+ * @param outState Saved state bundle for the fragment
+ */
+ public void onFragmentSaveInstanceState(FragmentManager fm, Fragment f, Bundle outState) {}
+
+ /**
+ * Called after the fragment has returned from the FragmentManager's call to
+ * {@link Fragment#onDestroyView()}.
+ *
+ * @param fm Host FragmentManager
+ * @param f Fragment changing state
+ */
+ public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {}
+
+ /**
+ * Called after the fragment has returned from the FragmentManager's call to
+ * {@link Fragment#onDestroy()}.
+ *
+ * @param fm Host FragmentManager
+ * @param f Fragment changing state
+ */
+ public void onFragmentDestroyed(FragmentManager fm, Fragment f) {}
+
+ /**
+ * Called after the fragment has returned from the FragmentManager's call to
+ * {@link Fragment#onDetach()}.
+ *
+ * @param fm Host FragmentManager
+ * @param f Fragment changing state
+ */
+ public void onFragmentDetached(FragmentManager fm, Fragment f) {}
+ }
}
final class FragmentManagerState implements Parcelable {
@@ -501,7 +661,7 @@
}
}
- ArrayList<Runnable> mPendingActions;
+ ArrayList<OpGenerator> mPendingActions;
Runnable[] mTmpActions;
boolean mExecutingActions;
@@ -516,10 +676,10 @@
ArrayList<Integer> mAvailBackStackIndices;
ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
+ private CopyOnWriteArrayList<Pair<FragmentLifecycleCallbacks, Boolean>> mLifecycleCallbacks;
int mCurState = Fragment.INITIALIZING;
FragmentHostCallback mHost;
- FragmentController mController;
FragmentContainer mContainer;
Fragment mParent;
@@ -531,10 +691,18 @@
String mNoTransactionsBecause;
boolean mHavePendingDeferredStart;
+ // Temporary vars for optimizing execution of BackStackRecords:
+ ArrayList<BackStackRecord> mTmpRecords;
+ ArrayList<Boolean> mTmpIsPop;
+ ArrayList<Fragment> mTmpAddedFragments;
+
// Temporary vars for state save and restore.
Bundle mStateBundle = null;
SparseArray<Parcelable> mStateArray = null;
+ // Postponed transactions.
+ ArrayList<StartEnterTransitionListener> mPostponedTransactions;
+
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
@@ -591,39 +759,31 @@
@Override
public boolean executePendingTransactions() {
- return execPendingActions();
+ boolean updates = execPendingActions();
+ forcePostponedTransactions();
+ return updates;
}
@Override
public void popBackStack() {
- enqueueAction(new Runnable() {
- @Override public void run() {
- popBackStackState(mHost.getHandler(), null, -1, 0);
- }
- }, false);
+ enqueueAction(new PopBackStackState(null, -1, 0), false);
}
@Override
public boolean popBackStackImmediate() {
checkStateLoss();
- executePendingTransactions();
- return popBackStackState(mHost.getHandler(), null, -1, 0);
+ return popBackStackImmediate(null, -1, 0);
}
@Override
public void popBackStack(final String name, final int flags) {
- enqueueAction(new Runnable() {
- @Override public void run() {
- popBackStackState(mHost.getHandler(), name, -1, flags);
- }
- }, false);
+ enqueueAction(new PopBackStackState(name, -1, flags), false);
}
@Override
public boolean popBackStackImmediate(String name, int flags) {
checkStateLoss();
- executePendingTransactions();
- return popBackStackState(mHost.getHandler(), name, -1, flags);
+ return popBackStackImmediate(name, -1, flags);
}
@Override
@@ -631,21 +791,42 @@
if (id < 0) {
throw new IllegalArgumentException("Bad id: " + id);
}
- enqueueAction(new Runnable() {
- @Override public void run() {
- popBackStackState(mHost.getHandler(), null, id, flags);
- }
- }, false);
+ enqueueAction(new PopBackStackState(null, id, flags), false);
}
@Override
public boolean popBackStackImmediate(int id, int flags) {
checkStateLoss();
- executePendingTransactions();
+ execPendingActions();
if (id < 0) {
throw new IllegalArgumentException("Bad id: " + id);
}
- return popBackStackState(mHost.getHandler(), null, id, flags);
+ return popBackStackImmediate(null, id, flags);
+ }
+
+ /**
+ * Used by all public popBackStackImmediate methods, this executes pending transactions and
+ * returns true if the pop action did anything, regardless of what other pending
+ * transactions did.
+ *
+ * @return true if the pop operation did anything or false otherwise.
+ */
+ private boolean popBackStackImmediate(String name, int id, int flags) {
+ execPendingActions();
+ ensureExecReady(true);
+
+ boolean executePop = popBackStackState(mTmpRecords, mTmpIsPop, name, id, flags);
+ if (executePop) {
+ mExecutingActions = true;
+ try {
+ optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
+ } finally {
+ cleanupExec();
+ }
+ }
+
+ doPendingDeferredStart();
+ return executePop;
}
@Override
@@ -821,7 +1002,7 @@
if (N > 0) {
writer.print(prefix); writer.println("Pending Actions:");
for (int i=0; i<N; i++) {
- Runnable r = mPendingActions.get(i);
+ OpGenerator r = mPendingActions.get(i);
writer.print(prefix); writer.print(" #"); writer.print(i);
writer.print(": "); writer.println(r);
}
@@ -882,14 +1063,14 @@
Animation loadAnimation(Fragment fragment, int transit, boolean enter,
int transitionStyle) {
- Animation animObj = fragment.onCreateAnimation(transit, enter,
- fragment.mNextAnim);
+ Animation animObj = fragment.onCreateAnimation(transit, enter, fragment.getNextAnim());
if (animObj != null) {
return animObj;
}
- if (fragment.mNextAnim != 0) {
- Animation anim = AnimationUtils.loadAnimation(mHost.getContext(), fragment.mNextAnim);
+ if (fragment.getNextAnim() != 0) {
+ Animation anim = AnimationUtils.loadAnimation(mHost.getContext(),
+ fragment.getNextAnim());
if (anim != null) {
return anim;
}
@@ -1010,13 +1191,13 @@
if (f.mFromLayout && !f.mInLayout) {
return;
}
- if (f.mAnimatingAway != null) {
+ if (f.getAnimatingAway() != null) {
// The fragment is currently being animated... but! Now we
// want to move our state back up. Give up on waiting for the
// animation, move to whatever the final state should be once
// the animation is done, and then we can proceed from there.
- f.mAnimatingAway = null;
- moveToState(f, f.mStateAfterAnimating, 0, 0, true);
+ f.setAnimatingAway(null);
+ moveToState(f, f.getStateAfterAnimating(), 0, 0, true);
}
switch (f.mState) {
case Fragment.INITIALIZING:
@@ -1044,6 +1225,7 @@
f.mParentFragment = mParent;
f.mFragmentManager = mParent != null
? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
+ dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
f.mCalled = false;
f.onAttach(mHost.getContext());
if (!f.mCalled) {
@@ -1055,9 +1237,11 @@
} else {
f.mParentFragment.onAttachFragment(f);
}
+ dispatchOnFragmentAttached(f, mHost.getContext(), false);
if (!f.mRetaining) {
f.performCreate(f.mSavedFragmentState);
+ dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
} else {
f.restoreChildFragmentState(f.mSavedFragmentState);
f.mState = Fragment.CREATED;
@@ -1078,6 +1262,7 @@
}
if (f.mHidden) f.mView.setVisibility(View.GONE);
f.onViewCreated(f.mView, f.mSavedFragmentState);
+ dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false);
} else {
f.mInnerView = null;
}
@@ -1120,22 +1305,23 @@
f.mView = NoSaveStateFrameLayout.wrap(f.mView);
}
if (container != null) {
- Animation anim = loadAnimation(f, transit, true,
- transitionStyle);
- if (anim != null) {
- setHWLayerAnimListenerIfAlpha(f.mView, anim);
- f.mView.startAnimation(anim);
- }
container.addView(f.mView);
+ f.mIsNewlyAdded = true;
}
- if (f.mHidden) f.mView.setVisibility(View.GONE);
+ if (f.mHidden) {
+ f.mView.setVisibility(View.GONE);
+ f.mIsNewlyAdded = false; // No animation
+ }
f.onViewCreated(f.mView, f.mSavedFragmentState);
+ dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
+ false);
} else {
f.mInnerView = null;
}
}
f.performActivityCreated(f.mSavedFragmentState);
+ dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
if (f.mView != null) {
f.restoreViewState(f.mSavedFragmentState);
}
@@ -1149,11 +1335,13 @@
if (newState > Fragment.STOPPED) {
if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
f.performStart();
+ dispatchOnFragmentStarted(f, false);
}
case Fragment.STARTED:
if (newState > Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
f.performResume();
+ dispatchOnFragmentResumed(f, false);
f.mSavedFragmentState = null;
f.mSavedViewState = null;
}
@@ -1164,11 +1352,13 @@
if (newState < Fragment.RESUMED) {
if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
f.performPause();
+ dispatchOnFragmentPaused(f, false);
}
case Fragment.STARTED:
if (newState < Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
f.performStop();
+ dispatchOnFragmentStopped(f, false);
}
case Fragment.STOPPED:
if (newState < Fragment.STOPPED) {
@@ -1186,25 +1376,27 @@
}
}
f.performDestroyView();
+ dispatchOnFragmentViewDestroyed(f, false);
if (f.mView != null && f.mContainer != null) {
Animation anim = null;
- if (mCurState > Fragment.INITIALIZING && !mDestroyed) {
+ if (mCurState > Fragment.INITIALIZING && !mDestroyed
+ && f.mView.getVisibility() == View.VISIBLE) {
anim = loadAnimation(f, transit, false,
transitionStyle);
}
if (anim != null) {
final Fragment fragment = f;
- f.mAnimatingAway = f.mView;
- f.mStateAfterAnimating = newState;
+ f.setAnimatingAway(f.mView);
+ f.setStateAfterAnimating(newState);
final View viewToAnimate = f.mView;
anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(
viewToAnimate, anim) {
@Override
public void onAnimationEnd(Animation animation) {
super.onAnimationEnd(animation);
- if (fragment.mAnimatingAway != null) {
- fragment.mAnimatingAway = null;
- moveToState(fragment, fragment.mStateAfterAnimating,
+ if (fragment.getAnimatingAway() != null) {
+ fragment.setAnimatingAway(null);
+ moveToState(fragment, fragment.getStateAfterAnimating(),
0, 0, false);
}
}
@@ -1220,34 +1412,36 @@
case Fragment.CREATED:
if (newState < Fragment.CREATED) {
if (mDestroyed) {
- if (f.mAnimatingAway != null) {
+ if (f.getAnimatingAway() != null) {
// The fragment's containing activity is
// being destroyed, but this fragment is
// currently animating away. Stop the
// animation right now -- it is not needed,
// and we can't wait any more on destroying
// the fragment.
- View v = f.mAnimatingAway;
- f.mAnimatingAway = null;
+ View v = f.getAnimatingAway();
+ f.setAnimatingAway(null);
v.clearAnimation();
}
}
- if (f.mAnimatingAway != null) {
+ if (f.getAnimatingAway() != null) {
// We are waiting for the fragment's view to finish
// animating away. Just make a note of the state
// the fragment now should move to once the animation
// is done.
- f.mStateAfterAnimating = newState;
+ f.setStateAfterAnimating(newState);
newState = Fragment.CREATED;
} else {
if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
if (!f.mRetaining) {
f.performDestroy();
+ dispatchOnFragmentDestroyed(f, false);
} else {
f.mState = Fragment.INITIALIZING;
}
f.performDetach();
+ dispatchOnFragmentDetached(f, false);
if (!keepActive) {
if (!f.mRetaining) {
makeInactive(f);
@@ -1273,26 +1467,122 @@
moveToState(f, mCurState, 0, 0, false);
}
- void moveToState(int newState, boolean always) {
- moveToState(newState, 0, 0, always);
+ /**
+ * Fragments that have been shown or hidden don't have their visibility changed or
+ * animations run during the {@link #showFragment(Fragment)} or {@link #hideFragment(Fragment)}
+ * calls. After fragments are brought to their final state in
+ * {@link #moveFragmentToExpectedState(Fragment)} the fragments that have been shown or
+ * hidden must have their visibility changed and their animations started here.
+ *
+ * @param fragment The fragment with mHiddenChanged = true that should change its View's
+ * visibility and start the show or hide animation.
+ */
+ void completeShowHideFragment(final Fragment fragment) {
+ if (fragment.mView != null) {
+ Animation anim = loadAnimation(fragment, fragment.getNextTransition(),
+ !fragment.mHidden, fragment.getNextTransitionStyle());
+ if (anim != null) {
+ setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
+ fragment.mView.startAnimation(anim);
+ setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
+ anim.start();
+ }
+ final int visibility = fragment.mHidden && !fragment.isHideReplaced()
+ ? View.GONE
+ : View.VISIBLE;
+ fragment.mView.setVisibility(visibility);
+ if (fragment.isHideReplaced()) {
+ fragment.setHideReplaced(false);
+ }
+ }
+ if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
+ mNeedMenuInvalidate = true;
+ }
+ fragment.mHiddenChanged = false;
+ fragment.onHiddenChanged(fragment.mHidden);
}
- void moveToState(int newState, int transit, int transitStyle, boolean always) {
- if (mHost == null && newState != Fragment.INITIALIZING) {
- throw new IllegalStateException("No host");
- }
-
- if (!always && mCurState == newState) {
+ /**
+ * Moves a fragment to its expected final state or the fragment manager's state, depending
+ * on whether the fragment manager's state is raised properly.
+ *
+ * @param f The fragment to change.
+ */
+ void moveFragmentToExpectedState(Fragment f) {
+ if (f == null) {
return;
}
+ int nextState = mCurState;
+ if (f.mRemoving) {
+ if (f.isInBackStack()) {
+ nextState = Math.min(nextState, Fragment.CREATED);
+ } else {
+ nextState = Math.min(nextState, Fragment.INITIALIZING);
+ }
+ }
+ moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);
+
+ if (f.mView != null) {
+ // Move the view if it is out of order
+ Fragment underFragment = findFragmentUnder(f);
+ if (underFragment != null) {
+ final View underView = underFragment.mView;
+ // make sure this fragment is in the right order.
+ final ViewGroup container = f.mContainer;
+ int underIndex = container.indexOfChild(underView);
+ int viewIndex = container.indexOfChild(f.mView);
+ if (viewIndex < underIndex) {
+ container.removeViewAt(viewIndex);
+ container.addView(f.mView, underIndex);
+ }
+ }
+ if (f.mIsNewlyAdded && f.mContainer != null) {
+ // Make it visible and run the animations
+ f.mView.setVisibility(View.VISIBLE);
+ f.mIsNewlyAdded = false;
+ // run animations:
+ Animation anim = loadAnimation(f, f.getNextTransition(), true,
+ f.getNextTransitionStyle());
+ if (anim != null) {
+ setHWLayerAnimListenerIfAlpha(f.mView, anim);
+ f.mView.startAnimation(anim);
+ }
+ }
+ }
+ if (f.mHiddenChanged) {
+ completeShowHideFragment(f);
+ }
+ }
+
+ void moveToState(int newState) {
+ if (mHost == null && newState != Fragment.INITIALIZING) {
+ throw new IllegalStateException("No activity");
+ }
mCurState = newState;
+
if (mActive != null) {
boolean loadersRunning = false;
- for (int i=0; i<mActive.size(); i++) {
+
+ // Must add them in the proper order. mActive fragments may be out of order
+ if (mAdded != null) {
+ final int numAdded = mAdded.size();
+ for (int i = 0; i < numAdded; i++) {
+ Fragment f = mAdded.get(i);
+ moveFragmentToExpectedState(f);
+ if (f.mLoaderManager != null) {
+ loadersRunning |= f.mLoaderManager.hasRunningLoaders();
+ }
+ }
+ }
+
+ // Now iterate through all active fragments. These will include those that are removed
+ // and detached.
+ final int numActive = mActive.size();
+ for (int i = 0; i < numActive; i++) {
Fragment f = mActive.get(i);
- if (f != null) {
- moveToState(f, newState, transit, transitStyle, false);
+ if (f != null && (f.mRemoving || f.mDetached) && !f.mIsNewlyAdded) {
+ moveFragmentToExpectedState(f);
if (f.mLoaderManager != null) {
loadersRunning |= f.mLoaderManager.hasRunningLoaders();
}
@@ -1368,6 +1658,9 @@
mAdded.add(fragment);
fragment.mAdded = true;
fragment.mRemoving = false;
+ if (fragment.mView == null) {
+ fragment.mHiddenChanged = false;
+ }
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
@@ -1377,7 +1670,7 @@
}
}
- public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
+ public void removeFragment(Fragment fragment) {
if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
final boolean inactive = !fragment.isInBackStack();
if (!fragment.mDetached || inactive) {
@@ -1389,52 +1682,42 @@
}
fragment.mAdded = false;
fragment.mRemoving = true;
- moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
- transition, transitionStyle, false);
}
}
- public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
+ /**
+ * Marks a fragment as hidden to be later animated in with
+ * {@link #completeShowHideFragment(Fragment)}.
+ *
+ * @param fragment The fragment to be shown.
+ */
+ public void hideFragment(Fragment fragment) {
if (DEBUG) Log.v(TAG, "hide: " + fragment);
if (!fragment.mHidden) {
fragment.mHidden = true;
- if (fragment.mView != null) {
- Animation anim = loadAnimation(fragment, transition, false,
- transitionStyle);
- if (anim != null) {
- setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
- fragment.mView.startAnimation(anim);
- }
- fragment.mView.setVisibility(View.GONE);
- }
- if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
- mNeedMenuInvalidate = true;
- }
- fragment.onHiddenChanged(true);
+ // Toggle hidden changed so that if a fragment goes through show/hide/show
+ // it doesn't go through the animation.
+ fragment.mHiddenChanged = !fragment.mHiddenChanged;
}
}
- public void showFragment(Fragment fragment, int transition, int transitionStyle) {
+ /**
+ * Marks a fragment as shown to be later animated in with
+ * {@link #completeShowHideFragment(Fragment)}.
+ *
+ * @param fragment The fragment to be shown.
+ */
+ public void showFragment(Fragment fragment) {
if (DEBUG) Log.v(TAG, "show: " + fragment);
if (fragment.mHidden) {
fragment.mHidden = false;
- if (fragment.mView != null) {
- Animation anim = loadAnimation(fragment, transition, true,
- transitionStyle);
- if (anim != null) {
- setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
- fragment.mView.startAnimation(anim);
- }
- fragment.mView.setVisibility(View.VISIBLE);
- }
- if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
- mNeedMenuInvalidate = true;
- }
- fragment.onHiddenChanged(false);
+ // Toggle hidden changed so that if a fragment goes through show/hide/show
+ // it doesn't go through the animation.
+ fragment.mHiddenChanged = !fragment.mHiddenChanged;
}
}
- public void detachFragment(Fragment fragment, int transition, int transitionStyle) {
+ public void detachFragment(Fragment fragment) {
if (DEBUG) Log.v(TAG, "detach: " + fragment);
if (!fragment.mDetached) {
fragment.mDetached = true;
@@ -1448,12 +1731,11 @@
mNeedMenuInvalidate = true;
}
fragment.mAdded = false;
- moveToState(fragment, Fragment.CREATED, transition, transitionStyle, false);
}
}
}
- public void attachFragment(Fragment fragment, int transition, int transitionStyle) {
+ public void attachFragment(Fragment fragment) {
if (DEBUG) Log.v(TAG, "attach: " + fragment);
if (fragment.mDetached) {
fragment.mDetached = false;
@@ -1470,7 +1752,6 @@
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
- moveToState(fragment, mCurState, transition, transitionStyle, false);
}
}
}
@@ -1551,7 +1832,7 @@
* @param allowStateLoss whether to allow loss of state information
* @throws IllegalStateException if the activity has been destroyed
*/
- public void enqueueAction(Runnable action, boolean allowStateLoss) {
+ public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
@@ -1560,10 +1841,25 @@
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
- mPendingActions = new ArrayList<Runnable>();
+ mPendingActions = new ArrayList<>();
}
mPendingActions.add(action);
- if (mPendingActions.size() == 1) {
+ scheduleCommit();
+ }
+ }
+
+ /**
+ * Schedules the execution when one hasn't been scheduled already. This should happen
+ * the first time {@link #enqueueAction(OpGenerator, boolean)} is called or when
+ * a postponed transaction has been started with
+ * {@link Fragment#startPostponedEnterTransition()}
+ */
+ private void scheduleCommit() {
+ synchronized (this) {
+ boolean postponeReady =
+ mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
+ boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
+ if (postponeReady || pendingReady) {
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
}
@@ -1626,7 +1922,13 @@
}
}
- public void execSingleAction(Runnable action, boolean allowStateLoss) {
+ /**
+ * Broken out from exec*, this prepares for gathering and executing operations.
+ *
+ * @param allowStateLoss true if state loss should be ignored or false if it should be
+ * checked.
+ */
+ private void ensureExecReady(boolean allowStateLoss) {
if (mExecutingActions) {
throw new IllegalStateException("FragmentManager is already executing transactions");
}
@@ -1639,50 +1941,51 @@
checkStateLoss();
}
- mExecutingActions = true;
- action.run();
- mExecutingActions = false;
+ if (mTmpRecords == null) {
+ mTmpRecords = new ArrayList<>();
+ mTmpIsPop = new ArrayList<>();
+ }
+ executePostponedTransaction(null, null);
+ }
+
+ public void execSingleAction(OpGenerator action, boolean allowStateLoss) {
+ ensureExecReady(allowStateLoss);
+ if (action.generateOps(mTmpRecords, mTmpIsPop)) {
+ mExecutingActions = true;
+ try {
+ optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
+ } finally {
+ cleanupExec();
+ }
+ }
doPendingDeferredStart();
}
/**
+ * Broken out of exec*, this cleans up the mExecutingActions and the temporary structures
+ * used in executing operations.
+ */
+ private void cleanupExec() {
+ mExecutingActions = false;
+ mTmpIsPop.clear();
+ mTmpRecords.clear();
+ }
+
+ /**
* Only call from main thread!
*/
public boolean execPendingActions() {
- if (mExecutingActions) {
- throw new IllegalStateException("FragmentManager is already executing transactions");
- }
-
- if (Looper.myLooper() != mHost.getHandler().getLooper()) {
- throw new IllegalStateException("Must be called from main thread of fragment host");
- }
+ ensureExecReady(true);
boolean didSomething = false;
-
- while (true) {
- int numActions;
-
- synchronized (this) {
- if (mPendingActions == null || mPendingActions.size() == 0) {
- break;
- }
-
- numActions = mPendingActions.size();
- if (mTmpActions == null || mTmpActions.length < numActions) {
- mTmpActions = new Runnable[numActions];
- }
- mPendingActions.toArray(mTmpActions);
- mPendingActions.clear();
- mHost.getHandler().removeCallbacks(mExecCommit);
- }
-
+ while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
mExecutingActions = true;
- for (int i=0; i<numActions; i++) {
- mTmpActions[i].run();
- mTmpActions[i] = null;
+ try {
+ optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
+ } finally {
+ cleanupExec();
}
- mExecutingActions = false;
didSomething = true;
}
@@ -1691,6 +1994,386 @@
return didSomething;
}
+ /**
+ * Complete the execution of transactions that have previously been postponed, but are
+ * now ready.
+ */
+ private void executePostponedTransaction(ArrayList<BackStackRecord> records,
+ ArrayList<Boolean> isRecordPop) {
+ int numPostponed = mPostponedTransactions == null ? 0 : mPostponedTransactions.size();
+ for (int i = 0; i < numPostponed; i++) {
+ StartEnterTransitionListener listener = mPostponedTransactions.get(i);
+ if (records != null && !listener.mIsBack) {
+ int index = records.indexOf(listener.mRecord);
+ if (index != -1 && isRecordPop.get(index)) {
+ listener.cancelTransaction();
+ continue;
+ }
+ }
+ if (listener.isReady() || (records != null
+ && listener.mRecord.interactsWith(records, 0, records.size()))) {
+ mPostponedTransactions.remove(i);
+ i--;
+ numPostponed--;
+ int index;
+ if (records != null && !listener.mIsBack
+ && (index = records.indexOf(listener.mRecord)) != -1
+ && isRecordPop.get(index)) {
+ // This is popping a postponed transaction
+ listener.cancelTransaction();
+ } else {
+ listener.completeTransaction();
+ }
+ }
+ }
+ }
+
+ /**
+ * Optimizes BackStackRecord operations. This method merges operations of proximate records
+ * that allow optimization. See {@link FragmentTransaction#setAllowOptimization(boolean)}.
+ * <p>
+ * For example, a transaction that adds to the back stack and then another that pops that
+ * back stack record will be optimized.
+ * <p>
+ * Likewise, two transactions committed that are executed at the same time will be optimized
+ * as well as two pop operations executed together.
+ *
+ * @param records The records pending execution
+ * @param isRecordPop The direction that these records are being run.
+ */
+ private void optimizeAndExecuteOps(ArrayList<BackStackRecord> records,
+ ArrayList<Boolean> isRecordPop) {
+ if (records == null || records.isEmpty()) {
+ return;
+ }
+
+ if (isRecordPop == null || records.size() != isRecordPop.size()) {
+ throw new IllegalStateException("Internal error with the back stack records");
+ }
+
+ // Force start of any postponed transactions that interact with scheduled transactions:
+ executePostponedTransaction(records, isRecordPop);
+
+ final int numRecords = records.size();
+ int startIndex = 0;
+ for (int recordNum = 0; recordNum < numRecords; recordNum++) {
+ final boolean canOptimize = records.get(recordNum).mAllowOptimization;
+ if (!canOptimize) {
+ // execute all previous transactions
+ if (startIndex != recordNum) {
+ executeOpsTogether(records, isRecordPop, startIndex, recordNum);
+ }
+ // execute all unoptimized together
+ int optimizeEnd;
+ for (optimizeEnd = recordNum + 1; optimizeEnd < numRecords; optimizeEnd++) {
+ if (records.get(optimizeEnd).mAllowOptimization) {
+ break;
+ }
+ }
+ executeOpsTogether(records, isRecordPop, recordNum, optimizeEnd);
+ startIndex = optimizeEnd;
+ recordNum = optimizeEnd - 1;
+ }
+ }
+ if (startIndex != numRecords) {
+ executeOpsTogether(records, isRecordPop, startIndex, numRecords);
+ }
+ }
+
+ /**
+ * Optimizes a subset of a list of BackStackRecords, all of which either allow optimization or
+ * do not allow optimization.
+ * @param records A list of BackStackRecords that are to be optimized
+ * @param isRecordPop The direction that these records are being run.
+ * @param startIndex The index of the first record in <code>records</code> to be optimized
+ * @param endIndex One more than the final record index in <code>records</code> to optimize.
+ */
+ private void executeOpsTogether(ArrayList<BackStackRecord> records,
+ ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
+ final boolean allowOptimization = records.get(startIndex).mAllowOptimization;
+ boolean addToBackStack = false;
+ if (mTmpAddedFragments == null) {
+ mTmpAddedFragments = new ArrayList<>();
+ } else {
+ mTmpAddedFragments.clear();
+ }
+ if (mAdded != null) {
+ mTmpAddedFragments.addAll(mAdded);
+ }
+ for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
+ final BackStackRecord record = records.get(recordNum);
+ final boolean isPop = isRecordPop.get(recordNum);
+ if (!isPop) {
+ record.expandReplaceOps(mTmpAddedFragments);
+ }
+ final int bumpAmount = isPop ? -1 : 1;
+ record.bumpBackStackNesting(bumpAmount);
+ addToBackStack = addToBackStack || record.mAddToBackStack;
+ }
+ mTmpAddedFragments.clear();
+
+ if (!allowOptimization) {
+ FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex,
+ false);
+ }
+ executeOps(records, isRecordPop, startIndex, endIndex);
+
+ int postponeIndex = endIndex;
+ if (allowOptimization) {
+ moveFragmentsToInvisible();
+ postponeIndex = postponePostponableTransactions(records, isRecordPop,
+ startIndex, endIndex);
+ }
+
+ if (postponeIndex != startIndex && allowOptimization) {
+ // need to run something now
+ FragmentTransition.startTransitions(this, records, isRecordPop, startIndex,
+ postponeIndex, true);
+ moveToState(mCurState);
+ }
+
+ for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
+ final BackStackRecord record = records.get(recordNum);
+ final boolean isPop = isRecordPop.get(recordNum);
+ if (isPop && record.mIndex >= 0) {
+ freeBackStackIndex(record.mIndex);
+ record.mIndex = -1;
+ }
+ }
+ if (addToBackStack) {
+ reportBackStackChanged();
+ }
+ }
+
+ /**
+ * Examine all transactions and determine which ones are marked as postponed. Those will
+ * have their operations rolled back and moved to the end of the record list (up to endIndex).
+ * It will also add the postponed transaction to the queue.
+ *
+ * @param records A list of BackStackRecords that should be checked.
+ * @param isRecordPop The direction that these records are being run.
+ * @param startIndex The index of the first record in <code>records</code> to be checked
+ * @param endIndex One more than the final record index in <code>records</code> to be checked.
+ * @return The index of the first postponed transaction or endIndex if no transaction was
+ * postponed.
+ */
+ private int postponePostponableTransactions(ArrayList<BackStackRecord> records,
+ ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
+ int postponeIndex = endIndex;
+ for (int i = endIndex - 1; i >= startIndex; i--) {
+ final BackStackRecord record = records.get(i);
+ final boolean isPop = isRecordPop.get(i);
+ boolean isPostponed = record.isPostponed()
+ && !record.interactsWith(records, i + 1, endIndex);
+ if (isPostponed) {
+ if (mPostponedTransactions == null) {
+ mPostponedTransactions = new ArrayList<>();
+ }
+ StartEnterTransitionListener listener =
+ new StartEnterTransitionListener(record, isPop);
+ mPostponedTransactions.add(listener);
+ record.setOnStartPostponedListener(listener);
+
+ // roll back the transaction
+ if (isPop) {
+ record.executeOps();
+ } else {
+ record.executePopOps();
+ }
+
+ // move to the end
+ postponeIndex--;
+ if (i != postponeIndex) {
+ records.remove(i);
+ records.add(postponeIndex, record);
+ }
+
+ // different views may be visible now
+ moveFragmentsToInvisible();
+ }
+ }
+ return postponeIndex;
+ }
+
+ /**
+ * When a postponed transaction is ready to be started, this completes the transaction,
+ * removing, hiding, or showing views as well as starting the animations and transitions.
+ * <p>
+ * {@code runtransitions} is set to false when the transaction postponement was interrupted
+ * abnormally -- normally by a new transaction being started that affects the postponed
+ * transaction.
+ *
+ * @param record The transaction to run
+ * @param isPop true if record is popping or false if it is adding
+ * @param runTransitions true if the fragment transition should be run or false otherwise.
+ * @param moveToState true if the state should be changed after executing the operations.
+ * This is false when the transaction is canceled when a postponed
+ * transaction is popped.
+ */
+ private void completeExecute(BackStackRecord record, boolean isPop, boolean runTransitions,
+ boolean moveToState) {
+ ArrayList<BackStackRecord> records = new ArrayList<>(1);
+ ArrayList<Boolean> isRecordPop = new ArrayList<>(1);
+ records.add(record);
+ isRecordPop.add(isPop);
+ executeOps(records, isRecordPop, 0, 1);
+ if (runTransitions) {
+ FragmentTransition.startTransitions(this, records, isRecordPop, 0, 1, true);
+ }
+ if (moveToState) {
+ moveToState(mCurState);
+ } else if (mActive != null) {
+ final int numActive = mActive.size();
+ for (int i = 0; i < numActive; i++) {
+ // Allow added fragments to be removed during the pop since we aren't going
+ // to move them to the final state with moveToState(mCurState).
+ Fragment fragment = mActive.get(i);
+ if (fragment.mView != null && fragment.mIsNewlyAdded
+ && record.interactsWith(fragment.mContainerId)) {
+ fragment.mIsNewlyAdded = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Find a fragment within the fragment's container whose View should be below the passed
+ * fragment. {@code null} is returned when the fragment has no View or if there should be
+ * no fragment with a View below the given fragment.
+ *
+ * As an example, if mAdded has two Fragments with Views sharing the same container:
+ * FragmentA
+ * FragmentB
+ *
+ * Then, when processing FragmentB, FragmentA will be returned. If, however, FragmentA
+ * had no View, null would be returned.
+ *
+ * @param f The fragment that may be on top of another fragment.
+ * @return The fragment with a View under f, if one exists or null if f has no View or
+ * there are no fragments with Views in the same container.
+ */
+ private Fragment findFragmentUnder(Fragment f) {
+ final ViewGroup container = f.mContainer;
+ final View view = f.mView;
+
+ if (container == null || view == null) {
+ return null;
+ }
+
+ final int fragmentIndex = mAdded.indexOf(f);
+ for (int i = fragmentIndex - 1; i >= 0; i--) {
+ Fragment underFragment = mAdded.get(i);
+ if (underFragment.mContainer == container && underFragment.mView != null) {
+ // Found the fragment under this one
+ return underFragment;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Run the operations in the BackStackRecords, either to push or pop.
+ *
+ * @param records The list of records whose operations should be run.
+ * @param isRecordPop The direction that these records are being run.
+ * @param startIndex The index of the first entry in records to run.
+ * @param endIndex One past the index of the final entry in records to run.
+ */
+ private static void executeOps(ArrayList<BackStackRecord> records,
+ ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
+ for (int i = startIndex; i < endIndex; i++) {
+ final BackStackRecord record = records.get(i);
+ final boolean isPop = isRecordPop.get(i);
+ if (isPop) {
+ record.executePopOps();
+ } else {
+ record.executeOps();
+ }
+ }
+ }
+
+ /**
+ * Ensure that fragments that are added are moved to at least the CREATED state.
+ * Any newly-added Views are made INVISIBLE so that the Transaction can be postponed
+ * with {@link Fragment#postponeEnterTransition()}.
+ */
+ private void moveFragmentsToInvisible() {
+ if (mCurState < Fragment.CREATED) {
+ return;
+ }
+ // We want to leave the fragment in the started state
+ final int state = Math.min(mCurState, Fragment.STARTED);
+ final int numAdded = mAdded == null ? 0 : mAdded.size();
+ for (int i = 0; i < numAdded; i++) {
+ Fragment fragment = mAdded.get(i);
+ if (fragment.mState < state) {
+ moveToState(fragment, state, fragment.getNextAnim(), fragment.getNextTransition(),
+ false);
+ if (fragment.mView != null && !fragment.mHidden && fragment.mIsNewlyAdded) {
+ fragment.mView.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+ }
+
+ /**
+ * Starts all postponed transactions regardless of whether they are ready or not.
+ */
+ private void forcePostponedTransactions() {
+ if (mPostponedTransactions != null) {
+ while (!mPostponedTransactions.isEmpty()) {
+ mPostponedTransactions.remove(0).completeTransaction();
+ }
+ }
+ }
+
+ /**
+ * Ends the animations of fragments so that they immediately reach the end state.
+ * This is used prior to saving the state so that the correct state is saved.
+ */
+ private void endAnimatingAwayFragments() {
+ final int numFragments = mActive == null ? 0 : mActive.size();
+ for (int i = 0; i < numFragments; i++) {
+ Fragment fragment = mActive.get(i);
+ if (fragment != null && fragment.getAnimatingAway() != null) {
+ // Give up waiting for the animation and just end it.
+ final int stateAfterAnimating = fragment.getStateAfterAnimating();
+ final View animatingAway = fragment.getAnimatingAway();
+ fragment.setAnimatingAway(null);
+ animatingAway.clearAnimation();
+ moveToState(fragment, stateAfterAnimating, 0, 0, false);
+ }
+ }
+ }
+
+ /**
+ * Adds all records in the pending actions to records and whether they are add or pop
+ * operations to isPop. After executing, the pending actions will be empty.
+ *
+ * @param records All pending actions will generate BackStackRecords added to this.
+ * This contains the transactions, in order, to execute.
+ * @param isPop All pending actions will generate booleans to add to this. This contains
+ * an entry for each entry in records to indicate whether or not it is a
+ * pop action.
+ */
+ private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,
+ ArrayList<Boolean> isPop) {
+ int numActions;
+ synchronized (this) {
+ if (mPendingActions == null || mPendingActions.size() == 0) {
+ return false;
+ }
+
+ numActions = mPendingActions.size();
+ for (int i = 0; i < numActions; i++) {
+ mPendingActions.get(i).generateOps(records, isPop);
+ }
+ mPendingActions.clear();
+ mHost.getHandler().removeCallbacks(mExecCommit);
+ }
+ return numActions > 0;
+ }
+
void doPendingDeferredStart() {
if (mHavePendingDeferredStart) {
boolean loadersRunning = false;
@@ -1724,23 +2407,18 @@
}
@SuppressWarnings("unused")
- boolean popBackStackState(Handler handler, String name, int id, int flags) {
+ boolean popBackStackState(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
+ String name, int id, int flags) {
if (mBackStack == null) {
return false;
}
- if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) {
- int last = mBackStack.size()-1;
+ if (name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0) {
+ int last = mBackStack.size() - 1;
if (last < 0) {
return false;
}
- final BackStackRecord bss = mBackStack.remove(last);
- SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
- SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
- if (mCurState >= Fragment.CREATED) {
- bss.calculateBackFragments(firstOutFragments, lastInFragments);
- }
- bss.popFromBackStack(true, null, firstOutFragments, lastInFragments);
- reportBackStackChanged();
+ records.add(mBackStack.remove(last));
+ isRecordPop.add(true);
} else {
int index = -1;
if (name != null || id >= 0) {
@@ -1777,26 +2455,10 @@
if (index == mBackStack.size()-1) {
return false;
}
- final ArrayList<BackStackRecord> states
- = new ArrayList<BackStackRecord>();
- for (int i=mBackStack.size()-1; i>index; i--) {
- states.add(mBackStack.remove(i));
+ for (int i = mBackStack.size() - 1; i > index; i--) {
+ records.add(mBackStack.remove(i));
+ isRecordPop.add(true);
}
- final int LAST = states.size()-1;
- SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
- SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
- if (mCurState >= Fragment.CREATED) {
- for (int i = 0; i <= LAST; i++) {
- states.get(i).calculateBackFragments(firstOutFragments, lastInFragments);
- }
- }
- BackStackRecord.TransitionState state = null;
- for (int i=0; i<=LAST; i++) {
- if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
- state = states.get(i).popFromBackStack(i == LAST, state,
- firstOutFragments, lastInFragments);
- }
- reportBackStackChanged();
}
return true;
}
@@ -1866,6 +2528,7 @@
mStateBundle = new Bundle();
}
f.performSaveInstanceState(mStateBundle);
+ dispatchOnFragmentSaveInstanceState(f, mStateBundle, false);
if (!mStateBundle.isEmpty()) {
result = mStateBundle;
mStateBundle = null;
@@ -1895,6 +2558,8 @@
Parcelable saveAllState() {
// Make sure all pending operations have now been executed to get
// our state update-to-date.
+ forcePostponedTransactions();
+ endAnimatingAwayFragments();
execPendingActions();
if (HONEYCOMB) {
@@ -2146,26 +2811,26 @@
public void dispatchCreate() {
mStateSaved = false;
- moveToState(Fragment.CREATED, false);
+ moveToState(Fragment.CREATED);
}
public void dispatchActivityCreated() {
mStateSaved = false;
- moveToState(Fragment.ACTIVITY_CREATED, false);
+ moveToState(Fragment.ACTIVITY_CREATED);
}
public void dispatchStart() {
mStateSaved = false;
- moveToState(Fragment.STARTED, false);
+ moveToState(Fragment.STARTED);
}
public void dispatchResume() {
mStateSaved = false;
- moveToState(Fragment.RESUMED, false);
+ moveToState(Fragment.RESUMED);
}
public void dispatchPause() {
- moveToState(Fragment.STARTED, false);
+ moveToState(Fragment.STARTED);
}
public void dispatchStop() {
@@ -2174,21 +2839,21 @@
// them.
mStateSaved = true;
- moveToState(Fragment.STOPPED, false);
+ moveToState(Fragment.STOPPED);
}
public void dispatchReallyStop() {
- moveToState(Fragment.ACTIVITY_CREATED, false);
+ moveToState(Fragment.ACTIVITY_CREATED);
}
public void dispatchDestroyView() {
- moveToState(Fragment.CREATED, false);
+ moveToState(Fragment.CREATED);
}
public void dispatchDestroy() {
mDestroyed = true;
execPendingActions();
- moveToState(Fragment.INITIALIZING, false);
+ moveToState(Fragment.INITIALIZING);
mHost = null;
mContainer = null;
mParent = null;
@@ -2326,6 +2991,265 @@
}
}
+ public void registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb,
+ boolean recursive) {
+ if (mLifecycleCallbacks == null) {
+ mLifecycleCallbacks = new CopyOnWriteArrayList<>();
+ }
+ mLifecycleCallbacks.add(new Pair(cb, recursive));
+ }
+
+ public void unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb) {
+ if (mLifecycleCallbacks == null) {
+ return;
+ }
+
+ synchronized (mLifecycleCallbacks) {
+ for (int i = 0, N = mLifecycleCallbacks.size(); i < N; i++) {
+ if (mLifecycleCallbacks.get(i).first == cb) {
+ mLifecycleCallbacks.remove(i);
+ break;
+ }
+ }
+ }
+ }
+
+ void dispatchOnFragmentPreAttached(Fragment f, Context context, boolean onlyRecursive) {
+ if (mParent != null) {
+ FragmentManager parentManager = mParent.getFragmentManager();
+ if (parentManager instanceof FragmentManagerImpl) {
+ ((FragmentManagerImpl) parentManager)
+ .dispatchOnFragmentPreAttached(f, context, true);
+ }
+ }
+ if (mLifecycleCallbacks == null) {
+ return;
+ }
+ for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+ if (!onlyRecursive || p.second) {
+ p.first.onFragmentPreAttached(this, f, context);
+ }
+ }
+ }
+
+ void dispatchOnFragmentAttached(Fragment f, Context context, boolean onlyRecursive) {
+ if (mParent != null) {
+ FragmentManager parentManager = mParent.getFragmentManager();
+ if (parentManager instanceof FragmentManagerImpl) {
+ ((FragmentManagerImpl) parentManager)
+ .dispatchOnFragmentAttached(f, context, true);
+ }
+ }
+ if (mLifecycleCallbacks == null) {
+ return;
+ }
+ for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+ if (!onlyRecursive || p.second) {
+ p.first.onFragmentAttached(this, f, context);
+ }
+ }
+ }
+
+ void dispatchOnFragmentCreated(Fragment f, Bundle savedInstanceState, boolean onlyRecursive) {
+ if (mParent != null) {
+ FragmentManager parentManager = mParent.getFragmentManager();
+ if (parentManager instanceof FragmentManagerImpl) {
+ ((FragmentManagerImpl) parentManager)
+ .dispatchOnFragmentCreated(f, savedInstanceState, true);
+ }
+ }
+ if (mLifecycleCallbacks == null) {
+ return;
+ }
+ for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+ if (!onlyRecursive || p.second) {
+ p.first.onFragmentCreated(this, f, savedInstanceState);
+ }
+ }
+ }
+
+ void dispatchOnFragmentActivityCreated(Fragment f, Bundle savedInstanceState,
+ boolean onlyRecursive) {
+ if (mParent != null) {
+ FragmentManager parentManager = mParent.getFragmentManager();
+ if (parentManager instanceof FragmentManagerImpl) {
+ ((FragmentManagerImpl) parentManager)
+ .dispatchOnFragmentActivityCreated(f, savedInstanceState, true);
+ }
+ }
+ if (mLifecycleCallbacks == null) {
+ return;
+ }
+ for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+ if (!onlyRecursive || p.second) {
+ p.first.onFragmentActivityCreated(this, f, savedInstanceState);
+ }
+ }
+ }
+
+ void dispatchOnFragmentViewCreated(Fragment f, View v, Bundle savedInstanceState,
+ boolean onlyRecursive) {
+ if (mParent != null) {
+ FragmentManager parentManager = mParent.getFragmentManager();
+ if (parentManager instanceof FragmentManagerImpl) {
+ ((FragmentManagerImpl) parentManager)
+ .dispatchOnFragmentViewCreated(f, v, savedInstanceState, true);
+ }
+ }
+ if (mLifecycleCallbacks == null) {
+ return;
+ }
+ for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+ if (!onlyRecursive || p.second) {
+ p.first.onFragmentViewCreated(this, f, v, savedInstanceState);
+ }
+ }
+ }
+
+ void dispatchOnFragmentStarted(Fragment f, boolean onlyRecursive) {
+ if (mParent != null) {
+ FragmentManager parentManager = mParent.getFragmentManager();
+ if (parentManager instanceof FragmentManagerImpl) {
+ ((FragmentManagerImpl) parentManager)
+ .dispatchOnFragmentStarted(f, true);
+ }
+ }
+ if (mLifecycleCallbacks == null) {
+ return;
+ }
+ for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+ if (!onlyRecursive || p.second) {
+ p.first.onFragmentStarted(this, f);
+ }
+ }
+ }
+
+ void dispatchOnFragmentResumed(Fragment f, boolean onlyRecursive) {
+ if (mParent != null) {
+ FragmentManager parentManager = mParent.getFragmentManager();
+ if (parentManager instanceof FragmentManagerImpl) {
+ ((FragmentManagerImpl) parentManager)
+ .dispatchOnFragmentResumed(f, true);
+ }
+ }
+ if (mLifecycleCallbacks == null) {
+ return;
+ }
+ for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+ if (!onlyRecursive || p.second) {
+ p.first.onFragmentResumed(this, f);
+ }
+ }
+ }
+
+ void dispatchOnFragmentPaused(Fragment f, boolean onlyRecursive) {
+ if (mParent != null) {
+ FragmentManager parentManager = mParent.getFragmentManager();
+ if (parentManager instanceof FragmentManagerImpl) {
+ ((FragmentManagerImpl) parentManager)
+ .dispatchOnFragmentPaused(f, true);
+ }
+ }
+ if (mLifecycleCallbacks == null) {
+ return;
+ }
+ for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+ if (!onlyRecursive || p.second) {
+ p.first.onFragmentPaused(this, f);
+ }
+ }
+ }
+
+ void dispatchOnFragmentStopped(Fragment f, boolean onlyRecursive) {
+ if (mParent != null) {
+ FragmentManager parentManager = mParent.getFragmentManager();
+ if (parentManager instanceof FragmentManagerImpl) {
+ ((FragmentManagerImpl) parentManager)
+ .dispatchOnFragmentStopped(f, true);
+ }
+ }
+ if (mLifecycleCallbacks == null) {
+ return;
+ }
+ for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+ if (!onlyRecursive || p.second) {
+ p.first.onFragmentStopped(this, f);
+ }
+ }
+ }
+
+ void dispatchOnFragmentSaveInstanceState(Fragment f, Bundle outState, boolean onlyRecursive) {
+ if (mParent != null) {
+ FragmentManager parentManager = mParent.getFragmentManager();
+ if (parentManager instanceof FragmentManagerImpl) {
+ ((FragmentManagerImpl) parentManager)
+ .dispatchOnFragmentSaveInstanceState(f, outState, true);
+ }
+ }
+ if (mLifecycleCallbacks == null) {
+ return;
+ }
+ for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+ if (!onlyRecursive || p.second) {
+ p.first.onFragmentSaveInstanceState(this, f, outState);
+ }
+ }
+ }
+
+ void dispatchOnFragmentViewDestroyed(Fragment f, boolean onlyRecursive) {
+ if (mParent != null) {
+ FragmentManager parentManager = mParent.getFragmentManager();
+ if (parentManager instanceof FragmentManagerImpl) {
+ ((FragmentManagerImpl) parentManager)
+ .dispatchOnFragmentViewDestroyed(f, true);
+ }
+ }
+ if (mLifecycleCallbacks == null) {
+ return;
+ }
+ for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+ if (!onlyRecursive || p.second) {
+ p.first.onFragmentViewDestroyed(this, f);
+ }
+ }
+ }
+
+ void dispatchOnFragmentDestroyed(Fragment f, boolean onlyRecursive) {
+ if (mParent != null) {
+ FragmentManager parentManager = mParent.getFragmentManager();
+ if (parentManager instanceof FragmentManagerImpl) {
+ ((FragmentManagerImpl) parentManager)
+ .dispatchOnFragmentDestroyed(f, true);
+ }
+ }
+ if (mLifecycleCallbacks == null) {
+ return;
+ }
+ for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+ if (!onlyRecursive || p.second) {
+ p.first.onFragmentDestroyed(this, f);
+ }
+ }
+ }
+
+ void dispatchOnFragmentDetached(Fragment f, boolean onlyRecursive) {
+ if (mParent != null) {
+ FragmentManager parentManager = mParent.getFragmentManager();
+ if (parentManager instanceof FragmentManagerImpl) {
+ ((FragmentManagerImpl) parentManager)
+ .dispatchOnFragmentDetached(f, true);
+ }
+ }
+ if (mLifecycleCallbacks == null) {
+ return;
+ }
+ for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+ if (!onlyRecursive || p.second) {
+ p.first.onFragmentDetached(this, f);
+ }
+ }
+ }
+
public static int reverseTransit(int transit) {
int rev = 0;
switch (transit) {
@@ -2472,4 +3396,121 @@
public static final int Fragment_name = 0;
public static final int Fragment_tag = 2;
}
+
+ /**
+ * An add or pop transaction to be scheduled for the UI thread.
+ */
+ interface OpGenerator {
+ /**
+ * Generate transactions to add to {@code records} and whether or not the transaction is
+ * an add or pop to {@code isRecordPop}.
+ *
+ * records and isRecordPop must be added equally so that each transaction in records
+ * matches the boolean for whether or not it is a pop in isRecordPop.
+ *
+ * @param records A list to add transactions to.
+ * @param isRecordPop A list to add whether or not the transactions added to records is
+ * a pop transaction.
+ * @return true if something was added or false otherwise.
+ */
+ boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop);
+ }
+
+ /**
+ * A pop operation OpGenerator. This will be run on the UI thread and will generate the
+ * transactions that will be popped if anything can be popped.
+ */
+ private class PopBackStackState implements OpGenerator {
+ final String mName;
+ final int mId;
+ final int mFlags;
+
+ PopBackStackState(String name, int id, int flags) {
+ mName = name;
+ mId = id;
+ mFlags = flags;
+ }
+
+ @Override
+ public boolean generateOps(ArrayList<BackStackRecord> records,
+ ArrayList<Boolean> isRecordPop) {
+ return popBackStackState(records, isRecordPop, mName, mId, mFlags);
+ }
+ }
+
+ /**
+ * A listener for a postponed transaction. This waits until
+ * {@link Fragment#startPostponedEnterTransition()} is called or a transaction is started
+ * that interacts with this one, based on interactions with the fragment container.
+ */
+ static class StartEnterTransitionListener
+ implements Fragment.OnStartEnterTransitionListener {
+ private final boolean mIsBack;
+ private final BackStackRecord mRecord;
+ private int mNumPostponed;
+
+ StartEnterTransitionListener(BackStackRecord record, boolean isBack) {
+ mIsBack = isBack;
+ mRecord = record;
+ }
+
+ /**
+ * Called from {@link Fragment#startPostponedEnterTransition()}, this decreases the
+ * number of Fragments that are postponed. This may cause the transaction to schedule
+ * to finish running and run transitions and animations.
+ */
+ @Override
+ public void onStartEnterTransition() {
+ mNumPostponed--;
+ if (mNumPostponed != 0) {
+ return;
+ }
+ mRecord.mManager.scheduleCommit();
+ }
+
+ /**
+ * Called from {@link Fragment#
+ * setOnStartEnterTransitionListener(Fragment.OnStartEnterTransitionListener)}, this
+ * increases the number of fragments that are postponed as part of this transaction.
+ */
+ @Override
+ public void startListening() {
+ mNumPostponed++;
+ }
+
+ /**
+ * @return true if there are no more postponed fragments as part of the transaction.
+ */
+ public boolean isReady() {
+ return mNumPostponed == 0;
+ }
+
+ /**
+ * Completes the transaction and start the animations and transitions. This may skip
+ * the transitions if this is called before all fragments have called
+ * {@link Fragment#startPostponedEnterTransition()}.
+ */
+ public void completeTransaction() {
+ final boolean canceled;
+ canceled = mNumPostponed > 0;
+ FragmentManagerImpl manager = mRecord.mManager;
+ final int numAdded = manager.mAdded.size();
+ for (int i = 0; i < numAdded; i++) {
+ final Fragment fragment = manager.mAdded.get(i);
+ fragment.setOnStartEnterTransitionListener(null);
+ if (canceled && fragment.isPostponed()) {
+ fragment.startPostponedEnterTransition();
+ }
+ }
+ mRecord.mManager.completeExecute(mRecord, mIsBack, !canceled, true);
+ }
+
+ /**
+ * Cancels this transaction instead of completing it. That means that the state isn't
+ * changed, so the pop results in no change to the state.
+ */
+ public void cancelTransaction() {
+ mRecord.mManager.completeExecute(mRecord, mIsBack, false, false);
+ }
+ }
}
diff --git a/fragment/java/android/support/v4/app/FragmentTransaction.java b/fragment/java/android/support/v4/app/FragmentTransaction.java
index 745f7e8..9be99b2 100644
--- a/fragment/java/android/support/v4/app/FragmentTransaction.java
+++ b/fragment/java/android/support/v4/app/FragmentTransaction.java
@@ -16,6 +16,8 @@
package android.support.v4.app;
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
import android.support.annotation.AnimRes;
import android.support.annotation.IdRes;
import android.support.annotation.IntDef;
@@ -23,14 +25,11 @@
import android.support.annotation.RestrictTo;
import android.support.annotation.StringRes;
import android.support.annotation.StyleRes;
-import android.support.v4.util.Pair;
import android.view.View;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
/**
* Static library support version of the framework's {@link android.app.FragmentTransaction}.
* Used to write apps that run on platforms prior to Android 3.0. When running
@@ -282,6 +281,30 @@
public abstract FragmentTransaction setBreadCrumbShortTitle(CharSequence text);
/**
+ * Sets whether or not to allow optimizing operations within and across
+ * transactions. Optimizing fragment transaction's operations can eliminate
+ * operations that cancel. For example, if two transactions are executed
+ * together, one that adds a fragment A and the next replaces it with fragment B,
+ * the operations will cancel and only fragment B will be added. That means that
+ * fragment A may not go through the creation/destruction lifecycle.
+ * <p>
+ * The side effect of optimization is that fragments may have state changes
+ * out of the expected order. For example, one transaction adds fragment A,
+ * a second adds fragment B, then a third removes fragment A. Without optimization,
+ * fragment B could expect that while it is being created, fragment A will also
+ * exist because fragment A will be removed after fragment B was added.
+ * With optimization, fragment B cannot expect fragment A to exist when
+ * it has been created because fragment A's add/remove will be optimized out.
+ * <p>
+ * The default is {@code true}.
+ *
+ * @param allowOptimization {@code true} to enable optimizing operations
+ * or {@code false} to disable optimizing
+ * operations on this transaction.
+ */
+ public abstract FragmentTransaction setAllowOptimization(boolean allowOptimization);
+
+ /**
* Schedules a commit of this transaction. The commit does
* not happen immediately; it will be scheduled as work on the main thread
* to be done the next time that thread is ready.
diff --git a/fragment/java/android/support/v4/app/FragmentTransition.java b/fragment/java/android/support/v4/app/FragmentTransition.java
new file mode 100644
index 0000000..9efd1bc
--- /dev/null
+++ b/fragment/java/android/support/v4/app/FragmentTransition.java
@@ -0,0 +1,1126 @@
+/*
+ * 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.v4.app;
+
+import android.graphics.Rect;
+import android.os.Build;
+import android.support.v4.util.ArrayMap;
+import android.support.v4.view.ViewCompat;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Contains the Fragment Transition functionality for both optimized and unoptimized
+ * Fragment Transactions. With optimized fragment transactions, all Views have been
+ * added to the View hierarchy prior to calling startTransitions. With unoptimized
+ * fragment transactions, Views will be removed and added after calling startTransitions.
+ */
+class FragmentTransition {
+ /**
+ * The inverse of all BackStackRecord operation commands. This assumes that
+ * REPLACE operations have already been replaced by add/remove operations.
+ */
+ private static final int[] INVERSE_OPS = {
+ BackStackRecord.OP_NULL, // inverse of OP_NULL (error)
+ BackStackRecord.OP_REMOVE, // inverse of OP_ADD
+ BackStackRecord.OP_NULL, // inverse of OP_REPLACE (error)
+ BackStackRecord.OP_ADD, // inverse of OP_REMOVE
+ BackStackRecord.OP_SHOW, // inverse of OP_HIDE
+ BackStackRecord.OP_HIDE, // inverse of OP_SHOW
+ BackStackRecord.OP_ATTACH, // inverse of OP_DETACH
+ BackStackRecord.OP_DETACH, // inverse of OP_ATTACH
+ };
+
+ /**
+ * The main entry point for Fragment Transitions, this starts the transitions
+ * set on the leaving Fragment's {@link Fragment#getExitTransition()}, the
+ * entering Fragment's {@link Fragment#getEnterTransition()} and
+ * {@link Fragment#getSharedElementEnterTransition()}. When popping,
+ * the leaving Fragment's {@link Fragment#getReturnTransition()} and
+ * {@link Fragment#getSharedElementReturnTransition()} and the entering
+ * {@link Fragment#getReenterTransition()} will be run.
+ * <p>
+ * With optimized Fragment Transitions, all Views have been added to the
+ * View hierarchy prior to calling this method. The incoming Fragment's Views
+ * will be INVISIBLE. With unoptimized Fragment Transitions, this method
+ * is called before any change has been made to the hierarchy. That means
+ * that the added Fragments have not created their Views yet and the hierarchy
+ * is unknown.
+ *
+ * @param fragmentManager The executing FragmentManagerImpl
+ * @param records The list of transactions being executed.
+ * @param isRecordPop For each transaction, whether it is a pop transaction or not.
+ * @param startIndex The first index into records and isRecordPop to execute as
+ * part of this transition.
+ * @param endIndex One past the last index into records and isRecordPop to execute
+ * as part of this transition.
+ * @param isOptimized true if this is an optimized transaction, meaning that the
+ * Views of incoming fragments have been added. false if the
+ * transaction has yet to be run and Views haven't been created.
+ */
+ static void startTransitions(FragmentManagerImpl fragmentManager,
+ ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
+ int startIndex, int endIndex, boolean isOptimized) {
+ if (fragmentManager.mCurState < Fragment.CREATED
+ || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ return;
+ }
+ SparseArray<FragmentContainerTransition> transitioningFragments =
+ new SparseArray<>();
+ for (int i = startIndex; i < endIndex; i++) {
+ final BackStackRecord record = records.get(i);
+ final boolean isPop = isRecordPop.get(i);
+ if (isPop) {
+ calculatePopFragments(record, transitioningFragments, isOptimized);
+ } else {
+ calculateFragments(record, transitioningFragments, isOptimized);
+ }
+ }
+
+ if (transitioningFragments.size() != 0) {
+ final View nonExistentView = new View(fragmentManager.mHost.getContext());
+ final int numContainers = transitioningFragments.size();
+ for (int i = 0; i < numContainers; i++) {
+ int containerId = transitioningFragments.keyAt(i);
+ ArrayMap<String, String> nameOverrides = calculateNameOverrides(containerId,
+ records, isRecordPop, startIndex, endIndex);
+
+ FragmentContainerTransition containerTransition = transitioningFragments.valueAt(i);
+
+ if (isOptimized) {
+ configureTransitionsOptimized(fragmentManager, containerId,
+ containerTransition, nonExistentView, nameOverrides);
+ } else {
+ configureTransitionsUnoptimized(fragmentManager, containerId,
+ containerTransition, nonExistentView, nameOverrides);
+ }
+ }
+ }
+ }
+
+ /**
+ * Iterates through the transactions that affect a given fragment container
+ * and tracks the shared element names across transactions. This is most useful
+ * in pop transactions where the names of shared elements are known.
+ *
+ * @param containerId The container ID that is executing the transition.
+ * @param records The list of transactions being executed.
+ * @param isRecordPop For each transaction, whether it is a pop transaction or not.
+ * @param startIndex The first index into records and isRecordPop to execute as
+ * part of this transition.
+ * @param endIndex One past the last index into records and isRecordPop to execute
+ * as part of this transition.
+ * @return A map from the initial shared element name to the final shared element name
+ * before any onMapSharedElements is run.
+ */
+ private static ArrayMap<String, String> calculateNameOverrides(int containerId,
+ ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
+ int startIndex, int endIndex) {
+ ArrayMap<String, String> nameOverrides = new ArrayMap<>();
+ for (int recordNum = endIndex - 1; recordNum >= startIndex; recordNum--) {
+ final BackStackRecord record = records.get(recordNum);
+ if (!record.interactsWith(containerId)) {
+ continue;
+ }
+ final boolean isPop = isRecordPop.get(recordNum);
+ if (record.mSharedElementSourceNames != null) {
+ final int numSharedElements = record.mSharedElementSourceNames.size();
+ final ArrayList<String> sources;
+ final ArrayList<String> targets;
+ if (isPop) {
+ targets = record.mSharedElementSourceNames;
+ sources = record.mSharedElementTargetNames;
+ } else {
+ sources = record.mSharedElementSourceNames;
+ targets = record.mSharedElementTargetNames;
+ }
+ for (int i = 0; i < numSharedElements; i++) {
+ String sourceName = sources.get(i);
+ String targetName = targets.get(i);
+ String previousTarget = nameOverrides.remove(targetName);
+ if (previousTarget != null) {
+ nameOverrides.put(sourceName, previousTarget);
+ } else {
+ nameOverrides.put(sourceName, targetName);
+ }
+ }
+ }
+ }
+ return nameOverrides;
+ }
+
+ /**
+ * Configures a transition for a single fragment container for which the transaction was
+ * optimized. That means that all Fragment Views have been added and incoming fragment
+ * Views are marked invisible.
+ *
+ * @param fragmentManager The executing FragmentManagerImpl
+ * @param containerId The container ID that is executing the transition.
+ * @param fragments A structure holding the transitioning fragments in this container.
+ * @param nonExistentView A View that does not exist in the hierarchy. This is used to
+ * prevent transitions from acting on other Views when there is no
+ * other target.
+ * @param nameOverrides A map of the shared element names from the starting fragment to
+ * the final fragment's Views as given in
+ * {@link FragmentTransaction#addSharedElement(View, String)}.
+ */
+ private static void configureTransitionsOptimized(FragmentManagerImpl fragmentManager,
+ int containerId, FragmentContainerTransition fragments,
+ View nonExistentView, ArrayMap<String, String> nameOverrides) {
+ ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+ if (sceneRoot == null) {
+ return;
+ }
+ final Fragment inFragment = fragments.lastIn;
+ final Fragment outFragment = fragments.firstOut;
+ final boolean inIsPop = fragments.lastInIsPop;
+ final boolean outIsPop = fragments.firstOutIsPop;
+
+ ArrayList<View> sharedElementsIn = new ArrayList<>();
+ ArrayList<View> sharedElementsOut = new ArrayList<>();
+ Object enterTransition = getEnterTransition(inFragment, inIsPop);
+ Object exitTransition = getExitTransition(outFragment, outIsPop);
+
+ Object sharedElementTransition = configureSharedElementsOptimized(sceneRoot,
+ nonExistentView, nameOverrides, fragments, sharedElementsOut, sharedElementsIn,
+ enterTransition, exitTransition);
+
+ if (enterTransition == null && sharedElementTransition == null
+ && exitTransition == null) {
+ return; // no transitions!
+ }
+
+ ArrayList<View> exitingViews = configureEnteringExitingViews(exitTransition,
+ outFragment, sharedElementsOut, nonExistentView);
+
+ ArrayList<View> enteringViews = configureEnteringExitingViews(enterTransition,
+ inFragment, sharedElementsIn, nonExistentView);
+
+ setViewVisibility(enteringViews, View.INVISIBLE);
+
+ Object transition = mergeTransitions(enterTransition, exitTransition,
+ sharedElementTransition, inFragment, inIsPop);
+
+ if (transition != null) {
+ replaceHide(exitTransition, outFragment, exitingViews);
+ ArrayList<String> inNames =
+ FragmentTransitionCompat21.prepareSetNameOverridesOptimized(sharedElementsIn);
+ FragmentTransitionCompat21.scheduleRemoveTargets(transition,
+ enterTransition, enteringViews, exitTransition, exitingViews,
+ sharedElementTransition, sharedElementsIn);
+ FragmentTransitionCompat21.beginDelayedTransition(sceneRoot, transition);
+ FragmentTransitionCompat21.setNameOverridesOptimized(sceneRoot, sharedElementsOut,
+ sharedElementsIn, inNames, nameOverrides);
+ setViewVisibility(enteringViews, View.VISIBLE);
+ FragmentTransitionCompat21.swapSharedElementTargets(sharedElementTransition,
+ sharedElementsOut, sharedElementsIn);
+ }
+ }
+
+ /**
+ * Replace hide operations with visibility changes on the exiting views. Instead of making
+ * the entire fragment's view GONE, make each exiting view INVISIBLE. At the end of the
+ * transition, make the fragment's view GONE.
+ */
+ private static void replaceHide(Object exitTransition, Fragment exitingFragment,
+ final ArrayList<View> exitingViews) {
+ if (exitingFragment != null && exitTransition != null && exitingFragment.mAdded
+ && exitingFragment.mHidden && exitingFragment.mHiddenChanged) {
+ exitingFragment.setHideReplaced(true);
+ FragmentTransitionCompat21.scheduleHideFragmentView(exitTransition,
+ exitingFragment.getView(), exitingViews);
+ final ViewGroup container = exitingFragment.mContainer;
+ container.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ container.getViewTreeObserver().removeOnPreDrawListener(this);
+ setViewVisibility(exitingViews, View.INVISIBLE);
+ return true;
+ }
+ });
+ }
+ }
+
+ /**
+ * Configures a transition for a single fragment container for which the transaction was
+ * not optimized. That means that the transaction has not been executed yet, so incoming
+ * Views are not yet known.
+ *
+ * @param fragmentManager The executing FragmentManagerImpl
+ * @param containerId The container ID that is executing the transition.
+ * @param fragments A structure holding the transitioning fragments in this container.
+ * @param nonExistentView A View that does not exist in the hierarchy. This is used to
+ * prevent transitions from acting on other Views when there is no
+ * other target.
+ * @param nameOverrides A map of the shared element names from the starting fragment to
+ * the final fragment's Views as given in
+ * {@link FragmentTransaction#addSharedElement(View, String)}.
+ */
+ private static void configureTransitionsUnoptimized(FragmentManagerImpl fragmentManager,
+ int containerId, FragmentContainerTransition fragments,
+ View nonExistentView, ArrayMap<String, String> nameOverrides) {
+ ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+ if (sceneRoot == null) {
+ return;
+ }
+ final Fragment inFragment = fragments.lastIn;
+ final Fragment outFragment = fragments.firstOut;
+ final boolean inIsPop = fragments.lastInIsPop;
+ final boolean outIsPop = fragments.firstOutIsPop;
+
+ Object enterTransition = getEnterTransition(inFragment, inIsPop);
+ Object exitTransition = getExitTransition(outFragment, outIsPop);
+
+ ArrayList<View> sharedElementsOut = new ArrayList<>();
+ ArrayList<View> sharedElementsIn = new ArrayList<>();
+
+ Object sharedElementTransition = configureSharedElementsUnoptimized(sceneRoot,
+ nonExistentView, nameOverrides, fragments, sharedElementsOut, sharedElementsIn,
+ enterTransition, exitTransition);
+
+ if (enterTransition == null && sharedElementTransition == null
+ && exitTransition == null) {
+ return; // no transitions!
+ }
+
+ ArrayList<View> exitingViews = configureEnteringExitingViews(exitTransition,
+ outFragment, sharedElementsOut, nonExistentView);
+
+ if (exitingViews == null || exitingViews.isEmpty()) {
+ exitTransition = null;
+ }
+
+ // Ensure the entering transition doesn't target anything until the views are made
+ // visible
+ FragmentTransitionCompat21.addTarget(enterTransition, nonExistentView);
+
+ Object transition = mergeTransitions(enterTransition, exitTransition,
+ sharedElementTransition, inFragment, fragments.lastInIsPop);
+
+ if (transition != null) {
+ final ArrayList<View> enteringViews = new ArrayList<>();
+ FragmentTransitionCompat21.scheduleRemoveTargets(transition,
+ enterTransition, enteringViews, exitTransition, exitingViews,
+ sharedElementTransition, sharedElementsIn);
+ scheduleTargetChange(sceneRoot, inFragment, nonExistentView, sharedElementsIn,
+ enterTransition, enteringViews, exitTransition, exitingViews);
+ FragmentTransitionCompat21.setNameOverridesUnoptimized(sceneRoot, sharedElementsIn,
+ nameOverrides);
+
+ FragmentTransitionCompat21.beginDelayedTransition(sceneRoot, transition);
+ FragmentTransitionCompat21.scheduleNameReset(sceneRoot, sharedElementsIn,
+ nameOverrides);
+ }
+ }
+
+ /**
+ * This method is used for fragment transitions for unoptimized transactions to change the
+ * enter and exit transition targets after the call to
+ * {@link FragmentTransitionCompat21#beginDelayedTransition(ViewGroup, Object)}. The exit
+ * transition must ensure that it does not target any Views and the enter transition must start
+ * targeting the Views of the incoming Fragment.
+ *
+ * @param sceneRoot The fragment container View
+ * @param inFragment The last fragment that is entering
+ * @param nonExistentView A view that does not exist in the hierarchy that is used as a
+ * transition target to ensure no View is targeted.
+ * @param sharedElementsIn The shared element Views of the incoming fragment
+ * @param enterTransition The enter transition of the incoming fragment
+ * @param enteringViews The entering Views of the incoming fragment
+ * @param exitTransition The exit transition of the outgoing fragment
+ * @param exitingViews The exiting views of the outgoing fragment
+ */
+ private static void scheduleTargetChange(final ViewGroup sceneRoot,
+ final Fragment inFragment, final View nonExistentView,
+ final ArrayList<View> sharedElementsIn,
+ final Object enterTransition, final ArrayList<View> enteringViews,
+ final Object exitTransition, final ArrayList<View> exitingViews) {
+
+ sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+
+ if (enterTransition != null) {
+ FragmentTransitionCompat21.removeTarget(enterTransition,
+ nonExistentView);
+ ArrayList<View> views = configureEnteringExitingViews(
+ enterTransition, inFragment, sharedElementsIn, nonExistentView);
+ enteringViews.addAll(views);
+ }
+
+ if (exitingViews != null) {
+ ArrayList<View> tempExiting = new ArrayList<>();
+ tempExiting.add(nonExistentView);
+ FragmentTransitionCompat21.replaceTargets(exitTransition, exitingViews,
+ tempExiting);
+ exitingViews.clear();
+ exitingViews.add(nonExistentView);
+ }
+
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Returns a TransitionSet containing the shared element transition. The wrapping TransitionSet
+ * targets all shared elements to ensure that no other Views are targeted. The shared element
+ * transition can then target any or all shared elements without worrying about accidentally
+ * targeting entering or exiting Views.
+ *
+ * @param inFragment The incoming fragment
+ * @param outFragment the outgoing fragment
+ * @param isPop True if this is a pop transaction or false if it is a normal (add) transaction.
+ * @return A TransitionSet wrapping the shared element transition or null if no such transition
+ * exists.
+ */
+ private static Object getSharedElementTransition(Fragment inFragment,
+ Fragment outFragment, boolean isPop) {
+ if (inFragment == null || outFragment == null) {
+ return null;
+ }
+ Object transition = FragmentTransitionCompat21.cloneTransition(isPop
+ ? outFragment.getSharedElementReturnTransition()
+ : inFragment.getSharedElementEnterTransition());
+ return FragmentTransitionCompat21.wrapTransitionInSet(transition);
+ }
+
+ /**
+ * Returns a clone of the enter transition or null if no such transition exists.
+ */
+ private static Object getEnterTransition(Fragment inFragment, boolean isPop) {
+ if (inFragment == null) {
+ return null;
+ }
+ return FragmentTransitionCompat21.cloneTransition(isPop
+ ? inFragment.getReenterTransition()
+ : inFragment.getEnterTransition());
+ }
+
+ /**
+ * Returns a clone of the exit transition or null if no such transition exists.
+ */
+ private static Object getExitTransition(Fragment outFragment, boolean isPop) {
+ if (outFragment == null) {
+ return null;
+ }
+ return FragmentTransitionCompat21.cloneTransition(isPop
+ ? outFragment.getReturnTransition()
+ : outFragment.getExitTransition());
+ }
+
+ /**
+ * Configures the shared elements of an optimized fragment transaction's transition.
+ * This retrieves the shared elements of the outgoing and incoming fragments, maps the
+ * views, and sets up the epicenter on the transitions.
+ * <p>
+ * The epicenter of exit and shared element transitions is the first shared element
+ * in the outgoing fragment. The epicenter of the entering transition is the first shared
+ * element in the incoming fragment.
+ *
+ * @param sceneRoot The fragment container View
+ * @param nonExistentView A View that does not exist in the hierarchy. This is used to
+ * prevent transitions from acting on other Views when there is no
+ * other target.
+ * @param nameOverrides A map of the shared element names from the starting fragment to
+ * the final fragment's Views as given in
+ * {@link FragmentTransaction#addSharedElement(View, String)}.
+ * @param fragments A structure holding the transitioning fragments in this container.
+ * @param sharedElementsOut A list modified to contain the shared elements in the outgoing
+ * fragment
+ * @param sharedElementsIn A list modified to contain the shared elements in the incoming
+ * fragment
+ * @param enterTransition The transition used for entering Views, modified by applying the
+ * epicenter
+ * @param exitTransition The transition used for exiting Views, modified by applying the
+ * epicenter
+ * @return The shared element transition or null if no shared elements exist
+ */
+ private static Object configureSharedElementsOptimized(final ViewGroup sceneRoot,
+ final View nonExistentView, final ArrayMap<String, String> nameOverrides,
+ final FragmentContainerTransition fragments,
+ final ArrayList<View> sharedElementsOut,
+ final ArrayList<View> sharedElementsIn,
+ final Object enterTransition, final Object exitTransition) {
+ final Fragment inFragment = fragments.lastIn;
+ final Fragment outFragment = fragments.firstOut;
+ if (inFragment != null) {
+ inFragment.getView().setVisibility(View.VISIBLE);
+ }
+ if (inFragment == null || outFragment == null) {
+ return null; // no shared element without a fragment
+ }
+
+ final boolean inIsPop = fragments.lastInIsPop;
+ Object sharedElementTransition = nameOverrides.isEmpty() ? null
+ : getSharedElementTransition(inFragment, outFragment, inIsPop);
+
+ final ArrayMap<String, View> outSharedElements = captureOutSharedElements(nameOverrides,
+ sharedElementTransition, fragments);
+
+ final ArrayMap<String, View> inSharedElements = captureInSharedElements(nameOverrides,
+ sharedElementTransition, fragments);
+
+ if (nameOverrides.isEmpty()) {
+ sharedElementTransition = null;
+ } else {
+ sharedElementsOut.addAll(outSharedElements.values());
+ sharedElementsIn.addAll(inSharedElements.values());
+ }
+
+ if (enterTransition == null && exitTransition == null && sharedElementTransition == null) {
+ // don't call onSharedElementStart/End since there is no transition
+ return null;
+ }
+
+ callSharedElementStartEnd(inFragment, outFragment, inIsPop, outSharedElements, true);
+
+ final Rect epicenter;
+ final View epicenterView;
+ if (sharedElementTransition != null) {
+ sharedElementsIn.add(nonExistentView);
+ FragmentTransitionCompat21.setSharedElementTargets(sharedElementTransition,
+ nonExistentView, sharedElementsOut);
+ final boolean outIsPop = fragments.firstOutIsPop;
+ final BackStackRecord outTransaction = fragments.firstOutTransaction;
+ setOutEpicenter(sharedElementTransition, exitTransition, outSharedElements, outIsPop,
+ outTransaction);
+ epicenter = new Rect();
+ epicenterView = getInEpicenterView(inSharedElements, fragments,
+ enterTransition, inIsPop);
+ if (epicenterView != null) {
+ FragmentTransitionCompat21.setEpicenter(enterTransition, epicenter);
+ }
+ } else {
+ epicenter = null;
+ epicenterView = null;
+ }
+
+ sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ callSharedElementStartEnd(inFragment, outFragment, inIsPop,
+ inSharedElements, false);
+ if (epicenterView != null) {
+ FragmentTransitionCompat21.getBoundsOnScreen(epicenterView, epicenter);
+ }
+ return true;
+ }
+ });
+ return sharedElementTransition;
+ }
+
+ /**
+ * Configures the shared elements of an unoptimized fragment transaction's transition.
+ * This retrieves the shared elements of the incoming fragments, and schedules capturing
+ * the incoming fragment's shared elements. It also maps the views, and sets up the epicenter
+ * on the transitions.
+ * <p>
+ * The epicenter of exit and shared element transitions is the first shared element
+ * in the outgoing fragment. The epicenter of the entering transition is the first shared
+ * element in the incoming fragment.
+ *
+ * @param sceneRoot The fragment container View
+ * @param nonExistentView A View that does not exist in the hierarchy. This is used to
+ * prevent transitions from acting on other Views when there is no
+ * other target.
+ * @param nameOverrides A map of the shared element names from the starting fragment to
+ * the final fragment's Views as given in
+ * {@link FragmentTransaction#addSharedElement(View, String)}.
+ * @param fragments A structure holding the transitioning fragments in this container.
+ * @param sharedElementsOut A list modified to contain the shared elements in the outgoing
+ * fragment
+ * @param sharedElementsIn A list modified to contain the shared elements in the incoming
+ * fragment
+ * @param enterTransition The transition used for entering Views, modified by applying the
+ * epicenter
+ * @param exitTransition The transition used for exiting Views, modified by applying the
+ * epicenter
+ * @return The shared element transition or null if no shared elements exist
+ */
+ private static Object configureSharedElementsUnoptimized(final ViewGroup sceneRoot,
+ final View nonExistentView, final ArrayMap<String, String> nameOverrides,
+ final FragmentContainerTransition fragments,
+ final ArrayList<View> sharedElementsOut,
+ final ArrayList<View> sharedElementsIn,
+ final Object enterTransition, final Object exitTransition) {
+ final Fragment inFragment = fragments.lastIn;
+ final Fragment outFragment = fragments.firstOut;
+
+ if (inFragment == null || outFragment == null) {
+ return null; // no transition
+ }
+
+ final boolean inIsPop = fragments.lastInIsPop;
+ Object sharedElementTransition = nameOverrides.isEmpty() ? null
+ : getSharedElementTransition(inFragment, outFragment, inIsPop);
+
+ ArrayMap<String, View> outSharedElements = captureOutSharedElements(nameOverrides,
+ sharedElementTransition, fragments);
+
+ if (nameOverrides.isEmpty()) {
+ sharedElementTransition = null;
+ } else {
+ sharedElementsOut.addAll(outSharedElements.values());
+ }
+
+ if (enterTransition == null && exitTransition == null && sharedElementTransition == null) {
+ // don't call onSharedElementStart/End since there is no transition
+ return null;
+ }
+
+ callSharedElementStartEnd(inFragment, outFragment, inIsPop, outSharedElements, true);
+
+ final Rect inEpicenter;
+ if (sharedElementTransition != null) {
+ inEpicenter = new Rect();
+ FragmentTransitionCompat21.setSharedElementTargets(sharedElementTransition,
+ nonExistentView, sharedElementsOut);
+ final boolean outIsPop = fragments.firstOutIsPop;
+ final BackStackRecord outTransaction = fragments.firstOutTransaction;
+ setOutEpicenter(sharedElementTransition, exitTransition, outSharedElements, outIsPop,
+ outTransaction);
+ if (enterTransition != null) {
+ FragmentTransitionCompat21.setEpicenter(enterTransition, inEpicenter);
+ }
+ } else {
+ inEpicenter = null;
+ }
+
+ final Object finalSharedElementTransition = sharedElementTransition;
+
+ sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ ArrayMap<String, View> inSharedElements = captureInSharedElements(
+ nameOverrides, finalSharedElementTransition, fragments);
+
+ if (inSharedElements != null) {
+ sharedElementsIn.addAll(inSharedElements.values());
+ sharedElementsIn.add(nonExistentView);
+ }
+
+ callSharedElementStartEnd(inFragment, outFragment, inIsPop,
+ inSharedElements, false);
+ if (finalSharedElementTransition != null) {
+ FragmentTransitionCompat21.swapSharedElementTargets(
+ finalSharedElementTransition, sharedElementsOut,
+ sharedElementsIn);
+
+ final View inEpicenterView = getInEpicenterView(inSharedElements,
+ fragments, enterTransition, inIsPop);
+ if (inEpicenterView != null) {
+ FragmentTransitionCompat21.getBoundsOnScreen(inEpicenterView,
+ inEpicenter);
+ }
+ }
+ return true;
+ }
+ });
+ return sharedElementTransition;
+ }
+
+ /**
+ * Finds the shared elements in the outgoing fragment. It also calls
+ * {@link SharedElementCallback#onMapSharedElements(List, Map)} to allow more control
+ * of the shared element mapping. {@code nameOverrides} is updated to match the
+ * actual transition name of the mapped shared elements.
+ *
+ * @param nameOverrides A map of the shared element names from the starting fragment to
+ * the final fragment's Views as given in
+ * {@link FragmentTransaction#addSharedElement(View, String)}.
+ * @param sharedElementTransition The shared element transition
+ * @param fragments A structure holding the transitioning fragments in this container.
+ * @return The mapping of shared element names to the Views in the hierarchy or null
+ * if there is no shared element transition.
+ */
+ private static ArrayMap<String, View> captureOutSharedElements(
+ ArrayMap<String, String> nameOverrides, Object sharedElementTransition,
+ FragmentContainerTransition fragments) {
+ if (nameOverrides.isEmpty() || sharedElementTransition == null) {
+ nameOverrides.clear();
+ return null;
+ }
+ final Fragment outFragment = fragments.firstOut;
+ final ArrayMap<String, View> outSharedElements = new ArrayMap<>();
+ FragmentTransitionCompat21.findNamedViews(outSharedElements, outFragment.getView());
+
+ final SharedElementCallback sharedElementCallback;
+ final ArrayList<String> names;
+ final BackStackRecord outTransaction = fragments.firstOutTransaction;
+ if (fragments.firstOutIsPop) {
+ sharedElementCallback = outFragment.getEnterTransitionCallback();
+ names = outTransaction.mSharedElementTargetNames;
+ } else {
+ sharedElementCallback = outFragment.getExitTransitionCallback();
+ names = outTransaction.mSharedElementSourceNames;
+ }
+
+ outSharedElements.retainAll(names);
+ if (sharedElementCallback != null) {
+ sharedElementCallback.onMapSharedElements(names, outSharedElements);
+ for (int i = names.size() - 1; i >= 0; i--) {
+ String name = names.get(i);
+ View view = outSharedElements.get(name);
+ if (view == null) {
+ nameOverrides.remove(name);
+ } else if (!name.equals(ViewCompat.getTransitionName(view))) {
+ String targetValue = nameOverrides.remove(name);
+ nameOverrides.put(ViewCompat.getTransitionName(view), targetValue);
+ }
+ }
+ } else {
+ nameOverrides.retainAll(outSharedElements.keySet());
+ }
+ return outSharedElements;
+ }
+
+ /**
+ * Finds the shared elements in the incoming fragment. It also calls
+ * {@link SharedElementCallback#onMapSharedElements(List, Map)} to allow more control
+ * of the shared element mapping. {@code nameOverrides} is updated to match the
+ * actual transition name of the mapped shared elements.
+ *
+ * @param nameOverrides A map of the shared element names from the starting fragment to
+ * the final fragment's Views as given in
+ * {@link FragmentTransaction#addSharedElement(View, String)}.
+ * @param sharedElementTransition The shared element transition
+ * @param fragments A structure holding the transitioning fragments in this container.
+ * @return The mapping of shared element names to the Views in the hierarchy or null
+ * if there is no shared element transition.
+ */
+ private static ArrayMap<String, View> captureInSharedElements(
+ ArrayMap<String, String> nameOverrides, Object sharedElementTransition,
+ FragmentContainerTransition fragments) {
+ Fragment inFragment = fragments.lastIn;
+ final View fragmentView = inFragment.getView();
+ if (nameOverrides.isEmpty() || sharedElementTransition == null || fragmentView == null) {
+ nameOverrides.clear();
+ return null;
+ }
+ final ArrayMap<String, View> inSharedElements = new ArrayMap<>();
+ FragmentTransitionCompat21.findNamedViews(inSharedElements, fragmentView);
+
+ final SharedElementCallback sharedElementCallback;
+ final ArrayList<String> names;
+ final BackStackRecord inTransaction = fragments.lastInTransaction;
+ if (fragments.lastInIsPop) {
+ sharedElementCallback = inFragment.getExitTransitionCallback();
+ names = inTransaction.mSharedElementSourceNames;
+ } else {
+ sharedElementCallback = inFragment.getEnterTransitionCallback();
+ names = inTransaction.mSharedElementTargetNames;
+ }
+
+ inSharedElements.retainAll(names);
+ if (sharedElementCallback != null) {
+ sharedElementCallback.onMapSharedElements(names, inSharedElements);
+ for (int i = names.size() - 1; i >= 0; i--) {
+ String name = names.get(i);
+ View view = inSharedElements.get(name);
+ if (view == null) {
+ String key = findKeyForValue(nameOverrides, name);
+ if (key != null) {
+ nameOverrides.remove(key);
+ }
+ } else if (!name.equals(ViewCompat.getTransitionName(view))) {
+ String key = findKeyForValue(nameOverrides, name);
+ if (key != null) {
+ nameOverrides.put(key, ViewCompat.getTransitionName(view));
+ }
+ }
+ }
+ } else {
+ retainValues(nameOverrides, inSharedElements);
+ }
+ return inSharedElements;
+ }
+
+ /**
+ * Utility to find the String key in {@code map} that maps to {@code value}.
+ */
+ private static String findKeyForValue(ArrayMap<String, String> map, String value) {
+ final int numElements = map.size();
+ for (int i = 0; i < numElements; i++) {
+ if (value.equals(map.valueAt(i))) {
+ return map.keyAt(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the View in the incoming Fragment that should be used as the epicenter.
+ *
+ * @param inSharedElements The mapping of shared element names to Views in the
+ * incoming fragment.
+ * @param fragments A structure holding the transitioning fragments in this container.
+ * @param enterTransition The transition used for the incoming Fragment's views
+ * @param inIsPop Is the incoming fragment being added as a pop transaction?
+ */
+ private static View getInEpicenterView(ArrayMap<String, View> inSharedElements,
+ FragmentContainerTransition fragments,
+ Object enterTransition, boolean inIsPop) {
+ BackStackRecord inTransaction = fragments.lastInTransaction;
+ if (enterTransition != null && inTransaction.mSharedElementSourceNames != null
+ && !inTransaction.mSharedElementSourceNames.isEmpty()) {
+ final String targetName = inIsPop
+ ? inTransaction.mSharedElementSourceNames.get(0)
+ : inTransaction.mSharedElementTargetNames.get(0);
+ return inSharedElements.get(targetName);
+ }
+ return null;
+ }
+
+ /**
+ * Sets the epicenter for the exit transition.
+ *
+ * @param sharedElementTransition The shared element transition
+ * @param exitTransition The transition for the outgoing fragment's views
+ * @param outSharedElements Shared elements in the outgoing fragment
+ * @param outIsPop Is the outgoing fragment being removed as a pop transaction?
+ * @param outTransaction The transaction that caused the fragment to be removed.
+ */
+ private static void setOutEpicenter(Object sharedElementTransition,
+ Object exitTransition, ArrayMap<String, View> outSharedElements, boolean outIsPop,
+ BackStackRecord outTransaction) {
+ if (outTransaction.mSharedElementSourceNames != null
+ && !outTransaction.mSharedElementSourceNames.isEmpty()) {
+ final String sourceName = outIsPop
+ ? outTransaction.mSharedElementTargetNames.get(0)
+ : outTransaction.mSharedElementSourceNames.get(0);
+ final View outEpicenterView = outSharedElements.get(sourceName);
+ FragmentTransitionCompat21.setEpicenter(sharedElementTransition, outEpicenterView);
+
+ if (exitTransition != null) {
+ FragmentTransitionCompat21.setEpicenter(exitTransition, outEpicenterView);
+ }
+ }
+ }
+
+ /**
+ * A utility to retain only the mappings in {@code nameOverrides} that have a value
+ * that has a key in {@code namedViews}. This is a useful equivalent to
+ * {@link ArrayMap#retainAll(Collection)} for values.
+ */
+ private static void retainValues(ArrayMap<String, String> nameOverrides,
+ ArrayMap<String, View> namedViews) {
+ for (int i = nameOverrides.size() - 1; i >= 0; i--) {
+ final String targetName = nameOverrides.valueAt(i);
+ if (!namedViews.containsKey(targetName)) {
+ nameOverrides.removeAt(i);
+ }
+ }
+ }
+
+ /**
+ * Calls the {@link SharedElementCallback#onSharedElementStart(List, List, List)} or
+ * {@link SharedElementCallback#onSharedElementEnd(List, List, List)} on the appropriate
+ * incoming or outgoing fragment.
+ *
+ * @param inFragment The incoming fragment
+ * @param outFragment The outgoing fragment
+ * @param isPop Is the incoming fragment part of a pop transaction?
+ * @param sharedElements The shared element Views
+ * @param isStart Call the start or end call on the SharedElementCallback
+ */
+ private static void callSharedElementStartEnd(Fragment inFragment, Fragment outFragment,
+ boolean isPop, ArrayMap<String, View> sharedElements, boolean isStart) {
+ SharedElementCallback sharedElementCallback = isPop
+ ? outFragment.getEnterTransitionCallback()
+ : inFragment.getEnterTransitionCallback();
+ if (sharedElementCallback != null) {
+ ArrayList<View> views = new ArrayList<>();
+ ArrayList<String> names = new ArrayList<>();
+ final int count = sharedElements == null ? 0 : sharedElements.size();
+ for (int i = 0; i < count; i++) {
+ names.add(sharedElements.keyAt(i));
+ views.add(sharedElements.valueAt(i));
+ }
+ if (isStart) {
+ sharedElementCallback.onSharedElementStart(names, views, null);
+ } else {
+ sharedElementCallback.onSharedElementEnd(names, views, null);
+ }
+ }
+ }
+
+ private static ArrayList<View> configureEnteringExitingViews(Object transition,
+ Fragment fragment, ArrayList<View> sharedElements, View nonExistentView) {
+ ArrayList<View> viewList = null;
+ if (transition != null) {
+ viewList = new ArrayList<>();
+ View root = fragment.getView();
+ FragmentTransitionCompat21.captureTransitioningViews(viewList, root);
+ if (sharedElements != null) {
+ viewList.removeAll(sharedElements);
+ }
+ if (!viewList.isEmpty()) {
+ viewList.add(nonExistentView);
+ FragmentTransitionCompat21.addTargets(transition, viewList);
+ }
+ }
+ return viewList;
+ }
+
+ /**
+ * Sets the visibility of all Views in {@code views} to {@code visibility}.
+ */
+ private static void setViewVisibility(ArrayList<View> views, int visibility) {
+ if (views == null) {
+ return;
+ }
+ for (int i = views.size() - 1; i >= 0; i--) {
+ final View view = views.get(i);
+ view.setVisibility(visibility);
+ }
+ }
+
+ /**
+ * Merges exit, shared element, and enter transitions so that they act together or
+ * sequentially as defined in the fragments.
+ */
+ private static Object mergeTransitions(Object enterTransition,
+ Object exitTransition, Object sharedElementTransition, Fragment inFragment,
+ boolean isPop) {
+ boolean overlap = true;
+ if (enterTransition != null && exitTransition != null && inFragment != null) {
+ overlap = isPop ? inFragment.getAllowReturnTransitionOverlap() :
+ inFragment.getAllowEnterTransitionOverlap();
+ }
+
+ // Wrap the transitions. Explicit targets like in enter and exit will cause the
+ // views to be targeted regardless of excluded views. If that happens, then the
+ // excluded fragments views (hidden fragments) will still be in the transition.
+
+ Object transition;
+ if (overlap) {
+ // Regular transition -- do it all together
+ transition = FragmentTransitionCompat21.mergeTransitionsTogether(exitTransition,
+ enterTransition, sharedElementTransition);
+ } else {
+ // First do exit, then enter, but allow shared element transition to happen
+ // during both.
+ transition = FragmentTransitionCompat21.mergeTransitionsInSequence(exitTransition,
+ enterTransition, sharedElementTransition);
+ }
+ return transition;
+ }
+
+ /**
+ * Finds the first removed fragment and last added fragments when going forward.
+ * If none of the fragments have transitions, then both lists will be empty.
+ *
+ * @param transitioningFragments Keyed on the container ID, the first fragments to be removed,
+ * and last fragments to be added. This will be modified by
+ * this method.
+ */
+ public static void calculateFragments(BackStackRecord transaction,
+ SparseArray<FragmentContainerTransition> transitioningFragments,
+ boolean isOptimized) {
+ final int numOps = transaction.mOps.size();
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ final BackStackRecord.Op op = transaction.mOps.get(opNum);
+ addToFirstInLastOut(transaction, op, transitioningFragments, false, isOptimized);
+ }
+ }
+
+ /**
+ * Finds the first removed fragment and last added fragments when popping the back stack.
+ * If none of the fragments have transitions, then both lists will be empty.
+ *
+ * @param transitioningFragments Keyed on the container ID, the first fragments to be removed,
+ * and last fragments to be added. This will be modified by
+ * this method.
+ */
+ public static void calculatePopFragments(BackStackRecord transaction,
+ SparseArray<FragmentContainerTransition> transitioningFragments, boolean isOptimized) {
+ if (!transaction.mManager.mContainer.onHasView()) {
+ return; // nothing to see, so no transitions
+ }
+ final int numOps = transaction.mOps.size();
+ for (int opNum = numOps - 1; opNum >= 0; opNum--) {
+ final BackStackRecord.Op op = transaction.mOps.get(opNum);
+ addToFirstInLastOut(transaction, op, transitioningFragments, true, isOptimized);
+ }
+ }
+
+ /**
+ * Examines the {@code command} and may set the first out or last in fragment for the fragment's
+ * container.
+ *
+ * @param transaction The executing transaction
+ * @param op The operation being run.
+ * @param transitioningFragments A structure holding the first in and last out fragments
+ * for each fragment container.
+ * @param isPop Is the operation a pop?
+ * @param isOptimizedTransaction True if the operations have been partially executed and the
+ * added fragments have Views in the hierarchy or false if the
+ * operations haven't been executed yet.
+ */
+ private static void addToFirstInLastOut(BackStackRecord transaction, BackStackRecord.Op op,
+ SparseArray<FragmentContainerTransition> transitioningFragments, boolean isPop,
+ boolean isOptimizedTransaction) {
+ final Fragment fragment = op.fragment;
+ final int containerId = fragment.mContainerId;
+ if (containerId == 0) {
+ return; // no container, no transition
+ }
+ final int command = isPop ? INVERSE_OPS[op.cmd] : op.cmd;
+ boolean setLastIn = false;
+ boolean wasRemoved = false;
+ boolean setFirstOut = false;
+ boolean wasAdded = false;
+ switch (command) {
+ case BackStackRecord.OP_SHOW:
+ if (isOptimizedTransaction) {
+ setLastIn = fragment.mHiddenChanged && !fragment.mHidden && fragment.mAdded;
+ } else {
+ setLastIn = fragment.mHidden;
+ }
+ wasAdded = true;
+ break;
+ case BackStackRecord.OP_ADD:
+ case BackStackRecord.OP_ATTACH:
+ if (isOptimizedTransaction) {
+ setLastIn = fragment.mIsNewlyAdded;
+ } else {
+ setLastIn = !fragment.mAdded && !fragment.mHidden;
+ }
+ wasAdded = true;
+ break;
+ case BackStackRecord.OP_HIDE:
+ if (isOptimizedTransaction) {
+ setFirstOut = fragment.mHiddenChanged && fragment.mAdded && fragment.mHidden;
+ } else {
+ setFirstOut = fragment.mAdded && !fragment.mHidden;
+ }
+ wasRemoved = true;
+ break;
+ case BackStackRecord.OP_REMOVE:
+ case BackStackRecord.OP_DETACH:
+ if (isOptimizedTransaction) {
+ setFirstOut = !fragment.mAdded && fragment.mView != null
+ && fragment.mView.getVisibility() == View.VISIBLE;
+ } else {
+ setFirstOut = fragment.mAdded && !fragment.mHidden;
+ }
+ wasRemoved = true;
+ break;
+ }
+ FragmentContainerTransition containerTransition = transitioningFragments.get(containerId);
+ if (setLastIn) {
+ containerTransition =
+ ensureContainer(containerTransition, transitioningFragments, containerId);
+ containerTransition.lastIn = fragment;
+ containerTransition.lastInIsPop = isPop;
+ containerTransition.lastInTransaction = transaction;
+ }
+ if (!isOptimizedTransaction && wasAdded) {
+ if (containerTransition != null && containerTransition.firstOut == fragment) {
+ containerTransition.firstOut = null;
+ }
+
+ /**
+ * Ensure that fragments that are entering are at least at the CREATED state
+ * so that they may load Transitions using TransitionInflater.
+ */
+ FragmentManagerImpl manager = transaction.mManager;
+ if (fragment.mState < Fragment.CREATED && manager.mCurState >= Fragment.CREATED
+ && !transaction.mAllowOptimization) {
+ manager.makeActive(fragment);
+ manager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
+ }
+ }
+ if (setFirstOut && (containerTransition == null || containerTransition.firstOut == null)) {
+ containerTransition =
+ ensureContainer(containerTransition, transitioningFragments, containerId);
+ containerTransition.firstOut = fragment;
+ containerTransition.firstOutIsPop = isPop;
+ containerTransition.firstOutTransaction = transaction;
+ }
+
+ if (!isOptimizedTransaction && wasRemoved
+ && (containerTransition != null && containerTransition.lastIn == fragment)) {
+ containerTransition.lastIn = null;
+ }
+ }
+
+ /**
+ * Ensures that a FragmentContainerTransition has been added to the SparseArray. If so,
+ * it returns the existing one. If not, one is created and added to the SparseArray and
+ * returned.
+ */
+ private static FragmentContainerTransition ensureContainer(
+ FragmentContainerTransition containerTransition,
+ SparseArray<FragmentContainerTransition> transitioningFragments, int containerId) {
+ if (containerTransition == null) {
+ containerTransition = new FragmentContainerTransition();
+ transitioningFragments.put(containerId, containerTransition);
+ }
+ return containerTransition;
+ }
+
+ /**
+ * Tracks the last fragment added and first fragment removed for fragment transitions.
+ * This also tracks which fragments are changed by push or pop transactions.
+ */
+ static class FragmentContainerTransition {
+ /**
+ * The last fragment added/attached/shown in its container
+ */
+ public Fragment lastIn;
+
+ /**
+ * true when lastIn was added during a pop transaction or false if added with a push
+ */
+ public boolean lastInIsPop;
+
+ /**
+ * The transaction that included the last in fragment
+ */
+ public BackStackRecord lastInTransaction;
+
+ /**
+ * The first fragment with a View that was removed/detached/hidden in its container.
+ */
+ public Fragment firstOut;
+
+ /**
+ * true when firstOut was removed during a pop transaction or false otherwise
+ */
+ public boolean firstOutIsPop;
+
+ /**
+ * The transaction that included the first out fragment
+ */
+ public BackStackRecord firstOutTransaction;
+ }
+}
diff --git a/fragment/jellybean/android/support/v4/app/BaseFragmentActivityJB.java b/fragment/jellybean/android/support/v4/app/BaseFragmentActivityJB.java
index 147251c..57c5094 100644
--- a/fragment/jellybean/android/support/v4/app/BaseFragmentActivityJB.java
+++ b/fragment/jellybean/android/support/v4/app/BaseFragmentActivityJB.java
@@ -16,16 +16,20 @@
package android.support.v4.app;
+import android.annotation.TargetApi;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
/**
* Base class for {@code FragmentActivity} to be able to use v16 APIs.
*
* @hide
*/
+@RequiresApi(16)
+@TargetApi(16)
abstract class BaseFragmentActivityJB extends BaseFragmentActivityHoneycomb {
// We need to keep track of whether startActivityForResult originated from a Fragment, so we
diff --git a/fragment/tests/java/android/support/v4/app/CountCallsFragment.java b/fragment/tests/java/android/support/v4/app/CountCallsFragment.java
new file mode 100644
index 0000000..bf9cadf
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/CountCallsFragment.java
@@ -0,0 +1,70 @@
+/*
+ * 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.v4.app;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Counts the number of onCreateView, onHiddenChanged (onHide, onShow), onAttach, and onDetach
+ * calls.
+ */
+public class CountCallsFragment extends StrictViewFragment {
+ public int onCreateViewCount = 0;
+ public int onDestroyViewCount = 0;
+ public int onHideCount = 0;
+ public int onShowCount = 0;
+ public int onAttachCount = 0;
+ public int onDetachCount = 0;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ onCreateViewCount++;
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ @Override
+ public void onHiddenChanged(boolean hidden) {
+ if (hidden) {
+ onHideCount++;
+ } else {
+ onShowCount++;
+ }
+ super.onHiddenChanged(hidden);
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ onAttachCount++;
+ super.onAttach(context);
+ }
+
+ @Override
+ public void onDetach() {
+ onDetachCount++;
+ super.onDetach();
+ }
+
+ @Override
+ public void onDestroyView() {
+ onDestroyViewCount++;
+ super.onDestroyView();
+ }
+}
diff --git a/fragment/tests/java/android/support/v4/app/CtsMockitoUtils.java b/fragment/tests/java/android/support/v4/app/CtsMockitoUtils.java
new file mode 100644
index 0000000..c82b61d
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/CtsMockitoUtils.java
@@ -0,0 +1,85 @@
+/*
+ * 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.v4.app;
+
+import android.os.SystemClock;
+
+import org.mockito.exceptions.base.MockitoAssertionError;
+import org.mockito.internal.verification.api.VerificationData;
+import org.mockito.invocation.Invocation;
+import org.mockito.verification.VerificationMode;
+
+import java.util.List;
+
+public class CtsMockitoUtils {
+ private CtsMockitoUtils() {}
+
+ public static VerificationMode within(long timeout) {
+ return new Within(timeout);
+ }
+
+ public static class Within implements VerificationMode {
+ private static final long TIME_SLICE = 50;
+ private final long mTimeout;
+
+ public Within(long timeout) {
+ mTimeout = timeout;
+ }
+
+ @Override
+ public void verify(VerificationData data) {
+ long timeout = mTimeout;
+ MockitoAssertionError errorToRethrow = null;
+ // Loop in the same way we do in PollingCheck, sleeping and then testing for the target
+ // invocation
+ while (timeout > 0) {
+ SystemClock.sleep(TIME_SLICE);
+
+ try {
+ final List<Invocation> actualInvocations = data.getAllInvocations();
+ // Iterate over all invocations so far to see if we have a match
+ for (Invocation invocation : actualInvocations) {
+ if (data.getWanted().matches(invocation)) {
+ // Found our match within our timeout. Mark all invocations as verified
+ markAllInvocationsAsVerified(data);
+ // and return
+ return;
+ }
+ }
+ } catch (MockitoAssertionError assertionError) {
+ errorToRethrow = assertionError;
+ }
+
+ timeout -= TIME_SLICE;
+ }
+
+ if (errorToRethrow != null) {
+ throw errorToRethrow;
+ }
+
+ throw new MockitoAssertionError("Timed out while waiting " + mTimeout + "ms for "
+ + data.getWanted().toString());
+ }
+
+ private void markAllInvocationsAsVerified(VerificationData data) {
+ for (Invocation invocation : data.getAllInvocations()) {
+ invocation.markVerified();
+ data.getWanted().captureArgumentsFrom(invocation);
+ }
+ }
+ }
+
+}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java b/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java
new file mode 100644
index 0000000..9f6b263
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java
@@ -0,0 +1,476 @@
+/*
+ * 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.v4.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.os.Parcelable;
+import android.support.annotation.AnimRes;
+import android.support.fragment.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.support.v4.view.ViewCompat;
+import android.util.Pair;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.TranslateAnimation;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class FragmentAnimationTest {
+ // These are pretend resource IDs for animators. We don't need real ones since we
+ // load them by overriding onCreateAnimator
+ @AnimRes
+ private static final int ENTER = 1;
+ @AnimRes
+ private static final int EXIT = 2;
+ @AnimRes
+ private static final int POP_ENTER = 3;
+ @AnimRes
+ private static final int POP_EXIT = 4;
+
+ @Rule
+ public ActivityTestRule<FragmentTestActivity> mActivityRule =
+ new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+ private Instrumentation mInstrumentation;
+
+ @Before
+ public void setupContainer() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ }
+
+ // Ensure that adding and popping a Fragment uses the enter and popExit animators
+ @Test
+ public void addAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ // One fragment with a view
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .add(R.id.fragmentContainer, fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertEnterPopExit(fragment);
+ }
+
+ // Ensure that removing and popping a Fragment uses the exit and popEnter animators
+ @Test
+ public void removeAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ // One fragment with a view
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .remove(fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertExitPopEnter(fragment);
+ }
+
+ // Ensure that showing and popping a Fragment uses the enter and popExit animators
+ @Test
+ public void showAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ // One fragment with a view
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .show(fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertEnterPopExit(fragment);
+ }
+
+ // Ensure that hiding and popping a Fragment uses the exit and popEnter animators
+ @Test
+ public void hideAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ // One fragment with a view
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .hide(fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertExitPopEnter(fragment);
+ }
+
+ // Ensure that attaching and popping a Fragment uses the enter and popExit animators
+ @Test
+ public void attachAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ // One fragment with a view
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).detach(fragment).commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .attach(fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertEnterPopExit(fragment);
+ }
+
+ // Ensure that detaching and popping a Fragment uses the exit and popEnter animators
+ @Test
+ public void detachAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ // One fragment with a view
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .detach(fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertExitPopEnter(fragment);
+ }
+
+ // Replace should exit the existing fragments and enter the added fragment, then
+ // popping should popExit the removed fragment and popEnter the added fragments
+ @Test
+ public void replaceAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ // One fragment with a view
+ final AnimatorFragment fragment1 = new AnimatorFragment();
+ final AnimatorFragment fragment2 = new AnimatorFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .add(R.id.fragmentContainer, fragment2, "2")
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ final AnimatorFragment fragment3 = new AnimatorFragment();
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .replace(R.id.fragmentContainer, fragment3)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertFragmentAnimation(fragment1, 1, false, EXIT);
+ assertFragmentAnimation(fragment2, 1, false, EXIT);
+ assertFragmentAnimation(fragment3, 1, true, ENTER);
+
+ fm.popBackStack();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertFragmentAnimation(fragment3, 2, false, POP_EXIT);
+ final AnimatorFragment replacement1 = (AnimatorFragment) fm.findFragmentByTag("1");
+ final AnimatorFragment replacement2 = (AnimatorFragment) fm.findFragmentByTag("1");
+ int expectedAnimations = replacement1 == fragment1 ? 2 : 1;
+ assertFragmentAnimation(replacement1, expectedAnimations, true, POP_ENTER);
+ assertFragmentAnimation(replacement2, expectedAnimations, true, POP_ENTER);
+ }
+
+ // Ensure that adding and popping a Fragment uses the enter and popExit animators,
+ // but the animators are delayed when an entering Fragment is postponed.
+ @Test
+ public void postponedAddAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fragment.postponeEnterTransition();
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .add(R.id.fragmentContainer, fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponed(fragment, 0);
+ fragment.startPostponedEnterTransition();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ assertEnterPopExit(fragment);
+ }
+
+ // Ensure that removing and popping a Fragment uses the exit and popEnter animators,
+ // but the animators are delayed when an entering Fragment is postponed.
+ @Test
+ public void postponedRemoveAnimators() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ final AnimatorFragment fragment = new AnimatorFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .remove(fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertExitPostponedPopEnter(fragment);
+ }
+
+ // Ensure that adding and popping a Fragment is postponed in both directions
+ // when the fragments have been marked for postponing.
+ @Test
+ public void postponedAddRemove() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ final AnimatorFragment fragment1 = new AnimatorFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ final AnimatorFragment fragment2 = new AnimatorFragment();
+ fragment2.postponeEnterTransition();
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponed(fragment2, 0);
+ assertNotNull(fragment1.getView());
+ assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
+ assertTrue(ViewCompat.isAttachedToWindow(fragment1.getView()));
+
+ fragment2.startPostponedEnterTransition();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertExitPostponedPopEnter(fragment1);
+ }
+
+ // Popping a postponed transaction should result in no animators
+ @Test
+ public void popPostponed() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ final AnimatorFragment fragment1 = new AnimatorFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ assertEquals(0, fragment1.numAnimators);
+
+ final AnimatorFragment fragment2 = new AnimatorFragment();
+ fragment2.postponeEnterTransition();
+
+ fm.beginTransaction()
+ .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponed(fragment2, 0);
+
+ // Now pop the postponed transaction
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertNotNull(fragment1.getView());
+ assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
+ assertTrue(ViewCompat.isAttachedToWindow(fragment1.getView()));
+ assertTrue(fragment1.isAdded());
+
+ assertNull(fragment2.getView());
+ assertFalse(fragment2.isAdded());
+
+ assertEquals(0, fragment1.numAnimators);
+ assertEquals(0, fragment2.numAnimators);
+ assertNull(fragment1.animation);
+ assertNull(fragment2.animation);
+ }
+
+ // Make sure that if the state was saved while a Fragment was animating that its
+ // state is proper after restoring.
+ @Test
+ public void saveWhileAnimatingAway() throws Throwable {
+ final FragmentController fc1 = FragmentTestUtil.createController(mActivityRule);
+ FragmentTestUtil.resume(fc1, null);
+
+ final FragmentManager fm1 = fc1.getSupportFragmentManager();
+
+ StrictViewFragment fragment1 = new StrictViewFragment();
+ fragment1.setLayoutId(R.layout.scene1);
+ fm1.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ StrictViewFragment fragment2 = new StrictViewFragment();
+
+ fm1.beginTransaction()
+ .setCustomAnimations(0, 0, 0, R.anim.long_fade_out)
+ .replace(R.id.fragmentContainer, fragment2, "2")
+ .addToBackStack(null)
+ .commit();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ fm1.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fm1.popBackStack();
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ fm1.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ // Now fragment2 should be animating away
+ assertFalse(fragment2.isAdded());
+ assertEquals(fragment2, fm1.findFragmentByTag("2")); // still exists because it is animating
+
+ Pair<Parcelable, FragmentManagerNonConfig> state =
+ FragmentTestUtil.destroy(fc1);
+
+ final FragmentController fc2 = FragmentTestUtil.createController(mActivityRule);
+ FragmentTestUtil.resume(fc2, state);
+
+ final FragmentManager fm2 = fc2.getSupportFragmentManager();
+ Fragment fragment2restored = fm2.findFragmentByTag("2");
+ assertNull(fragment2restored);
+
+ Fragment fragment1restored = fm2.findFragmentByTag("1");
+ assertNotNull(fragment1restored);
+ assertNotNull(fragment1restored.getView());
+ }
+
+ private void assertEnterPopExit(AnimatorFragment fragment) throws Throwable {
+ assertFragmentAnimation(fragment, 1, true, ENTER);
+
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ fm.popBackStack();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertFragmentAnimation(fragment, 2, false, POP_EXIT);
+ }
+
+ private void assertExitPopEnter(AnimatorFragment fragment) throws Throwable {
+ assertFragmentAnimation(fragment, 1, false, EXIT);
+
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ fm.popBackStack();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ AnimatorFragment replacement = (AnimatorFragment) fm.findFragmentByTag("1");
+
+ boolean isSameFragment = replacement == fragment;
+ int expectedAnimators = isSameFragment ? 2 : 1;
+ assertFragmentAnimation(replacement, expectedAnimators, true, POP_ENTER);
+ }
+
+ private void assertExitPostponedPopEnter(AnimatorFragment fragment) throws Throwable {
+ assertFragmentAnimation(fragment, 1, false, EXIT);
+
+ fragment.postponeEnterTransition();
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponed(fragment, 1);
+
+ fragment.startPostponedEnterTransition();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ assertFragmentAnimation(fragment, 2, true, POP_ENTER);
+ }
+
+ private void assertFragmentAnimation(AnimatorFragment fragment, int numAnimators,
+ boolean isEnter, int animatorResourceId) throws InterruptedException {
+ assertEquals(numAnimators, fragment.numAnimators);
+ assertEquals(isEnter, fragment.enter);
+ assertEquals(animatorResourceId, fragment.resourceId);
+ assertNotNull(fragment.animation);
+ assertTrue(FragmentTestUtil.waitForAnimationEnd(1000, fragment.animation));
+ assertTrue(fragment.animation.hasStarted());
+ }
+
+ private void assertPostponed(AnimatorFragment fragment, int expectedAnimators)
+ throws InterruptedException {
+ assertTrue(fragment.mOnCreateViewCalled);
+ assertEquals(View.INVISIBLE, fragment.getView().getVisibility());
+ assertEquals(expectedAnimators, fragment.numAnimators);
+ }
+
+ public static class AnimatorFragment extends StrictViewFragment {
+ public int numAnimators;
+ public Animation animation;
+ public boolean enter;
+ public int resourceId;
+
+ @Override
+ public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
+ if (nextAnim == 0) {
+ return null;
+ }
+ this.numAnimators++;
+ this.animation = new TranslateAnimation(-10, 0, 0, 0);
+ this.animation.setDuration(1);
+ this.resourceId = nextAnim;
+ this.enter = enter;
+ return this.animation;
+ }
+ }
+}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java b/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java
index 02cca09..94d0445 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java
@@ -17,16 +17,6 @@
package android.support.v4.app;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNotSame;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertSame;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.assertNotEquals;
-
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -38,6 +28,7 @@
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks;
import android.support.v4.app.test.EmptyFragmentTestActivity;
import android.support.v4.view.ViewCompat;
import android.view.LayoutInflater;
@@ -45,7 +36,6 @@
import android.view.ViewGroup;
import android.view.Window;
import android.widget.TextView;
-
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
@@ -54,6 +44,10 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import static junit.framework.Assert.*;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.Mockito.*;
+
@RunWith(AndroidJUnit4.class)
@MediumTest
public class FragmentLifecycleTest {
@@ -437,6 +431,69 @@
fc.attachHost(null);
fc.dispatchCreate();
+ FragmentLifecycleCallbacks mockLc = mock(FragmentLifecycleCallbacks.class);
+ FragmentLifecycleCallbacks mockRecursiveLc = mock(FragmentLifecycleCallbacks.class);
+
+ FragmentManager fm = fc.getSupportFragmentManager();
+ fm.registerFragmentLifecycleCallbacks(mockLc, false);
+ fm.registerFragmentLifecycleCallbacks(mockRecursiveLc, true);
+
+ ChildFragmentManagerFragment fragment = new ChildFragmentManagerFragment();
+ fm.beginTransaction()
+ .add(android.R.id.content, fragment)
+ .commitNow();
+
+ verify(mockLc, times(1)).onFragmentCreated(fm, fragment, null);
+
+ fc.dispatchActivityCreated();
+
+ Fragment childFragment = fragment.getChildFragment();
+
+ verify(mockLc, times(1)).onFragmentActivityCreated(fm, fragment, null);
+ verify(mockRecursiveLc, times(1)).onFragmentActivityCreated(fm, fragment, null);
+ verify(mockRecursiveLc, times(1)).onFragmentActivityCreated(fm, childFragment, null);
+
+ fc.dispatchStart();
+
+ verify(mockLc, times(1)).onFragmentStarted(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentStarted(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentStarted(fm, childFragment);
+
+ fc.dispatchResume();
+
+ verify(mockLc, times(1)).onFragmentResumed(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentResumed(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentResumed(fm, childFragment);
+
+ // Confirm that the parent fragment received onAttachFragment
+ assertTrue("parent fragment did not receive onAttachFragment",
+ fragment.mCalledOnAttachFragment);
+
+ fc.dispatchStop();
+
+ verify(mockLc, times(1)).onFragmentStopped(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentStopped(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentStopped(fm, childFragment);
+
+ fc.dispatchReallyStop();
+ fc.dispatchDestroy();
+
+ verify(mockLc, times(1)).onFragmentDestroyed(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentDestroyed(fm, fragment);
+ verify(mockRecursiveLc, times(1)).onFragmentDestroyed(fm, childFragment);
+ }
+
+ /**
+ * This test checks that FragmentLifecycleCallbacks are invoked when expected.
+ */
+ @Test
+ @UiThreadTest
+ public void fragmentLifecycleCallbacks() throws Throwable {
+ FragmentController fc = FragmentController.createController(
+ new HostCallbacks(mActivityRule.getActivity()));
+ fc.attachHost(null);
+ fc.dispatchCreate();
+
FragmentManager fm = fc.getSupportFragmentManager();
ChildFragmentManagerFragment fragment = new ChildFragmentManagerFragment();
@@ -458,6 +515,31 @@
fc.dispatchDestroy();
}
+ /**
+ * This tests that fragments call onDestroy when the activity finishes.
+ */
+ @Test
+ @UiThreadTest
+ public void fragmentDestroyedOnFinish() throws Throwable {
+ FragmentController fc = startupFragmentController(null);
+ FragmentManager fm = fc.getSupportFragmentManager();
+
+ StrictViewFragment fragmentA = StrictViewFragment.create(R.layout.fragment_a);
+ StrictViewFragment fragmentB = StrictViewFragment.create(R.layout.fragment_b);
+ fm.beginTransaction()
+ .add(android.R.id.content, fragmentA)
+ .commit();
+ fm.executePendingTransactions();
+ fm.beginTransaction()
+ .replace(android.R.id.content, fragmentB)
+ .addToBackStack(null)
+ .commit();
+ fm.executePendingTransactions();
+ shutdownFragmentController(fc);
+ assertTrue(fragmentB.mCalledOnDestroy);
+ assertTrue(fragmentA.mCalledOnDestroy);
+ }
+
private void assertAnimationsMatch(FragmentManager fm, int enter, int exit, int popEnter,
int popExit) {
FragmentManagerImpl fmImpl = (FragmentManagerImpl) fm;
@@ -546,8 +628,14 @@
}
}
+ /**
+ * This tests a deliberately odd use of a child fragment, added in onCreateView instead
+ * of elsewhere. It simulates creating a UI child fragment added to the view hierarchy
+ * created by this fragment.
+ */
public static class ChildFragmentManagerFragment extends StrictFragment {
private FragmentManager mSavedChildFragmentManager;
+ private ChildFragmentManagerChildFragment mChildFragment;
@Override
public void onAttach(Context context) {
@@ -561,13 +649,24 @@
@Nullable Bundle savedInstanceState) {
assertSame("child FragmentManagers not the same instance", mSavedChildFragmentManager,
getChildFragmentManager());
- ChildFragmentManagerChildFragment child = new ChildFragmentManagerChildFragment("foo");
- mSavedChildFragmentManager.beginTransaction()
- .add(child, "tag")
- .commitNow();
- assertEquals("argument strings don't match", "foo", child.getString());
+ ChildFragmentManagerChildFragment child =
+ (ChildFragmentManagerChildFragment) mSavedChildFragmentManager
+ .findFragmentByTag("tag");
+ if (child == null) {
+ child = new ChildFragmentManagerChildFragment("foo");
+ mSavedChildFragmentManager.beginTransaction()
+ .add(child, "tag")
+ .commitNow();
+ assertEquals("argument strings don't match", "foo", child.getString());
+ }
+ mChildFragment = child;
return new TextView(container.getContext());
}
+
+ @Nullable
+ public Fragment getChildFragment() {
+ return mChildFragment;
+ }
}
public static class ChildFragmentManagerChildFragment extends StrictFragment {
diff --git a/fragment/tests/java/android/support/v4/app/FragmentOptimizationTest.java b/fragment/tests/java/android/support/v4/app/FragmentOptimizationTest.java
new file mode 100644
index 0000000..39dbf11
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/FragmentOptimizationTest.java
@@ -0,0 +1,547 @@
+/*
+ * 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.v4.app;
+
+import static org.junit.Assert.*;
+
+import android.app.Instrumentation;
+import android.support.fragment.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class FragmentOptimizationTest {
+ @Rule
+ public ActivityTestRule<FragmentTestActivity> mActivityRule =
+ new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+ private ViewGroup mContainer;
+ private FragmentManager mFM;
+ private Instrumentation mInstrumentation;
+
+ @Before
+ public void setup() {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ mContainer = (ViewGroup) mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ mFM = mActivityRule.getActivity().getSupportFragmentManager();
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ }
+
+ // Test that when you add and replace a fragment that only the replace's add
+ // actually creates a View.
+ @Test
+ public void addReplace() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ mFM.executePendingTransactions();
+ }
+ });
+ assertEquals(0, fragment1.onCreateViewCount);
+ FragmentTestUtil.assertChildren(mContainer, fragment2);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.popBackStack();
+ mFM.popBackStack();
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer);
+ }
+
+ // Test that it is possible to merge a transaction that starts with pop and adds
+ // the same view back again.
+ @Test
+ public void startWithPop() throws Throwable {
+ // Start with a single fragment on the back stack
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ mFM.beginTransaction().add(R.id.fragmentContainer, fragment1).addToBackStack(null).commit();
+
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ // Now pop and add
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.popBackStack();
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ assertEquals(1, fragment1.onCreateViewCount);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+ FragmentTestUtil.assertChildren(mContainer);
+ assertEquals(1, fragment1.onCreateViewCount);
+ }
+
+ // Popping the back stack in the middle of other operations doesn't fool it.
+ @Test
+ public void middlePop() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ final CountCallsFragment fragment2 = new CountCallsFragment();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.popBackStack();
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer, fragment2);
+ assertEquals(0, fragment1.onAttachCount);
+ assertEquals(1, fragment2.onCreateViewCount);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+ FragmentTestUtil.assertChildren(mContainer);
+ assertEquals(1, fragment2.onDetachCount);
+ }
+
+ // ensure that removing a view after adding it is optimized into no
+ // View being created. Hide still gets notified.
+ @Test
+ public void optimizeRemove() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ final int[] id = new int[1];
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ id[0] = mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().remove(fragment1).addToBackStack(null).commit();
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer);
+ assertEquals(0, fragment1.onCreateViewCount);
+ assertEquals(1, fragment1.onHideCount);
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(0, fragment1.onDetachCount);
+ assertEquals(0, fragment1.onAttachCount);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule, id[0],
+ FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ FragmentTestUtil.assertChildren(mContainer);
+ assertEquals(0, fragment1.onCreateViewCount);
+ assertEquals(1, fragment1.onHideCount);
+ assertEquals(1, fragment1.onShowCount);
+ assertEquals(0, fragment1.onDetachCount);
+ assertEquals(0, fragment1.onAttachCount);
+ }
+
+ // Ensure that removing and adding the same view results in no operation
+ @Test
+ public void optimizeAdd() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ int id = mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertEquals(1, fragment1.onCreateViewCount);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .remove(fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.executePendingTransactions();
+ }
+ });
+
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ // should be optimized out
+ assertEquals(1, fragment1.onCreateViewCount);
+
+ mFM.popBackStack(id, 0);
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ // optimize out going back, too
+ assertEquals(1, fragment1.onCreateViewCount);
+ }
+
+ // detaching, then attaching results in on change. Hide still functions
+ @Test
+ public void optimizeAttach() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ int id = mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertEquals(1, fragment1.onAttachCount);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
+ mFM.executePendingTransactions();
+ }
+ });
+
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ // can optimize out the detach/attach
+ assertEquals(0, fragment1.onDestroyViewCount);
+ assertEquals(1, fragment1.onHideCount);
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onCreateViewCount);
+ assertEquals(1, fragment1.onAttachCount);
+ assertEquals(0, fragment1.onDetachCount);
+
+ mFM.popBackStack(id, 0);
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ // optimized out again, but not the show
+ assertEquals(0, fragment1.onDestroyViewCount);
+ assertEquals(1, fragment1.onHideCount);
+ assertEquals(1, fragment1.onShowCount);
+ assertEquals(1, fragment1.onCreateViewCount);
+ assertEquals(1, fragment1.onAttachCount);
+ assertEquals(0, fragment1.onDetachCount);
+ }
+
+ // attaching, then detaching shouldn't result in a View being created
+ @Test
+ public void optimizeDetach() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ int id = mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .detach(fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ // the add detach is not fully optimized out
+ assertEquals(1, fragment1.onAttachCount);
+ assertEquals(0, fragment1.onDetachCount);
+ assertTrue(fragment1.isDetached());
+ assertEquals(0, fragment1.onCreateViewCount);
+ FragmentTestUtil.assertChildren(mContainer);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
+ mFM.executePendingTransactions();
+ }
+ });
+
+ FragmentTestUtil.assertChildren(mContainer);
+ // can optimize out the attach/detach, and the hide call
+ assertEquals(1, fragment1.onAttachCount);
+ assertEquals(0, fragment1.onDetachCount);
+ assertEquals(1, fragment1.onHideCount);
+ assertTrue(fragment1.isHidden());
+ assertEquals(0, fragment1.onShowCount);
+
+ mFM.popBackStack(id, 0);
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(mContainer);
+
+ // we can optimize out the attach/detach on the way back
+ assertEquals(1, fragment1.onAttachCount);
+ assertEquals(0, fragment1.onDetachCount);
+ assertEquals(1, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+ assertFalse(fragment1.isHidden());
+ }
+
+ // show, then hide should optimize out
+ @Test
+ public void optimizeHide() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ int id = mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .hide(fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .show(fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .remove(fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .hide(fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.executePendingTransactions();
+ }
+ });
+
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ // optimize out hide/show
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ // still optimized out
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction().show(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+ mFM.executePendingTransactions();
+ }
+ });
+
+ // The show/hide can be optimized out and nothing should change.
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction().show(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+ mFM.executePendingTransactions();
+ }
+ });
+
+ // the detach/attach should not affect the show/hide, so show/hide should cancel each other
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(1, fragment1.onHideCount);
+ }
+
+ // hiding and showing the same view should optimize out
+ @Test
+ public void optimizeShow() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ int id = mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(0, fragment1.onHideCount);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
+ mFM.beginTransaction().show(fragment1).addToBackStack(null).commit();
+ mFM.executePendingTransactions();
+ }
+ });
+
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ // can optimize out the show/hide
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(0, fragment1.onHideCount);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule, id,
+ FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ assertEquals(0, fragment1.onShowCount);
+ assertEquals(0, fragment1.onHideCount);
+ }
+
+ // The View order shouldn't be messed up by optimization -- a view that
+ // is optimized to not remove/add should be in its correct position after
+ // the transaction completes.
+ @Test
+ public void viewOrder() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ int id = mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+ final CountCallsFragment fragment2 = new CountCallsFragment();
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer, fragment2, fragment1);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+ FragmentTestUtil.assertChildren(mContainer, fragment1);
+ }
+
+ // Popping an added transaction results in no operation
+ @Test
+ public void addPopBackStack() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.popBackStack();
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer);
+
+ // Was never instantiated because it was popped before anything could happen
+ assertEquals(0, fragment1.onCreateViewCount);
+ }
+
+ // A non-back-stack transaction doesn't interfere with back stack add/pop
+ // optimization.
+ @Test
+ public void popNonBackStack() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ final CountCallsFragment fragment2 = new CountCallsFragment();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment2)
+ .commit();
+ mFM.popBackStack();
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer, fragment2);
+
+ // It should be optimized with the replace, so no View creation
+ assertEquals(0, fragment1.onCreateViewCount);
+ }
+
+ // When optimization is disabled, the transaction prior to the disabled optimization
+ // transaction should all be run prior to running the non-optimized transaction.
+ @Test
+ public void noOptimization() throws Throwable {
+ final CountCallsFragment fragment1 = new CountCallsFragment();
+ final CountCallsFragment fragment2 = new CountCallsFragment();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFM.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ mFM.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .setAllowOptimization(false)
+ .commit();
+ mFM.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(mContainer, fragment2);
+
+ // No optimization, so fragment1 should have created its View
+ assertEquals(1, fragment1.onCreateViewCount);
+ }
+
+}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentReceiveResultTest.java b/fragment/tests/java/android/support/v4/app/FragmentReceiveResultTest.java
index f7c8a81..6a8ac05 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentReceiveResultTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentReceiveResultTest.java
@@ -36,6 +36,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* Tests for Fragment startActivityForResult and startIntentSenderForResult.
*/
@@ -127,6 +130,7 @@
mFragment.startActivityForResult(intent, requestCode);
}
});
+ assertTrue(mFragment.mResultReceiveLatch.await(1, TimeUnit.SECONDS));
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -150,6 +154,7 @@
}
}
});
+ assertTrue(mFragment.mResultReceiveLatch.await(1, TimeUnit.SECONDS));
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -158,6 +163,7 @@
int mRequestCode = -1;
int mResultCode = 100;
String mResultContent;
+ final CountDownLatch mResultReceiveLatch = new CountDownLatch(1);
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
@@ -165,6 +171,7 @@
mRequestCode = requestCode;
mResultCode = resultCode;
mResultContent = data.getStringExtra(FragmentResultActivity.EXTRA_RESULT_CONTENT);
+ mResultReceiveLatch.countDown();
}
}
}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentTestUtil.java b/fragment/tests/java/android/support/v4/app/FragmentTestUtil.java
new file mode 100644
index 0000000..ba5875a
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/FragmentTestUtil.java
@@ -0,0 +1,197 @@
+/*
+ * 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.v4.app;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.Instrumentation;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.util.Pair;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+
+public class FragmentTestUtil {
+ private static final Runnable DO_NOTHING = new Runnable() {
+ @Override
+ public void run() {
+ }
+ };
+
+ public static void waitForExecution(final ActivityTestRule<FragmentTestActivity> rule) {
+ // Wait for two cycles. When starting a postponed transition, it will post to
+ // the UI thread and then the execution will be added onto the queue after that.
+ // The two-cycle wait makes sure fragments have the opportunity to complete both
+ // before returning.
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ instrumentation.runOnMainSync(DO_NOTHING);
+ instrumentation.runOnMainSync(DO_NOTHING);
+ }
+
+ public static boolean executePendingTransactions(
+ final ActivityTestRule<FragmentTestActivity> rule) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ final boolean[] ret = new boolean[1];
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ ret[0] =
+ rule.getActivity().getSupportFragmentManager().executePendingTransactions();
+ }
+ });
+ return ret[0];
+ }
+
+ public static boolean popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ final boolean[] ret = new boolean[1];
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ ret[0] = rule.getActivity().getSupportFragmentManager().popBackStackImmediate();
+ }
+ });
+ return ret[0];
+ }
+
+ public static boolean popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule,
+ final int id, final int flags) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ final boolean[] ret = new boolean[1];
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ ret[0] = rule.getActivity().getSupportFragmentManager().popBackStackImmediate(id,
+ flags);
+ }
+ });
+ return ret[0];
+ }
+
+ public static boolean popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule,
+ final String name, final int flags) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ final boolean[] ret = new boolean[1];
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ ret[0] = rule.getActivity().getSupportFragmentManager().popBackStackImmediate(name,
+ flags);
+ }
+ });
+ return ret[0];
+ }
+
+ public static void setContentView(final ActivityTestRule<FragmentTestActivity> rule,
+ final int layoutId) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ rule.getActivity().setContentView(layoutId);
+ }
+ });
+ }
+
+ public static void assertChildren(ViewGroup container, Fragment... fragments) {
+ final int numFragments = fragments == null ? 0 : fragments.length;
+ assertEquals("There aren't the correct number of fragment Views in its container",
+ numFragments, container.getChildCount());
+ for (int i = 0; i < numFragments; i++) {
+ assertEquals("Wrong Fragment View order for [" + i + "]", container.getChildAt(i),
+ fragments[i].getView());
+ }
+ }
+
+ public static FragmentController createController(ActivityTestRule<FragmentTestActivity> rule) {
+ final FragmentController[] controller = new FragmentController[1];
+ final FragmentTestActivity activity = rule.getActivity();
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ Handler handler = new Handler();
+ HostCallbacks hostCallbacks = new HostCallbacks(activity, handler, 0);
+ controller[0] = FragmentController.createController(hostCallbacks);
+ }
+ });
+ return controller[0];
+ }
+
+ public static void resume(final FragmentController fragmentController,
+ final Pair<Parcelable, FragmentManagerNonConfig> savedState) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ fragmentController.attachHost(null);
+ if (savedState != null) {
+ fragmentController.restoreAllState(savedState.first, savedState.second);
+ }
+ fragmentController.dispatchCreate();
+ fragmentController.dispatchActivityCreated();
+ fragmentController.noteStateNotSaved();
+ fragmentController.execPendingActions();
+ fragmentController.dispatchStart();
+ fragmentController.reportLoaderStart();
+ fragmentController.dispatchResume();
+ fragmentController.execPendingActions();
+ }
+ });
+ }
+
+ public static Pair<Parcelable, FragmentManagerNonConfig> destroy(
+ final FragmentController fragmentController) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ final Pair<Parcelable, FragmentManagerNonConfig>[] result = new Pair[1];
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ fragmentController.dispatchPause();
+ final Parcelable savedState = fragmentController.saveAllState();
+ final FragmentManagerNonConfig nonConfig =
+ fragmentController.retainNestedNonConfig();
+ fragmentController.dispatchStop();
+ fragmentController.doLoaderStop(false);
+ fragmentController.dispatchDestroy();
+ fragmentController.doLoaderDestroy();
+ result[0] = Pair.create(savedState, nonConfig);
+ }
+ });
+ return result[0];
+ }
+
+ public static boolean waitForAnimationEnd(long timeout, final Animation animation) {
+ long endTime = SystemClock.uptimeMillis() + timeout;
+ final boolean[] hasEnded = new boolean[1];
+ Runnable check = new Runnable() {
+ @Override
+ public void run() {
+ hasEnded[0] = animation.hasEnded();
+ }
+ };
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ do {
+ SystemClock.sleep(10);
+ instrumentation.runOnMainSync(check);
+ } while (!hasEnded[0] && SystemClock.uptimeMillis() < endTime);
+ return hasEnded[0];
+ }
+}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java b/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java
index 78a9acf..bc8e9e1 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -15,382 +15,879 @@
*/
package android.support.v4.app;
+import static junit.framework.Assert.assertNull;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
import android.app.Instrumentation;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
+import android.graphics.Rect;
+import android.os.Build;
import android.support.fragment.test.R;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
import android.support.v4.app.test.FragmentTestActivity;
-import android.support.v4.app.test.FragmentTestActivity.TestFragment;
-import android.support.v4.view.ViewCompat;
+import android.transition.TransitionSet;
import android.view.View;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.ArgumentCaptor;
-@RunWith(AndroidJUnit4.class)
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
@MediumTest
+@RunWith(Parameterized.class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
public class FragmentTransitionTest {
+ private final boolean mOptimize;
+
+ @Parameterized.Parameters
+ public static Object[] data() {
+ return new Boolean[] {
+ false, true
+ };
+ }
+
@Rule
public ActivityTestRule<FragmentTestActivity> mActivityRule =
new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
- private TestFragment mStartFragment;
- private TestFragment mMidFragment;
- private TestFragment mEndFragment;
- private FragmentTestActivity mActivity;
private Instrumentation mInstrumentation;
+ private FragmentManager mFragmentManager;
+
+ public FragmentTransitionTest(final boolean optimize) {
+ mOptimize = optimize;
+ }
@Before
- public void setup() {
- mActivity = mActivityRule.getActivity();
+ public void setup() throws Throwable {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mFragmentManager = mActivityRule.getActivity().getSupportFragmentManager();
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
}
+ // Test that normal view transitions (enter, exit, reenter, return) run with
+ // a single fragment.
@Test
- public void testFragmentTransition() throws Throwable {
- if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
- return;
- }
- launchStartFragment();
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- final View sharedElement = mActivity.findViewById(R.id.hello);
- assertEquals("source", ViewCompat.getTransitionName(sharedElement));
+ public void enterExitTransitions() throws Throwable {
+ // enter transition
+ TransitionFragment fragment = setupInitialFragment();
+ final View blue = findBlue();
+ final View green = findBlue();
- mEndFragment = TestFragment.create(R.layout.fragment_end);
- mActivity.getSupportFragmentManager().beginTransaction()
- .replace(R.id.content, mEndFragment)
- .addSharedElement(sharedElement, "destination")
- .addToBackStack(null)
- .commit();
- mActivity.getSupportFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mEndFragment, TestFragment.ENTER);
- assertTrue(mEndFragment.wasEndCalled(TestFragment.ENTER));
- assertTrue(mStartFragment.wasEndCalled(TestFragment.EXIT));
- assertTrue(mEndFragment.wasEndCalled(TestFragment.SHARED_ELEMENT_ENTER));
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- final View textView = mActivity.findViewById(R.id.hello);
- assertEquals("destination", ViewCompat.getTransitionName(textView));
- mActivity.getSupportFragmentManager().popBackStack();
- mActivity.getSupportFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mStartFragment, TestFragment.REENTER);
- assertTrue(mStartFragment.wasEndCalled(TestFragment.REENTER));
- assertTrue(mEndFragment.wasEndCalled(TestFragment.RETURN));
+ // exit transition
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .remove(fragment)
+ .addToBackStack(null)
+ .commit();
+
+ fragment.waitForTransition();
+ verifyAndClearTransition(fragment.exitTransition, null, green, blue);
+ verifyNoOtherTransitions(fragment);
+
+ // reenter transition
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+ fragment.waitForTransition();
+ final View green2 = findGreen();
+ final View blue2 = findBlue();
+ verifyAndClearTransition(fragment.reenterTransition, null, green2, blue2);
+ verifyNoOtherTransitions(fragment);
+
+ // return transition
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+ fragment.waitForTransition();
+ verifyAndClearTransition(fragment.returnTransition, null, green2, blue2);
+ verifyNoOtherTransitions(fragment);
}
+ // Test that shared elements transition from one fragment to the next
+ // and back during pop.
@Test
- public void testFirstOutLastInTransition() throws Throwable {
- if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
- return;
- }
- launchStartFragment();
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mMidFragment = TestFragment.create(R.layout.fragment_middle);
- mEndFragment = TestFragment.create(R.layout.fragment_end);
- mActivity.getSupportFragmentManager().beginTransaction()
- .replace(R.id.content, mMidFragment)
- .replace(R.id.content, mEndFragment)
- .addToBackStack(null)
- .commit();
- mActivity.getSupportFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mEndFragment, TestFragment.ENTER);
- assertTrue(mEndFragment.wasEndCalled(TestFragment.ENTER));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.EXIT));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.REENTER));
+ public void sharedElement() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
- assertTrue(mStartFragment.wasEndCalled(TestFragment.EXIT));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.ENTER));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.REENTER));
+ // Now do a transition to scene2
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
- assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
+ verifyTransition(fragment1, fragment2, "blueSquare");
- mStartFragment.clearNotifications();
- mEndFragment.clearNotifications();
-
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mActivity.getSupportFragmentManager().popBackStack();
- mActivity.getSupportFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mEndFragment, TestFragment.RETURN);
- assertTrue(mEndFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
-
- assertTrue(mStartFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.RETURN));
+ // Now pop the back stack
+ verifyPopTransition(1, fragment2, fragment1);
}
+ // Test that shared element transitions through multiple fragments work together
@Test
- public void testPopTwo() throws Throwable {
- if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
- return;
- }
- launchStartFragment();
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mMidFragment = TestFragment.create(R.layout.fragment_middle);
- mActivity.getSupportFragmentManager().beginTransaction()
- .replace(R.id.content, mMidFragment)
- .addToBackStack(null)
- .commit();
- mActivity.getSupportFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mMidFragment, TestFragment.ENTER);
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mEndFragment = TestFragment.create(R.layout.fragment_end);
- mActivity.getSupportFragmentManager().beginTransaction()
- .replace(R.id.content, mEndFragment)
- .addToBackStack(null)
- .commit();
- mActivity.getSupportFragmentManager().executePendingTransactions();
- }
- });
- waitForEnd(mEndFragment, TestFragment.ENTER);
- assertTrue(mEndFragment.wasEndCalled(TestFragment.ENTER));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.EXIT));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.REENTER));
+ public void intermediateFragment() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
- assertTrue(mStartFragment.wasEndCalled(TestFragment.EXIT));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.ENTER));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.REENTER));
+ final TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene3);
- assertTrue(mMidFragment.wasStartCalled(TestFragment.ENTER));
- assertTrue(mMidFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
+ verifyTransition(fragment1, fragment2, "shared");
- mStartFragment.clearNotifications();
- mMidFragment.clearNotifications();
- mEndFragment.clearNotifications();
+ final TransitionFragment fragment3 = new TransitionFragment();
+ fragment3.setLayoutId(R.layout.scene2);
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- FragmentManager fm = mActivity.getSupportFragmentManager();
- int id = fm.getBackStackEntryAt(0).getId();
- fm.popBackStack(id, FragmentManager.POP_BACK_STACK_INCLUSIVE);
- fm.executePendingTransactions();
- }
- });
- waitForEnd(mEndFragment, TestFragment.RETURN);
- assertTrue(mEndFragment.wasEndCalled(TestFragment.RETURN));
+ verifyTransition(fragment2, fragment3, "blueSquare");
- assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
-
- assertTrue(mStartFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.RETURN));
+ // Should transfer backwards when popping multiple:
+ verifyPopTransition(2, fragment3, fragment1, fragment2);
}
+ // Adding/removing the same fragment multiple times shouldn't mess anything up
@Test
- public void testNullTransition() throws Throwable {
- if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
- return;
- }
- mInstrumentation.waitForIdleSync();
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mStartFragment = TestFragment.create(R.layout.fragment_start);
- mStartFragment.clearTransitions();
- mActivity.getSupportFragmentManager().beginTransaction()
- .replace(R.id.content, mStartFragment)
- .commit();
- mActivity.getSupportFragmentManager().executePendingTransactions();
- }
- });
- waitForStart(mStartFragment, TestFragment.ENTER);
- // No transitions
- assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
+ public void removeAdded() throws Throwable {
+ final TransitionFragment fragment1 = setupInitialFragment();
- mActivityRule.runOnUiThread(new Runnable() {
+ final View startBlue = findBlue();
+ final View startGreen = findGreen();
+
+ final TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
@Override
public void run() {
- mMidFragment = TestFragment.create(R.layout.fragment_middle);
- mEndFragment = TestFragment.create(R.layout.fragment_end);
- mEndFragment.clearTransitions();
- mActivity.getSupportFragmentManager().beginTransaction()
- .replace(R.id.content, mMidFragment)
- .replace(R.id.content, mEndFragment)
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .replace(R.id.fragmentContainer, fragment2)
+ .replace(R.id.fragmentContainer, fragment1)
+ .replace(R.id.fragmentContainer, fragment2)
.addToBackStack(null)
.commit();
- mActivity.getSupportFragmentManager().executePendingTransactions();
}
});
- waitForStart(mEndFragment, TestFragment.ENTER);
- assertFalse(mEndFragment.wasEndCalled(TestFragment.ENTER));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.EXIT));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mEndFragment.wasEndCalled(TestFragment.REENTER));
+ FragmentTestUtil.waitForExecution(mActivityRule);
- assertFalse(mStartFragment.wasEndCalled(TestFragment.EXIT));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.ENTER));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mStartFragment.wasEndCalled(TestFragment.REENTER));
+ // should be a normal transition from fragment1 to fragment2
+ fragment2.waitForTransition();
+ final View endBlue = findBlue();
+ final View endGreen = findGreen();
+ verifyAndClearTransition(fragment1.exitTransition, null, startBlue, startGreen);
+ verifyAndClearTransition(fragment2.enterTransition, null, endBlue, endGreen);
+ verifyNoOtherTransitions(fragment1);
+ verifyNoOtherTransitions(fragment2);
- assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
+ // Pop should also do the same thing
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mActivity.getSupportFragmentManager().popBackStack();
- mActivity.getSupportFragmentManager().executePendingTransactions();
- }
- });
- waitForStart(mEndFragment, TestFragment.RETURN);
- assertFalse(mEndFragment.wasEndCalled(TestFragment.RETURN));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
-
- assertFalse(mStartFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.RETURN));
+ fragment1.waitForTransition();
+ final View popBlue = findBlue();
+ final View popGreen = findGreen();
+ verifyAndClearTransition(fragment1.reenterTransition, null, popBlue, popGreen);
+ verifyAndClearTransition(fragment2.returnTransition, null, endBlue, endGreen);
+ verifyNoOtherTransitions(fragment1);
+ verifyNoOtherTransitions(fragment2);
}
+ // Make sure that shared elements on two different fragment containers don't interact
@Test
- public void testRemoveAdded() throws Throwable {
- if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
- return;
- }
- launchStartFragment();
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mEndFragment = TestFragment.create(R.layout.fragment_end);
- mActivity.getSupportFragmentManager().beginTransaction()
- .replace(R.id.content, mEndFragment)
- .replace(R.id.content, mStartFragment)
- .replace(R.id.content, mEndFragment)
- .addToBackStack(null)
- .commit();
- mActivity.getSupportFragmentManager().executePendingTransactions();
- }
- });
- assertTrue(waitForEnd(mEndFragment, TestFragment.ENTER));
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mActivity.getSupportFragmentManager().popBackStack();
- mActivity.getSupportFragmentManager().executePendingTransactions();
- }
- });
- assertTrue(waitForEnd(mStartFragment, TestFragment.REENTER));
+ public void crossContainer() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+ TransitionFragment fragment1 = new TransitionFragment();
+ fragment1.setLayoutId(R.layout.scene1);
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene1);
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .add(R.id.fragmentContainer1, fragment1)
+ .add(R.id.fragmentContainer2, fragment2)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fragment1.waitForTransition();
+ final View greenSquare1 = findViewById(fragment1, R.id.greenSquare);
+ final View blueSquare1 = findViewById(fragment1, R.id.blueSquare);
+ verifyAndClearTransition(fragment1.enterTransition, null, greenSquare1, blueSquare1);
+ verifyNoOtherTransitions(fragment1);
+ fragment2.waitForTransition();
+ final View greenSquare2 = findViewById(fragment2, R.id.greenSquare);
+ final View blueSquare2 = findViewById(fragment2, R.id.blueSquare);
+ verifyAndClearTransition(fragment2.enterTransition, null, greenSquare2, blueSquare2);
+ verifyNoOtherTransitions(fragment2);
+
+ // Make sure the correct transitions are run when the target names
+ // are different in both shared elements. We may fool the system.
+ verifyCrossTransition(false, fragment1, fragment2);
+
+ // Make sure the correct transitions are run when the source names
+ // are different in both shared elements. We may fool the system.
+ verifyCrossTransition(true, fragment1, fragment2);
}
+ // Make sure that onSharedElementStart and onSharedElementEnd are called
@Test
- public void testAddRemoved() throws Throwable {
- if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
- return;
+ public void callStartEndWithSharedElements() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+
+ // Now do a transition to scene2
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ SharedElementCallback enterCallback = mock(SharedElementCallback.class);
+ fragment2.setEnterSharedElementCallback(enterCallback);
+
+ final View startBlue = findBlue();
+
+ verifyTransition(fragment1, fragment2, "blueSquare");
+
+ ArgumentCaptor<List> names = ArgumentCaptor.forClass(List.class);
+ ArgumentCaptor<List> views = ArgumentCaptor.forClass(List.class);
+ ArgumentCaptor<List> snapshots = ArgumentCaptor.forClass(List.class);
+ verify(enterCallback).onSharedElementStart(names.capture(), views.capture(),
+ snapshots.capture());
+ assertEquals(1, names.getValue().size());
+ assertEquals(1, views.getValue().size());
+ assertNull(snapshots.getValue());
+ assertEquals("blueSquare", names.getValue().get(0));
+ assertEquals(startBlue, views.getValue().get(0));
+
+ final View endBlue = findBlue();
+
+ verify(enterCallback).onSharedElementEnd(names.capture(), views.capture(),
+ snapshots.capture());
+ assertEquals(1, names.getValue().size());
+ assertEquals(1, views.getValue().size());
+ assertNull(snapshots.getValue());
+ assertEquals("blueSquare", names.getValue().get(0));
+ assertEquals(endBlue, views.getValue().get(0));
+
+ // Now pop the back stack
+ reset(enterCallback);
+ verifyPopTransition(1, fragment2, fragment1);
+
+ verify(enterCallback).onSharedElementStart(names.capture(), views.capture(),
+ snapshots.capture());
+ assertEquals(1, names.getValue().size());
+ assertEquals(1, views.getValue().size());
+ assertNull(snapshots.getValue());
+ assertEquals("blueSquare", names.getValue().get(0));
+ assertEquals(endBlue, views.getValue().get(0));
+
+ final View reenterBlue = findBlue();
+
+ verify(enterCallback).onSharedElementEnd(names.capture(), views.capture(),
+ snapshots.capture());
+ assertEquals(1, names.getValue().size());
+ assertEquals(1, views.getValue().size());
+ assertNull(snapshots.getValue());
+ assertEquals("blueSquare", names.getValue().get(0));
+ assertEquals(reenterBlue, views.getValue().get(0));
+ }
+
+ // Make sure that onMapSharedElement works to change the shared element going out
+ @Test
+ public void onMapSharedElementOut() throws Throwable {
+ final TransitionFragment fragment1 = setupInitialFragment();
+
+ // Now do a transition to scene2
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ final View startBlue = findBlue();
+ final View startGreen = findGreen();
+
+ final Rect startGreenBounds = getBoundsOnScreen(startGreen);
+
+ SharedElementCallback mapOut = new SharedElementCallback() {
+ @Override
+ public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
+ assertEquals(1, names.size());
+ assertEquals("blueSquare", names.get(0));
+ assertEquals(1, sharedElements.size());
+ assertEquals(startBlue, sharedElements.get("blueSquare"));
+ sharedElements.put("blueSquare", startGreen);
+ }
+ };
+ fragment1.setExitSharedElementCallback(mapOut);
+
+ mFragmentManager.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment2)
+ .setAllowOptimization(mOptimize)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ final View endBlue = findBlue();
+ final Rect endBlueBounds = getBoundsOnScreen(endBlue);
+
+ verifyAndClearTransition(fragment2.sharedElementEnter, startGreenBounds, startGreen,
+ endBlue);
+
+ SharedElementCallback mapBack = new SharedElementCallback() {
+ @Override
+ public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
+ assertEquals(1, names.size());
+ assertEquals("blueSquare", names.get(0));
+ assertEquals(1, sharedElements.size());
+ final View expectedBlue = findViewById(fragment1, R.id.blueSquare);
+ assertEquals(expectedBlue, sharedElements.get("blueSquare"));
+ final View greenSquare = findViewById(fragment1, R.id.greenSquare);
+ sharedElements.put("blueSquare", greenSquare);
+ }
+ };
+ fragment1.setExitSharedElementCallback(mapBack);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ final View reenterGreen = findGreen();
+ verifyAndClearTransition(fragment2.sharedElementReturn, endBlueBounds, endBlue,
+ reenterGreen);
+ }
+
+ // Make sure that onMapSharedElement works to change the shared element target
+ @Test
+ public void onMapSharedElementIn() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+
+ // Now do a transition to scene2
+ final TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ final View startBlue = findBlue();
+ final Rect startBlueBounds = getBoundsOnScreen(startBlue);
+
+ SharedElementCallback mapIn = new SharedElementCallback() {
+ @Override
+ public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
+ assertEquals(1, names.size());
+ assertEquals("blueSquare", names.get(0));
+ assertEquals(1, sharedElements.size());
+ final View blueSquare = findViewById(fragment2, R.id.blueSquare);
+ assertEquals(blueSquare, sharedElements.get("blueSquare"));
+ final View greenSquare = findViewById(fragment2, R.id.greenSquare);
+ sharedElements.put("blueSquare", greenSquare);
+ }
+ };
+ fragment2.setEnterSharedElementCallback(mapIn);
+
+ mFragmentManager.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment2)
+ .setAllowOptimization(mOptimize)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ final View endGreen = findGreen();
+ final View endBlue = findBlue();
+ final Rect endGreenBounds = getBoundsOnScreen(endGreen);
+
+ verifyAndClearTransition(fragment2.sharedElementEnter, startBlueBounds, startBlue,
+ endGreen);
+
+ SharedElementCallback mapBack = new SharedElementCallback() {
+ @Override
+ public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
+ assertEquals(1, names.size());
+ assertEquals("blueSquare", names.get(0));
+ assertEquals(1, sharedElements.size());
+ assertEquals(endBlue, sharedElements.get("blueSquare"));
+ sharedElements.put("blueSquare", endGreen);
+ }
+ };
+ fragment2.setEnterSharedElementCallback(mapBack);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ final View reenterBlue = findBlue();
+ verifyAndClearTransition(fragment2.sharedElementReturn, endGreenBounds, endGreen,
+ reenterBlue);
+ }
+
+ // Ensure that shared element transitions that have targets properly target the views
+ @Test
+ public void complexSharedElementTransition() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+
+ // Now do a transition to scene2
+ ComplexTransitionFragment fragment2 = new ComplexTransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ final View startBlue = findBlue();
+ final View startGreen = findGreen();
+ final Rect startBlueBounds = getBoundsOnScreen(startBlue);
+
+ mFragmentManager.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .addSharedElement(startGreen, "greenSquare")
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ final View endBlue = findBlue();
+ final View endGreen = findGreen();
+ final Rect endBlueBounds = getBoundsOnScreen(endBlue);
+
+ verifyAndClearTransition(fragment2.sharedElementEnterTransition1, startBlueBounds,
+ startBlue, endBlue);
+ verifyAndClearTransition(fragment2.sharedElementEnterTransition2, startBlueBounds,
+ startGreen, endGreen);
+
+ // Now see if it works when popped
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ final View reenterBlue = findBlue();
+ final View reenterGreen = findGreen();
+
+ verifyAndClearTransition(fragment2.sharedElementReturnTransition1, endBlueBounds,
+ endBlue, reenterBlue);
+ verifyAndClearTransition(fragment2.sharedElementReturnTransition2, endBlueBounds,
+ endGreen, reenterGreen);
+ }
+
+ // Ensure that after transitions have executed that they don't have any targets or other
+ // unfortunate modifications.
+ @Test
+ public void transitionsEndUnchanged() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+
+ // Now do a transition to scene2
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ verifyTransition(fragment1, fragment2, "blueSquare");
+ assertEquals(0, fragment1.exitTransition.getTargets().size());
+ assertEquals(0, fragment2.sharedElementEnter.getTargets().size());
+ assertEquals(0, fragment2.enterTransition.getTargets().size());
+ assertNull(fragment1.exitTransition.getEpicenterCallback());
+ assertNull(fragment2.enterTransition.getEpicenterCallback());
+ assertNull(fragment2.sharedElementEnter.getEpicenterCallback());
+
+ // Now pop the back stack
+ verifyPopTransition(1, fragment2, fragment1);
+
+ assertEquals(0, fragment2.returnTransition.getTargets().size());
+ assertEquals(0, fragment2.sharedElementReturn.getTargets().size());
+ assertEquals(0, fragment1.reenterTransition.getTargets().size());
+ assertNull(fragment2.returnTransition.getEpicenterCallback());
+ assertNull(fragment2.sharedElementReturn.getEpicenterCallback());
+ assertNull(fragment2.reenterTransition.getEpicenterCallback());
+ }
+
+ // Ensure that transitions are done when a fragment is shown and hidden
+ @Test
+ public void showHideTransition() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ final View startBlue = findBlue();
+ final View startGreen = findGreen();
+
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .add(R.id.fragmentContainer, fragment2)
+ .hide(fragment1)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ final View endGreen = findViewById(fragment2, R.id.greenSquare);
+ final View endBlue = findViewById(fragment2, R.id.blueSquare);
+
+ assertEquals(View.GONE, fragment1.getView().getVisibility());
+ assertEquals(View.VISIBLE, startGreen.getVisibility());
+ assertEquals(View.VISIBLE, startBlue.getVisibility());
+
+ verifyAndClearTransition(fragment1.exitTransition, null, startGreen, startBlue);
+ verifyNoOtherTransitions(fragment1);
+
+ verifyAndClearTransition(fragment2.enterTransition, null, endGreen, endBlue);
+ verifyNoOtherTransitions(fragment2);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+
+ verifyAndClearTransition(fragment1.reenterTransition, null, startGreen, startBlue);
+ verifyNoOtherTransitions(fragment1);
+
+ assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
+ assertEquals(View.VISIBLE, startGreen.getVisibility());
+ assertEquals(View.VISIBLE, startBlue.getVisibility());
+
+ verifyAndClearTransition(fragment2.returnTransition, null, endGreen, endBlue);
+ verifyNoOtherTransitions(fragment2);
+ }
+
+ // Ensure that transitions are done when a fragment is attached and detached
+ @Test
+ public void attachDetachTransition() throws Throwable {
+ TransitionFragment fragment1 = setupInitialFragment();
+ TransitionFragment fragment2 = new TransitionFragment();
+ fragment2.setLayoutId(R.layout.scene2);
+
+ final View startBlue = findBlue();
+ final View startGreen = findGreen();
+
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .add(R.id.fragmentContainer, fragment2)
+ .detach(fragment1)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ final View endGreen = findViewById(fragment2, R.id.greenSquare);
+ final View endBlue = findViewById(fragment2, R.id.blueSquare);
+
+ verifyAndClearTransition(fragment1.exitTransition, null, startGreen, startBlue);
+ verifyNoOtherTransitions(fragment1);
+
+ verifyAndClearTransition(fragment2.enterTransition, null, endGreen, endBlue);
+ verifyNoOtherTransitions(fragment2);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ final View reenterBlue = findBlue();
+ final View reenterGreen = findGreen();
+
+ verifyAndClearTransition(fragment1.reenterTransition, null, reenterGreen, reenterBlue);
+ verifyNoOtherTransitions(fragment1);
+
+ verifyAndClearTransition(fragment2.returnTransition, null, endGreen, endBlue);
+ verifyNoOtherTransitions(fragment2);
+ }
+
+ private TransitionFragment setupInitialFragment() throws Throwable {
+ TransitionFragment fragment1 = new TransitionFragment();
+ fragment1.setLayoutId(R.layout.scene1);
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ fragment1.waitForTransition();
+ final View blueSquare1 = findBlue();
+ final View greenSquare1 = findGreen();
+ verifyAndClearTransition(fragment1.enterTransition, null, blueSquare1, greenSquare1);
+ verifyNoOtherTransitions(fragment1);
+ return fragment1;
+ }
+
+ private View findViewById(Fragment fragment, int id) {
+ return fragment.getView().findViewById(id);
+ }
+
+ private View findGreen() {
+ return mActivityRule.getActivity().findViewById(R.id.greenSquare);
+ }
+
+ private View findBlue() {
+ return mActivityRule.getActivity().findViewById(R.id.blueSquare);
+ }
+
+ private View findRed() {
+ return mActivityRule.getActivity().findViewById(R.id.redSquare);
+ }
+
+ private void verifyAndClearTransition(TargetTracking transition, Rect epicenter,
+ View... expected) {
+ if (epicenter == null) {
+ assertNull(transition.getCapturedEpicenter());
+ } else {
+ assertEquals(epicenter, transition.getCapturedEpicenter());
}
- launchStartFragment();
+ ArrayList<View> targets = transition.getTrackedTargets();
+ StringBuilder sb = new StringBuilder();
+ sb
+ .append("Expected: [")
+ .append(expected.length)
+ .append("] {");
+ boolean isFirst = true;
+ for (View view : expected) {
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ sb.append(", ");
+ }
+ sb.append(view);
+ }
+ sb
+ .append("}, but got: [")
+ .append(targets.size())
+ .append("] {");
+ isFirst = true;
+ for (View view : targets) {
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ sb.append(", ");
+ }
+ sb.append(view);
+ }
+ sb.append("}");
+ String errorMessage = sb.toString();
+
+ assertEquals(errorMessage, expected.length, targets.size());
+ for (View view : expected) {
+ assertTrue(errorMessage, targets.contains(view));
+ }
+ transition.clearTargets();
+ }
+
+ private void verifyNoOtherTransitions(TransitionFragment fragment) {
+ assertEquals(0, fragment.enterTransition.targets.size());
+ assertEquals(0, fragment.exitTransition.targets.size());
+ assertEquals(0, fragment.reenterTransition.targets.size());
+ assertEquals(0, fragment.returnTransition.targets.size());
+ assertEquals(0, fragment.sharedElementEnter.targets.size());
+ assertEquals(0, fragment.sharedElementReturn.targets.size());
+ }
+
+ private void verifyTransition(TransitionFragment from, TransitionFragment to,
+ String sharedElementName) throws Throwable {
+ final View startBlue = findBlue();
+ final View startGreen = findGreen();
+ final View startRed = findRed();
+
+ final Rect startBlueRect = getBoundsOnScreen(startBlue);
+
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .addSharedElement(startBlue, sharedElementName)
+ .replace(R.id.fragmentContainer, to)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ to.waitForTransition();
+ final View endGreen = findGreen();
+ final View endBlue = findBlue();
+ final View endRed = findRed();
+ final Rect endBlueRect = getBoundsOnScreen(endBlue);
+
+ if (startRed != null) {
+ verifyAndClearTransition(from.exitTransition, startBlueRect, startGreen, startRed);
+ } else {
+ verifyAndClearTransition(from.exitTransition, startBlueRect, startGreen);
+ }
+ verifyNoOtherTransitions(from);
+
+ if (endRed != null) {
+ verifyAndClearTransition(to.enterTransition, endBlueRect, endGreen, endRed);
+ } else {
+ verifyAndClearTransition(to.enterTransition, endBlueRect, endGreen);
+ }
+ verifyAndClearTransition(to.sharedElementEnter, startBlueRect, startBlue, endBlue);
+ verifyNoOtherTransitions(to);
+ }
+
+ private void verifyCrossTransition(boolean swapSource,
+ TransitionFragment from1, TransitionFragment from2) throws Throwable {
+
+ final TransitionFragment to1 = new TransitionFragment();
+ to1.setLayoutId(R.layout.scene2);
+ final TransitionFragment to2 = new TransitionFragment();
+ to2.setLayoutId(R.layout.scene2);
+
+ final View fromExit1 = findViewById(from1, R.id.greenSquare);
+ final View fromShared1 = findViewById(from1, R.id.blueSquare);
+ final Rect fromSharedRect1 = getBoundsOnScreen(fromShared1);
+
+ final int fromExitId2 = swapSource ? R.id.blueSquare : R.id.greenSquare;
+ final int fromSharedId2 = swapSource ? R.id.greenSquare : R.id.blueSquare;
+ final View fromExit2 = findViewById(from2, fromExitId2);
+ final View fromShared2 = findViewById(from2, fromSharedId2);
+ final Rect fromSharedRect2 = getBoundsOnScreen(fromShared2);
+
+ final String sharedElementName = swapSource ? "blueSquare" : "greenSquare";
+
mActivityRule.runOnUiThread(new Runnable() {
@Override
public void run() {
- mEndFragment = TestFragment.create(R.layout.fragment_end);
- mActivity.getSupportFragmentManager().beginTransaction()
- .replace(R.id.content, mEndFragment)
- .replace(R.id.content, mStartFragment)
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .addSharedElement(fromShared1, "blueSquare")
+ .replace(R.id.fragmentContainer1, to1)
.addToBackStack(null)
.commit();
- mActivity.getSupportFragmentManager().executePendingTransactions();
- }
- });
- waitForStart(mStartFragment, TestFragment.ENTER);
- assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.EXIT));
- assertFalse(mEndFragment.wasStartCalled(TestFragment.ENTER));
- assertFalse(mEndFragment.wasStartCalled(TestFragment.EXIT));
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mActivity.getSupportFragmentManager().popBackStack();
- mActivity.getSupportFragmentManager().executePendingTransactions();
- }
- });
- waitForStart(mStartFragment, TestFragment.REENTER);
- assertFalse(mStartFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mStartFragment.wasStartCalled(TestFragment.RETURN));
- assertFalse(mEndFragment.wasStartCalled(TestFragment.REENTER));
- assertFalse(mEndFragment.wasStartCalled(TestFragment.RETURN));
- }
-
- private void launchStartFragment() throws Throwable {
- mInstrumentation.waitForIdleSync();
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mStartFragment = TestFragment.create(R.layout.fragment_start);
- mActivity.getSupportFragmentManager().beginTransaction()
- .replace(R.id.content, mStartFragment)
+ mFragmentManager.beginTransaction()
+ .setAllowOptimization(mOptimize)
+ .addSharedElement(fromShared2, sharedElementName)
+ .replace(R.id.fragmentContainer2, to2)
+ .addToBackStack(null)
.commit();
- mActivity.getSupportFragmentManager().executePendingTransactions();
}
});
- assertTrue(waitForEnd(mStartFragment, TestFragment.ENTER));
- mStartFragment.clearNotifications();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ from1.waitForTransition();
+ from2.waitForTransition();
+ to1.waitForTransition();
+ to2.waitForTransition();
+
+ final View toEnter1 = findViewById(to1, R.id.greenSquare);
+ final View toShared1 = findViewById(to1, R.id.blueSquare);
+ final Rect toSharedRect1 = getBoundsOnScreen(toShared1);
+
+ final View toEnter2 = findViewById(to2, fromSharedId2);
+ final View toShared2 = findViewById(to2, fromExitId2);
+ final Rect toSharedRect2 = getBoundsOnScreen(toShared2);
+
+ verifyAndClearTransition(from1.exitTransition, fromSharedRect1, fromExit1);
+ verifyAndClearTransition(from2.exitTransition, fromSharedRect2, fromExit2);
+ verifyNoOtherTransitions(from1);
+ verifyNoOtherTransitions(from2);
+
+ verifyAndClearTransition(to1.enterTransition, toSharedRect1, toEnter1);
+ verifyAndClearTransition(to2.enterTransition, toSharedRect2, toEnter2);
+ verifyAndClearTransition(to1.sharedElementEnter, fromSharedRect1, fromShared1, toShared1);
+ verifyAndClearTransition(to2.sharedElementEnter, fromSharedRect2, fromShared2, toShared2);
+ verifyNoOtherTransitions(to1);
+ verifyNoOtherTransitions(to2);
+
+ // Now pop it back
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mFragmentManager.popBackStack();
+ mFragmentManager.popBackStack();
+ }
+ });
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ from1.waitForTransition();
+ from2.waitForTransition();
+ to1.waitForTransition();
+ to2.waitForTransition();
+
+ final View returnEnter1 = findViewById(from1, R.id.greenSquare);
+ final View returnShared1 = findViewById(from1, R.id.blueSquare);
+
+ final View returnEnter2 = findViewById(from2, fromExitId2);
+ final View returnShared2 = findViewById(from2, fromSharedId2);
+
+ verifyAndClearTransition(to1.returnTransition, toSharedRect1, toEnter1);
+ verifyAndClearTransition(to2.returnTransition, toSharedRect2, toEnter2);
+ verifyAndClearTransition(to1.sharedElementReturn, toSharedRect1, toShared1, returnShared1);
+ verifyAndClearTransition(to2.sharedElementReturn, toSharedRect2, toShared2, returnShared2);
+ verifyNoOtherTransitions(to1);
+ verifyNoOtherTransitions(to2);
+
+ verifyAndClearTransition(from1.reenterTransition, fromSharedRect1, returnEnter1);
+ verifyAndClearTransition(from2.reenterTransition, fromSharedRect2, returnEnter2);
+ verifyNoOtherTransitions(from1);
+ verifyNoOtherTransitions(from2);
}
- private boolean waitForStart(TestFragment fragment, int key) throws InterruptedException {
- boolean started = fragment.waitForStart(key);
- mInstrumentation.waitForIdleSync();
- return started;
- }
+ private void verifyPopTransition(final int numPops, TransitionFragment from,
+ TransitionFragment to, TransitionFragment... others) throws Throwable {
+ final View startBlue = findBlue();
+ final View startGreen = findGreen();
+ final View startRed = findRed();
+ final Rect startSharedRect = getBoundsOnScreen(startBlue);
- private boolean waitForEnd(TestFragment fragment, int key) throws InterruptedException {
- if (!waitForStart(fragment, key)) {
- return false;
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < numPops; i++) {
+ mFragmentManager.popBackStack();
+ }
+ }
+ });
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ to.waitForTransition();
+ final View endGreen = findGreen();
+ final View endBlue = findBlue();
+ final View endRed = findRed();
+ final Rect endSharedRect = getBoundsOnScreen(endBlue);
+
+ if (startRed != null) {
+ verifyAndClearTransition(from.returnTransition, startSharedRect, startGreen, startRed);
+ } else {
+ verifyAndClearTransition(from.returnTransition, startSharedRect, startGreen);
}
- final boolean ended = fragment.waitForEnd(key);
- mInstrumentation.waitForIdleSync();
- return ended;
+ verifyAndClearTransition(from.sharedElementReturn, startSharedRect, startBlue, endBlue);
+ verifyNoOtherTransitions(from);
+
+ if (endRed != null) {
+ verifyAndClearTransition(to.reenterTransition, endSharedRect, endGreen, endRed);
+ } else {
+ verifyAndClearTransition(to.reenterTransition, endSharedRect, endGreen);
+ }
+ verifyNoOtherTransitions(to);
+
+ if (others != null) {
+ for (TransitionFragment fragment : others) {
+ verifyNoOtherTransitions(fragment);
+ }
+ }
+ }
+
+ private static Rect getBoundsOnScreen(View view) {
+ final int[] loc = new int[2];
+ view.getLocationOnScreen(loc);
+ return new Rect(loc[0], loc[1], loc[0] + view.getWidth(), loc[1] + view.getHeight());
+ }
+
+ public static class ComplexTransitionFragment extends TransitionFragment {
+ public final TrackingTransition sharedElementEnterTransition1 = new TrackingTransition();
+ public final TrackingTransition sharedElementEnterTransition2 = new TrackingTransition();
+ public final TrackingTransition sharedElementReturnTransition1 = new TrackingTransition();
+ public final TrackingTransition sharedElementReturnTransition2 = new TrackingTransition();
+
+ public final TransitionSet sharedElementEnterTransition = new TransitionSet()
+ .addTransition(sharedElementEnterTransition1)
+ .addTransition(sharedElementEnterTransition2);
+ public final TransitionSet sharedElementReturnTransition = new TransitionSet()
+ .addTransition(sharedElementReturnTransition1)
+ .addTransition(sharedElementReturnTransition2);
+
+ public ComplexTransitionFragment() {
+ sharedElementEnterTransition1.addTarget(R.id.blueSquare);
+ sharedElementEnterTransition2.addTarget(R.id.greenSquare);
+ sharedElementReturnTransition1.addTarget(R.id.blueSquare);
+ sharedElementReturnTransition2.addTarget(R.id.greenSquare);
+ setSharedElementEnterTransition(sharedElementEnterTransition);
+ setSharedElementReturnTransition(sharedElementReturnTransition);
+ }
+
}
}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentViewTests.java b/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
new file mode 100644
index 0000000..22a52ed
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
@@ -0,0 +1,889 @@
+/*
+ * 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.v4.app;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertSame;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.support.fragment.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class FragmentViewTests {
+ @Rule
+ public ActivityTestRule<FragmentTestActivity> mActivityRule =
+ new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+ private Instrumentation mInstrumentation;
+
+ @Before
+ public void setupInstrumentation() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ }
+
+ // Test that adding a fragment adds the Views in the proper order. Popping the back stack
+ // should remove the correct Views.
+ @Test
+ public void addFragments() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ // One fragment with a view
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment1).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container, fragment1);
+
+ // Add another on top
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment2).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container, fragment1, fragment2);
+
+ // Now add two in one transaction:
+ final StrictViewFragment fragment3 = new StrictViewFragment();
+ final StrictViewFragment fragment4 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment3)
+ .add(R.id.fragmentContainer, fragment4)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container, fragment1, fragment2, fragment3, fragment4);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container, fragment1, fragment2);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertEquals(1, container.getChildCount());
+ FragmentTestUtil.assertChildren(container, fragment1);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container);
+ }
+
+ // Add fragments to multiple containers in the same transaction. Make sure that
+ // they pop correctly, too.
+ @Test
+ public void addTwoContainers() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+ ViewGroup container1 = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer1);
+ ViewGroup container2 = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer2);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer1, fragment1).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container1, fragment1);
+
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer2, fragment2).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container2, fragment2);
+
+ final StrictViewFragment fragment3 = new StrictViewFragment();
+ final StrictViewFragment fragment4 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer1, fragment3)
+ .add(R.id.fragmentContainer2, fragment4)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container1, fragment1, fragment3);
+ FragmentTestUtil.assertChildren(container2, fragment2, fragment4);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container1, fragment1);
+ FragmentTestUtil.assertChildren(container2, fragment2);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container1, fragment1);
+ FragmentTestUtil.assertChildren(container2);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertEquals(0, container1.getChildCount());
+ }
+
+ // When you add a fragment that's has already been added, it should throw.
+ @Test
+ public void doubleAdd() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment1).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ fm.executePendingTransactions();
+ fail("Adding a fragment that is already added should be an error");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ }
+ });
+ }
+
+ // Make sure that removed fragments remove the right Views. Popping the back stack should
+ // add the Views back properly
+ @Test
+ public void removeFragments() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ final StrictViewFragment fragment3 = new StrictViewFragment();
+ final StrictViewFragment fragment4 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .add(R.id.fragmentContainer, fragment2, "2")
+ .add(R.id.fragmentContainer, fragment3, "3")
+ .add(R.id.fragmentContainer, fragment4, "4")
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container, fragment1, fragment2, fragment3, fragment4);
+
+ // Remove a view
+ fm.beginTransaction().remove(fragment4).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertEquals(3, container.getChildCount());
+ FragmentTestUtil.assertChildren(container, fragment1, fragment2, fragment3);
+
+ // remove another one
+ fm.beginTransaction().remove(fragment2).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container, fragment1, fragment3);
+
+ // Now remove the remaining:
+ fm.beginTransaction()
+ .remove(fragment3)
+ .remove(fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ final Fragment replacement1 = fm.findFragmentByTag("1");
+ final Fragment replacement3 = fm.findFragmentByTag("3");
+ FragmentTestUtil.assertChildren(container, replacement1, replacement3);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ final Fragment replacement2 = fm.findFragmentByTag("2");
+ FragmentTestUtil.assertChildren(container, replacement1, replacement3, replacement2);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ final Fragment replacement4 = fm.findFragmentByTag("4");
+ FragmentTestUtil.assertChildren(container, replacement1, replacement3, replacement2,
+ replacement4);
+ }
+
+ // Removing a hidden fragment should remove the View and popping should bring it back hidden
+ @Test
+ public void removeHiddenView() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment1, "1").hide(fragment1).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container, fragment1);
+ assertTrue(fragment1.isHidden());
+
+ fm.beginTransaction().remove(fragment1).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ final Fragment replacement1 = fm.findFragmentByTag("1");
+ FragmentTestUtil.assertChildren(container, replacement1);
+ assertTrue(replacement1.isHidden());
+ assertEquals(View.GONE, replacement1.getView().getVisibility());
+ }
+
+ // Removing a detached fragment should do nothing to the View and popping should bring
+ // the Fragment back detached
+ @Test
+ public void removeDetatchedView() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .detach(fragment1)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container);
+ assertTrue(fragment1.isDetached());
+
+ fm.beginTransaction().remove(fragment1).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ final Fragment replacement1 = fm.findFragmentByTag("1");
+ FragmentTestUtil.assertChildren(container);
+ assertTrue(replacement1.isDetached());
+ }
+
+ // Unlike adding the same fragment twice, you should be able to add and then remove and then
+ // add the same fragment in one transaction.
+ @Test
+ public void addRemoveAdd() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment)
+ .remove(fragment)
+ .add(R.id.fragmentContainer, fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container, fragment);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container);
+ }
+
+ // Removing a fragment that isn't in should throw
+ @Test
+ public void removeNothThere() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction().remove(fragment).commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Removing a fragment that isn't in should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Hide a fragment and its View should be GONE. Then pop it and the View should be VISIBLE
+ @Test
+ public void hideFragment() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+ fm.beginTransaction().hide(fragment).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertTrue(fragment.isHidden());
+ assertEquals(View.GONE, fragment.getView().getVisibility());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertFalse(fragment.isHidden());
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+ }
+
+ // Hiding a hidden fragment should throw
+ @Test
+ public void doubleHide() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment)
+ .hide(fragment)
+ .hide(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Hiding a hidden fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Hiding a non-existing fragment should throw
+ @Test
+ public void hideUnAdded() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .hide(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Hiding a non-existing fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Show a hidden fragment and its View should be VISIBLE. Then pop it and the View should be
+ // GONE.
+ @Test
+ public void showFragment() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertTrue(fragment.isHidden());
+ assertEquals(View.GONE, fragment.getView().getVisibility());
+
+ fm.beginTransaction().show(fragment).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertFalse(fragment.isHidden());
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertTrue(fragment.isHidden());
+ assertEquals(View.GONE, fragment.getView().getVisibility());
+ }
+
+ // Showing a shown fragment should throw
+ @Test
+ public void showShown() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment)
+ .show(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Showing a visible fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Showing a non-existing fragment should throw
+ @Test
+ public void showUnAdded() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .show(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Showing a non-existing fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Detaching a fragment should remove the View from the hierarchy. Then popping it should
+ // bring it back VISIBLE
+ @Test
+ public void detachFragment() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertFalse(fragment.isDetached());
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+ fm.beginTransaction().detach(fragment).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container);
+ assertTrue(fragment.isDetached());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertFalse(fragment.isDetached());
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+ }
+
+ // Detaching a hidden fragment should remove the View from the hierarchy. Then popping it should
+ // bring it back hidden
+ @Test
+ public void detachHiddenFragment() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertFalse(fragment.isDetached());
+ assertTrue(fragment.isHidden());
+ assertEquals(View.GONE, fragment.getView().getVisibility());
+
+ fm.beginTransaction().detach(fragment).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container);
+ assertTrue(fragment.isHidden());
+ assertTrue(fragment.isDetached());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertTrue(fragment.isHidden());
+ assertFalse(fragment.isDetached());
+ assertEquals(View.GONE, fragment.getView().getVisibility());
+ }
+
+ // Detaching a detached fragment should throw
+ @Test
+ public void detachDetatched() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment)
+ .detach(fragment)
+ .detach(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Detaching a detached fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Detaching a non-existing fragment should throw
+ @Test
+ public void detachUnAdded() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .detach(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Detaching a non-existing fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Attaching a fragment should add the View back into the hierarchy. Then popping it should
+ // remove it again
+ @Test
+ public void attachFragment() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment).detach(fragment).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container);
+ assertTrue(fragment.isDetached());
+
+ fm.beginTransaction().attach(fragment).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertFalse(fragment.isDetached());
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container);
+ assertTrue(fragment.isDetached());
+ }
+
+ // Attaching a hidden fragment should add the View as GONE the hierarchy. Then popping it should
+ // remove it again.
+ @Test
+ public void attachHiddenFragment() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment)
+ .hide(fragment)
+ .detach(fragment)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container);
+ assertTrue(fragment.isDetached());
+ assertTrue(fragment.isHidden());
+
+ fm.beginTransaction().attach(fragment).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertTrue(fragment.isHidden());
+ assertFalse(fragment.isDetached());
+ assertEquals(View.GONE, fragment.getView().getVisibility());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container);
+ assertTrue(fragment.isDetached());
+ assertTrue(fragment.isHidden());
+ }
+
+ // Attaching an attached fragment should throw
+ @Test
+ public void attachAttached() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment)
+ .attach(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Attaching an attached fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Attaching a non-existing fragment should throw
+ @Test
+ public void attachUnAdded() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .attach(fragment)
+ .commit();
+ try {
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ fail("Attaching a non-existing fragment should throw an exception");
+ } catch (Throwable t) {
+ // expected
+ }
+ }
+
+ // Simple replace of one fragment in a container. Popping should replace it back again
+ @Test
+ public void replaceOne() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment1, "1").commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment1);
+
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment2);
+ assertEquals(View.VISIBLE, fragment2.getView().getVisibility());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ Fragment replacement1 = fm.findFragmentByTag("1");
+ assertNotNull(replacement1);
+ FragmentTestUtil.assertChildren(container, replacement1);
+ assertFalse(replacement1.isHidden());
+ assertTrue(replacement1.isAdded());
+ assertFalse(replacement1.isDetached());
+ assertEquals(View.VISIBLE, replacement1.getView().getVisibility());
+ }
+
+ // Replace of multiple fragments in a container. Popping should replace it back again
+ @Test
+ public void replaceTwo() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .add(R.id.fragmentContainer, fragment2, "2")
+ .hide(fragment2)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment1, fragment2);
+
+ final StrictViewFragment fragment3 = new StrictViewFragment();
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment3)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment3);
+ assertEquals(View.VISIBLE, fragment3.getView().getVisibility());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ Fragment replacement1 = fm.findFragmentByTag("1");
+ Fragment replacement2 = fm.findFragmentByTag("2");
+ assertNotNull(replacement1);
+ assertNotNull(replacement2);
+ FragmentTestUtil.assertChildren(container, replacement1, replacement2);
+ assertFalse(replacement1.isHidden());
+ assertTrue(replacement1.isAdded());
+ assertFalse(replacement1.isDetached());
+ assertEquals(View.VISIBLE, replacement1.getView().getVisibility());
+
+ // fragment2 was hidden, so it should be returned hidden
+ assertTrue(replacement2.isHidden());
+ assertTrue(replacement2.isAdded());
+ assertFalse(replacement2.isDetached());
+ assertEquals(View.GONE, replacement2.getView().getVisibility());
+ }
+
+ // Replace of empty container. Should act as add and popping should just remove the fragment
+ @Test
+ public void replaceZero() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ final StrictViewFragment fragment = new StrictViewFragment();
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment);
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container);
+ }
+
+ // Replace a fragment that exists with itself
+ @Test
+ public void replaceExisting() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .add(R.id.fragmentContainer, fragment2, "2")
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment1, fragment2);
+
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment1);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ final Fragment replacement1 = fm.findFragmentByTag("1");
+ final Fragment replacement2 = fm.findFragmentByTag("2");
+
+ assertSame(fragment1, replacement1);
+ FragmentTestUtil.assertChildren(container, replacement1, replacement2);
+ }
+
+ // Have two replace operations in the same transaction to ensure that they
+ // don't interfere with each other
+ @Test
+ public void replaceReplace() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+ ViewGroup container1 = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer1);
+ ViewGroup container2 = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer2);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ final StrictViewFragment fragment3 = new StrictViewFragment();
+ final StrictViewFragment fragment4 = new StrictViewFragment();
+ final StrictViewFragment fragment5 = new StrictViewFragment();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer1, fragment1)
+ .add(R.id.fragmentContainer2, fragment2)
+ .replace(R.id.fragmentContainer1, fragment3)
+ .replace(R.id.fragmentContainer2, fragment4)
+ .replace(R.id.fragmentContainer1, fragment5)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container1, fragment5);
+ assertChildren(container2, fragment4);
+
+ fm.popBackStack();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertChildren(container1);
+ assertChildren(container2);
+ }
+
+ // Test to prevent regressions in FragmentManager fragment replace method. See b/24693644
+ @Test
+ public void testReplaceFragment() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ StrictViewFragment fragmentA = new StrictViewFragment();
+ fragmentA.setLayoutId(R.layout.text_a);
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragmentA)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ assertNotNull(findViewById(R.id.textA));
+ assertNull(findViewById(R.id.textB));
+ assertNull(findViewById(R.id.textC));
+
+ StrictViewFragment fragmentB = new StrictViewFragment();
+ fragmentB.setLayoutId(R.layout.text_b);
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragmentB)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertNotNull(findViewById(R.id.textA));
+ assertNotNull(findViewById(R.id.textB));
+ assertNull(findViewById(R.id.textC));
+
+ StrictViewFragment fragmentC = new StrictViewFragment();
+ fragmentC.setLayoutId(R.layout.text_c);
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer, fragmentC)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ assertNull(findViewById(R.id.textA));
+ assertNull(findViewById(R.id.textB));
+ assertNotNull(findViewById(R.id.textC));
+ }
+
+ private View findViewById(int viewId) {
+ return mActivityRule.getActivity().findViewById(viewId);
+ }
+
+ private void assertChildren(ViewGroup container, Fragment... fragments) {
+ final int numFragments = fragments == null ? 0 : fragments.length;
+ assertEquals("There aren't the correct number of fragment Views in its container",
+ numFragments, container.getChildCount());
+ for (int i = 0; i < numFragments; i++) {
+ assertEquals("Wrong Fragment View order for [" + i + "]", container.getChildAt(i),
+ fragments[i].getView());
+ }
+ }
+}
diff --git a/fragment/tests/java/android/support/v4/app/HostCallbacks.java b/fragment/tests/java/android/support/v4/app/HostCallbacks.java
new file mode 100644
index 0000000..9a0ef1c
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/HostCallbacks.java
@@ -0,0 +1,46 @@
+/*
+ * 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.v4.app;
+
+import android.os.Handler;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+
+class HostCallbacks extends FragmentHostCallback<FragmentTestActivity> {
+ private final FragmentTestActivity mActivity;
+
+ HostCallbacks(FragmentTestActivity activity, Handler handler, int windowAnimations) {
+ super(activity, handler, windowAnimations);
+ mActivity = activity;
+ }
+
+ @Override
+ public FragmentTestActivity onGetHost() {
+ return mActivity;
+ }
+
+ @Override
+ public View onFindViewById(int id) {
+ return mActivity.findViewById(id);
+ }
+
+ @Override
+ public LayoutInflater onGetLayoutInflater() {
+ return mActivity.getLayoutInflater().cloneInContext(mActivity);
+ }
+
+}
diff --git a/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java b/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java
new file mode 100644
index 0000000..b20298d
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java
@@ -0,0 +1,813 @@
+/*
+ * 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.v4.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.fragment.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+public class PostponedTransitionTest {
+ @Rule
+ public ActivityTestRule<FragmentTestActivity> mActivityRule =
+ new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+ private Instrumentation mInstrumentation;
+ private PostponedFragment1 mBeginningFragment;
+
+ @Before
+ public void setupContainer() throws Throwable {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ mBeginningFragment = new PostponedFragment1();
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, mBeginningFragment)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ mBeginningFragment.startPostponedEnterTransition();
+ mBeginningFragment.waitForTransition();
+ clearTargets(mBeginningFragment);
+ }
+
+ // Ensure that replacing with a fragment that has a postponed transition
+ // will properly postpone it, both adding and popping.
+ @Test
+ public void replaceTransition() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final View startBlue = mActivityRule.getActivity().findViewById(R.id.blueSquare);
+
+ final PostponedFragment2 fragment = new PostponedFragment2();
+ fm.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ // should be postponed now
+ assertPostponedTransition(mBeginningFragment, fragment, null);
+
+ // start the postponed transition
+ fragment.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertForwardTransition(mBeginningFragment, fragment);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ // should be postponed going back, too
+ assertPostponedTransition(fragment, mBeginningFragment, null);
+
+ // start the postponed transition
+ mBeginningFragment.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment, mBeginningFragment);
+ }
+
+ // Ensure that postponed transition is forced after another has been committed.
+ // This tests when the transactions are executed together
+ @Test
+ public void forcedTransition1() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final View startBlue = mActivityRule.getActivity().findViewById(R.id.blueSquare);
+
+ final PostponedFragment2 fragment2 = new PostponedFragment2();
+ final PostponedFragment1 fragment3 = new PostponedFragment1();
+
+ final int[] commit = new int[1];
+ // Need to run this on the UI thread so that the transaction doesn't start
+ // between the two
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ commit[0] = fm.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment3)
+ .addToBackStack(null)
+ .commit();
+ }
+ });
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ // transition to fragment2 should be started
+ assertForwardTransition(mBeginningFragment, fragment2);
+
+ // fragment3 should be postponed, but fragment2 should be executed with no transition.
+ assertPostponedTransition(fragment2, fragment3, mBeginningFragment);
+
+ // start the postponed transition
+ fragment3.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertForwardTransition(fragment2, fragment3);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule, commit[0],
+ FragmentManager.POP_BACK_STACK_INCLUSIVE);
+
+ assertBackTransition(fragment3, fragment2);
+
+ assertPostponedTransition(fragment2, mBeginningFragment, fragment3);
+
+ // start the postponed transition
+ mBeginningFragment.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment2, mBeginningFragment);
+ }
+
+ // Ensure that postponed transition is forced after another has been committed.
+ // This tests when the transactions are processed separately.
+ @Test
+ public void forcedTransition2() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final View startBlue = mActivityRule.getActivity().findViewById(R.id.blueSquare);
+
+ final PostponedFragment2 fragment2 = new PostponedFragment2();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(mBeginningFragment, fragment2, null);
+
+ final PostponedFragment1 fragment3 = new PostponedFragment1();
+ fm.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment3)
+ .addToBackStack(null)
+ .commit();
+
+ // This should cancel the mBeginningFragment -> fragment2 transition
+ // and start fragment2 -> fragment3 transition postponed
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ // fragment3 should be postponed, but fragment2 should be executed with no transition.
+ assertPostponedTransition(fragment2, fragment3, mBeginningFragment);
+
+ // start the postponed transition
+ fragment3.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertForwardTransition(fragment2, fragment3);
+
+ // Pop back to fragment2, but it should be postponed
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponedTransition(fragment3, fragment2, null);
+
+ // Pop to mBeginningFragment -- should cancel the fragment2 transition and
+ // start the mBeginningFragment transaction postponed
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponedTransition(fragment2, mBeginningFragment, fragment3);
+
+ // start the postponed transition
+ mBeginningFragment.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment2, mBeginningFragment);
+ }
+
+ // Do a bunch of things to one fragment in a transaction and see if it can screw things up.
+ @Test
+ public void crazyTransition() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final View startBlue = mActivityRule.getActivity().findViewById(R.id.blueSquare);
+
+ final PostponedFragment2 fragment2 = new PostponedFragment2();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .hide(mBeginningFragment)
+ .replace(R.id.fragmentContainer, fragment2)
+ .hide(fragment2)
+ .detach(fragment2)
+ .attach(fragment2)
+ .show(fragment2)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(mBeginningFragment, fragment2, null);
+
+ // start the postponed transition
+ fragment2.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertForwardTransition(mBeginningFragment, fragment2);
+
+ // Pop back to fragment2, but it should be postponed
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponedTransition(fragment2, mBeginningFragment, null);
+
+ // start the postponed transition
+ mBeginningFragment.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment2, mBeginningFragment);
+ }
+
+ // Execute transactions on different containers and ensure that they don't conflict
+ @Test
+ public void differentContainers() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ fm.beginTransaction().remove(mBeginningFragment).commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+
+ TransitionFragment fragment1 = new PostponedFragment1();
+ TransitionFragment fragment2 = new PostponedFragment1();
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer1, fragment1)
+ .add(R.id.fragmentContainer2, fragment2)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ fragment1.startPostponedEnterTransition();
+ fragment2.startPostponedEnterTransition();
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+ clearTargets(fragment1);
+ clearTargets(fragment2);
+
+ final View startBlue1 = fragment1.getView().findViewById(R.id.blueSquare);
+ final View startBlue2 = fragment2.getView().findViewById(R.id.blueSquare);
+
+ final TransitionFragment fragment3 = new PostponedFragment2();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue1, "blueSquare")
+ .replace(R.id.fragmentContainer1, fragment3)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(fragment1, fragment3, null);
+
+ final TransitionFragment fragment4 = new PostponedFragment2();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue2, "blueSquare")
+ .replace(R.id.fragmentContainer2, fragment4)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(fragment1, fragment3, null);
+ assertPostponedTransition(fragment2, fragment4, null);
+
+ // start the postponed transition
+ fragment3.startPostponedEnterTransition();
+
+ // make sure only one ran
+ assertForwardTransition(fragment1, fragment3);
+ assertPostponedTransition(fragment2, fragment4, null);
+
+ // start the postponed transition
+ fragment4.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertForwardTransition(fragment2, fragment4);
+
+ // Pop back to fragment2 -- should be postponed
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponedTransition(fragment4, fragment2, null);
+
+ // Pop back to fragment1 -- also should be postponed
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponedTransition(fragment4, fragment2, null);
+ assertPostponedTransition(fragment3, fragment1, null);
+
+ // start the postponed transition
+ fragment2.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment4, fragment2);
+
+ // but not the postponed one
+ assertPostponedTransition(fragment3, fragment1, null);
+
+ // start the postponed transition
+ fragment1.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment3, fragment1);
+ }
+
+ // Execute transactions on different containers and ensure that they don't conflict.
+ // The postponement can be started out-of-order
+ @Test
+ public void outOfOrderContainers() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ fm.beginTransaction().remove(mBeginningFragment).commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+
+ TransitionFragment fragment1 = new PostponedFragment1();
+ TransitionFragment fragment2 = new PostponedFragment1();
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer1, fragment1)
+ .add(R.id.fragmentContainer2, fragment2)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ fragment1.startPostponedEnterTransition();
+ fragment2.startPostponedEnterTransition();
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+ clearTargets(fragment1);
+ clearTargets(fragment2);
+
+ final View startBlue1 = fragment1.getView().findViewById(R.id.blueSquare);
+ final View startBlue2 = fragment2.getView().findViewById(R.id.blueSquare);
+
+ final TransitionFragment fragment3 = new PostponedFragment2();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue1, "blueSquare")
+ .replace(R.id.fragmentContainer1, fragment3)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(fragment1, fragment3, null);
+
+ final TransitionFragment fragment4 = new PostponedFragment2();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue2, "blueSquare")
+ .replace(R.id.fragmentContainer2, fragment4)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(fragment1, fragment3, null);
+ assertPostponedTransition(fragment2, fragment4, null);
+
+ // start the postponed transition
+ fragment4.startPostponedEnterTransition();
+
+ // make sure only one ran
+ assertForwardTransition(fragment2, fragment4);
+ assertPostponedTransition(fragment1, fragment3, null);
+
+ // start the postponed transition
+ fragment3.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertForwardTransition(fragment1, fragment3);
+
+ // Pop back to fragment2 -- should be postponed
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponedTransition(fragment4, fragment2, null);
+
+ // Pop back to fragment1 -- also should be postponed
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ assertPostponedTransition(fragment4, fragment2, null);
+ assertPostponedTransition(fragment3, fragment1, null);
+
+ // start the postponed transition
+ fragment1.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment3, fragment1);
+
+ // but not the postponed one
+ assertPostponedTransition(fragment4, fragment2, null);
+
+ // start the postponed transition
+ fragment2.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertBackTransition(fragment4, fragment2);
+ }
+
+ // Make sure that commitNow for a transaction on a different fragment container doesn't
+ // affect the postponed transaction
+ @Test
+ public void commitNowNoEffect() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ fm.beginTransaction().remove(mBeginningFragment).commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+
+ final TransitionFragment fragment1 = new PostponedFragment1();
+ final TransitionFragment fragment2 = new PostponedFragment1();
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer1, fragment1)
+ .add(R.id.fragmentContainer2, fragment2)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ fragment1.startPostponedEnterTransition();
+ fragment2.startPostponedEnterTransition();
+ fragment1.waitForTransition();
+ fragment2.waitForTransition();
+ clearTargets(fragment1);
+ clearTargets(fragment2);
+
+ final View startBlue1 = fragment1.getView().findViewById(R.id.blueSquare);
+ final View startBlue2 = fragment2.getView().findViewById(R.id.blueSquare);
+
+ final TransitionFragment fragment3 = new PostponedFragment2();
+ final StrictFragment strictFragment1 = new StrictFragment();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue1, "blueSquare")
+ .replace(R.id.fragmentContainer1, fragment3)
+ .add(strictFragment1, "1")
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(fragment1, fragment3, null);
+
+ final TransitionFragment fragment4 = new PostponedFragment2();
+ final StrictFragment strictFragment2 = new StrictFragment();
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ fm.beginTransaction()
+ .addSharedElement(startBlue2, "blueSquare")
+ .replace(R.id.fragmentContainer2, fragment4)
+ .remove(strictFragment1)
+ .add(strictFragment2, "2")
+ .commitNow();
+ }
+ });
+
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(fragment1, fragment3, null);
+ assertPostponedTransition(fragment2, fragment4, null);
+
+ // start the postponed transition
+ fragment4.startPostponedEnterTransition();
+
+ // make sure only one ran
+ assertForwardTransition(fragment2, fragment4);
+ assertPostponedTransition(fragment1, fragment3, null);
+
+ // start the postponed transition
+ fragment3.startPostponedEnterTransition();
+
+ // make sure it ran
+ assertForwardTransition(fragment1, fragment3);
+ }
+
+ // Make sure that commitNow for a transaction affecting a postponed fragment in the same
+ // container forces the postponed transition to start.
+ @Test
+ public void commitNowStartsPostponed() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final View startBlue1 = mBeginningFragment.getView().findViewById(R.id.blueSquare);
+
+ final TransitionFragment fragment2 = new PostponedFragment2();
+ final TransitionFragment fragment1 = new PostponedFragment1();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue1, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ final View startBlue2 = fragment2.getView().findViewById(R.id.blueSquare);
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ fm.beginTransaction()
+ .addSharedElement(startBlue2, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment1)
+ .commitNow();
+ }
+ });
+
+ assertPostponedTransition(fragment2, fragment1, mBeginningFragment);
+
+ // start the postponed transition
+ fragment1.startPostponedEnterTransition();
+
+ assertForwardTransition(fragment2, fragment1);
+ }
+
+ // Make sure that when a transaction that removes a view is postponed that
+ // another transaction doesn't accidentally remove the view early.
+ @Test
+ public void noAccidentalRemoval() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ fm.beginTransaction().remove(mBeginningFragment).commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+
+ TransitionFragment fragment1 = new PostponedFragment1();
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer1, fragment1)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ fragment1.startPostponedEnterTransition();
+ fragment1.waitForTransition();
+ clearTargets(fragment1);
+
+ TransitionFragment fragment2 = new PostponedFragment2();
+ // Create a postponed transaction that removes a view
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer1, fragment2)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+ assertPostponedTransition(fragment1, fragment2, null);
+
+ TransitionFragment fragment3 = new PostponedFragment1();
+ // Create a transaction that doesn't interfere with the previously postponed one
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer2, fragment3)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(fragment1, fragment2, null);
+
+ fragment3.startPostponedEnterTransition();
+ fragment3.waitForTransition();
+ clearTargets(fragment3);
+
+ assertPostponedTransition(fragment1, fragment2, null);
+ }
+
+ // Ensure that a postponed transaction that is popped runs immediately and that
+ // the transaction results in the original state with no transition.
+ @Test
+ public void popPostponedTransaction() throws Throwable {
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+ final View startBlue = mBeginningFragment.getView().findViewById(R.id.blueSquare);
+
+ final TransitionFragment fragment = new PostponedFragment2();
+
+ fm.beginTransaction()
+ .addSharedElement(startBlue, "blueSquare")
+ .replace(R.id.fragmentContainer, fragment)
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ assertPostponedTransition(mBeginningFragment, fragment, null);
+
+ FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+ fragment.waitForNoTransition();
+ mBeginningFragment.waitForNoTransition();
+
+ assureNoTransition(fragment);
+ assureNoTransition(mBeginningFragment);
+
+ assertFalse(fragment.isAdded());
+ assertNull(fragment.getView());
+ assertNotNull(mBeginningFragment.getView());
+ assertEquals(View.VISIBLE, mBeginningFragment.getView().getVisibility());
+ assertTrue(mBeginningFragment.getView().isAttachedToWindow());
+ }
+
+ // Make sure that when saving the state during a postponed transaction that it saves
+ // the state as if it wasn't postponed.
+ @Test
+ public void saveWhilePostponed() throws Throwable {
+ final FragmentController fc1 = FragmentTestUtil.createController(mActivityRule);
+ FragmentTestUtil.resume(fc1, null);
+
+ final FragmentManager fm1 = fc1.getSupportFragmentManager();
+
+ PostponedFragment1 fragment1 = new PostponedFragment1();
+ fm1.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1, "1")
+ .addToBackStack(null)
+ .commit();
+ FragmentTestUtil.waitForExecution(mActivityRule);
+
+ Pair<Parcelable, FragmentManagerNonConfig> state =
+ FragmentTestUtil.destroy(fc1);
+
+ final FragmentController fc2 = FragmentTestUtil.createController(mActivityRule);
+ FragmentTestUtil.resume(fc2, state);
+
+ final FragmentManager fm2 = fc2.getSupportFragmentManager();
+ Fragment fragment2 = fm2.findFragmentByTag("1");
+ assertNotNull(fragment2);
+ assertNotNull(fragment2.getView());
+ assertEquals(View.VISIBLE, fragment2.getView().getVisibility());
+ assertTrue(fragment2.isResumed());
+ assertTrue(fragment2.isAdded());
+ assertTrue(fragment2.getView().isAttachedToWindow());
+
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(fm2.popBackStackImmediate());
+
+ }
+ });
+
+ assertFalse(fragment2.isResumed());
+ assertFalse(fragment2.isAdded());
+ assertNull(fragment2.getView());
+ }
+
+ private void assertPostponedTransition(TransitionFragment fromFragment,
+ TransitionFragment toFragment, TransitionFragment removedFragment)
+ throws InterruptedException {
+ if (removedFragment != null) {
+ assertNull(removedFragment.getView());
+ assureNoTransition(removedFragment);
+ }
+
+ toFragment.waitForNoTransition();
+ assertNotNull(fromFragment.getView());
+ assertNotNull(toFragment.getView());
+ assertTrue(fromFragment.getView().isAttachedToWindow());
+ assertTrue(toFragment.getView().isAttachedToWindow());
+ assertEquals(View.VISIBLE, fromFragment.getView().getVisibility());
+ assertEquals(View.INVISIBLE, toFragment.getView().getVisibility());
+ assureNoTransition(fromFragment);
+ assureNoTransition(toFragment);
+ assertTrue(fromFragment.isResumed());
+ assertFalse(toFragment.isResumed());
+ }
+
+ private void clearTargets(TransitionFragment fragment) {
+ fragment.enterTransition.targets.clear();
+ fragment.reenterTransition.targets.clear();
+ fragment.exitTransition.targets.clear();
+ fragment.returnTransition.targets.clear();
+ fragment.sharedElementEnter.targets.clear();
+ fragment.sharedElementReturn.targets.clear();
+ }
+
+ private void assureNoTransition(TransitionFragment fragment) {
+ assertEquals(0, fragment.enterTransition.targets.size());
+ assertEquals(0, fragment.reenterTransition.targets.size());
+ assertEquals(0, fragment.enterTransition.targets.size());
+ assertEquals(0, fragment.returnTransition.targets.size());
+ assertEquals(0, fragment.sharedElementEnter.targets.size());
+ assertEquals(0, fragment.sharedElementReturn.targets.size());
+ }
+
+ private void assertForwardTransition(TransitionFragment start, TransitionFragment end)
+ throws InterruptedException {
+ start.waitForTransition();
+ end.waitForTransition();
+ assertEquals(0, start.enterTransition.targets.size());
+ assertEquals(1, end.enterTransition.targets.size());
+
+ assertEquals(0, start.reenterTransition.targets.size());
+ assertEquals(0, end.reenterTransition.targets.size());
+
+ assertEquals(0, start.returnTransition.targets.size());
+ assertEquals(0, end.returnTransition.targets.size());
+
+ assertEquals(1, start.exitTransition.targets.size());
+ assertEquals(0, end.exitTransition.targets.size());
+
+ assertEquals(0, start.sharedElementEnter.targets.size());
+ assertEquals(2, end.sharedElementEnter.targets.size());
+
+ assertEquals(0, start.sharedElementReturn.targets.size());
+ assertEquals(0, end.sharedElementReturn.targets.size());
+
+ final View blue = end.getView().findViewById(R.id.blueSquare);
+ assertTrue(end.sharedElementEnter.targets.contains(blue));
+ assertEquals("blueSquare", end.sharedElementEnter.targets.get(0).getTransitionName());
+ assertEquals("blueSquare", end.sharedElementEnter.targets.get(1).getTransitionName());
+
+ assertNoTargets(start);
+ assertNoTargets(end);
+
+ clearTargets(start);
+ clearTargets(end);
+ }
+
+ private void assertBackTransition(TransitionFragment start, TransitionFragment end)
+ throws InterruptedException {
+ start.waitForTransition();
+ end.waitForTransition();
+ assertEquals(1, end.reenterTransition.targets.size());
+ assertEquals(0, start.reenterTransition.targets.size());
+
+ assertEquals(0, end.returnTransition.targets.size());
+ assertEquals(1, start.returnTransition.targets.size());
+
+ assertEquals(0, start.enterTransition.targets.size());
+ assertEquals(0, end.enterTransition.targets.size());
+
+ assertEquals(0, start.exitTransition.targets.size());
+ assertEquals(0, end.exitTransition.targets.size());
+
+ assertEquals(0, start.sharedElementEnter.targets.size());
+ assertEquals(0, end.sharedElementEnter.targets.size());
+
+ assertEquals(2, start.sharedElementReturn.targets.size());
+ assertEquals(0, end.sharedElementReturn.targets.size());
+
+ final View blue = end.getView().findViewById(R.id.blueSquare);
+ assertTrue(start.sharedElementReturn.targets.contains(blue));
+ assertEquals("blueSquare", start.sharedElementReturn.targets.get(0).getTransitionName());
+ assertEquals("blueSquare", start.sharedElementReturn.targets.get(1).getTransitionName());
+
+ assertNoTargets(end);
+ assertNoTargets(start);
+
+ clearTargets(start);
+ clearTargets(end);
+ }
+
+ private static void assertNoTargets(TransitionFragment fragment) {
+ assertTrue(fragment.enterTransition.getTargets().isEmpty());
+ assertTrue(fragment.reenterTransition.getTargets().isEmpty());
+ assertTrue(fragment.exitTransition.getTargets().isEmpty());
+ assertTrue(fragment.returnTransition.getTargets().isEmpty());
+ assertTrue(fragment.sharedElementEnter.getTargets().isEmpty());
+ assertTrue(fragment.sharedElementReturn.getTargets().isEmpty());
+ }
+
+ public static class PostponedFragment1 extends TransitionFragment {
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ postponeEnterTransition();
+ return inflater.inflate(R.layout.scene1, container, false);
+ }
+ }
+
+ public static class PostponedFragment2 extends TransitionFragment {
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ postponeEnterTransition();
+ return inflater.inflate(R.layout.scene2, container, false);
+ }
+ }
+}
diff --git a/fragment/tests/java/android/support/v4/app/StrictFragment.java b/fragment/tests/java/android/support/v4/app/StrictFragment.java
index dfda814..7e57745 100644
--- a/fragment/tests/java/android/support/v4/app/StrictFragment.java
+++ b/fragment/tests/java/android/support/v4/app/StrictFragment.java
@@ -139,7 +139,10 @@
super.onSaveInstanceState(outState);
mCalledOnSaveInstanceState = true;
checkGetActivity();
- checkStateAtLeast("onSaveInstanceState", STARTED);
+ // FIXME: We should not allow onSaveInstanceState except when STARTED or greater.
+ // But FragmentManager currently does it in saveAllState for fragments on the
+ // back stack, so fragments may be in the CREATED state.
+ checkStateAtLeast("onSaveInstanceState", CREATED);
}
@Override
diff --git a/fragment/tests/java/android/support/v4/app/StrictViewFragment.java b/fragment/tests/java/android/support/v4/app/StrictViewFragment.java
index e0bdf33..ecaa350 100644
--- a/fragment/tests/java/android/support/v4/app/StrictViewFragment.java
+++ b/fragment/tests/java/android/support/v4/app/StrictViewFragment.java
@@ -17,21 +17,32 @@
package android.support.v4.app;
-import android.support.fragment.test.R;
import android.os.Bundle;
+import android.support.fragment.test.R;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class StrictViewFragment extends StrictFragment {
boolean mOnCreateViewCalled, mOnViewCreatedCalled, mOnDestroyViewCalled;
+ int mLayoutId = R.layout.strict_view_fragment;
+
+ public void setLayoutId(int layoutId) {
+ mLayoutId = layoutId;
+ }
+
+ public static StrictViewFragment create(int layoutId) {
+ StrictViewFragment fragment = new StrictViewFragment();
+ fragment.mLayoutId = layoutId;
+ return fragment;
+ }
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
checkGetActivity();
checkState("onCreateView", CREATED);
- final View result = inflater.inflate(R.layout.strict_view_fragment, container, false);
+ final View result = inflater.inflate(mLayoutId, container, false);
mOnCreateViewCalled = true;
return result;
}
diff --git a/fragment/tests/java/android/support/v4/app/TargetTracking.java b/fragment/tests/java/android/support/v4/app/TargetTracking.java
new file mode 100644
index 0000000..2639ff8
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/TargetTracking.java
@@ -0,0 +1,27 @@
+/*
+ * 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.v4.app;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import java.util.ArrayList;
+
+public interface TargetTracking {
+ ArrayList<View> getTrackedTargets();
+ void clearTargets();
+ Rect getCapturedEpicenter();
+}
diff --git a/fragment/tests/java/android/support/v4/app/TrackingTransition.java b/fragment/tests/java/android/support/v4/app/TrackingTransition.java
new file mode 100644
index 0000000..87f5245
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/TrackingTransition.java
@@ -0,0 +1,91 @@
+/*
+ * 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.v4.app;
+
+import android.animation.Animator;
+import android.graphics.Rect;
+import android.transition.Transition;
+import android.transition.TransitionValues;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * A transition that tracks which targets are applied to it.
+ * It will assume any target that it applies to will have differences
+ * between the start and end state, regardless of the differences
+ * that actually exist. In other words, it doesn't actually check
+ * any size or position differences or any other property of the view.
+ * It just records the difference.
+ * <p>
+ * Both start and end value Views are recorded, but no actual animation
+ * is created.
+ */
+class TrackingTransition extends Transition implements TargetTracking {
+ public final ArrayList<View> targets = new ArrayList<>();
+ private final Rect[] mEpicenter = new Rect[1];
+ private static final String PROP = "tracking:prop";
+ private static final String[] PROPS = { PROP };
+
+ @Override
+ public String[] getTransitionProperties() {
+ return PROPS;
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ transitionValues.values.put(PROP, 0);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ transitionValues.values.put(PROP, 1);
+ }
+
+ @Override
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues != null) {
+ targets.add(startValues.view);
+ }
+ if (endValues != null) {
+ targets.add(endValues.view);
+ }
+ Rect epicenter = getEpicenter();
+ if (epicenter != null) {
+ mEpicenter[0] = new Rect(epicenter);
+ } else {
+ mEpicenter[0] = null;
+ }
+ return null;
+ }
+
+ @Override
+ public ArrayList<View> getTrackedTargets() {
+ return targets;
+ }
+
+ @Override
+ public void clearTargets() {
+ targets.clear();
+ }
+
+ @Override
+ public Rect getCapturedEpicenter() {
+ return mEpicenter[0];
+ }
+}
diff --git a/fragment/tests/java/android/support/v4/app/TrackingVisibility.java b/fragment/tests/java/android/support/v4/app/TrackingVisibility.java
new file mode 100644
index 0000000..b302d7e
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/TrackingVisibility.java
@@ -0,0 +1,75 @@
+/*
+ * 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.v4.app;
+
+import android.animation.Animator;
+import android.graphics.Rect;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * Visibility transition that tracks which targets are applied to it.
+ * This transition does no animation.
+ */
+class TrackingVisibility extends Visibility implements TargetTracking {
+ public final ArrayList<View> targets = new ArrayList<>();
+ private final Rect[] mEpicenter = new Rect[1];
+
+ @Override
+ public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+ TransitionValues endValues) {
+ targets.add(endValues.view);
+ Rect epicenter = getEpicenter();
+ if (epicenter != null) {
+ mEpicenter[0] = new Rect(epicenter);
+ } else {
+ mEpicenter[0] = null;
+ }
+ return null;
+ }
+
+ @Override
+ public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+ TransitionValues endValues) {
+ targets.add(startValues.view);
+ Rect epicenter = getEpicenter();
+ if (epicenter != null) {
+ mEpicenter[0] = new Rect(epicenter);
+ } else {
+ mEpicenter[0] = null;
+ }
+ return null;
+ }
+
+ @Override
+ public ArrayList<View> getTrackedTargets() {
+ return targets;
+ }
+
+ @Override
+ public void clearTargets() {
+ targets.clear();
+ }
+
+ @Override
+ public Rect getCapturedEpicenter() {
+ return mEpicenter[0];
+ }
+}
diff --git a/fragment/tests/java/android/support/v4/app/TransitionFragment.java b/fragment/tests/java/android/support/v4/app/TransitionFragment.java
new file mode 100644
index 0000000..fda2784
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/TransitionFragment.java
@@ -0,0 +1,77 @@
+/*
+ * 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.v4.app;
+
+import static android.support.v4.app.CtsMockitoUtils.within;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.transition.Transition;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A fragment that has transitions that can be tracked.
+ */
+public class TransitionFragment extends StrictViewFragment {
+ public final TrackingVisibility enterTransition = new TrackingVisibility();
+ public final TrackingVisibility reenterTransition = new TrackingVisibility();
+ public final TrackingVisibility exitTransition = new TrackingVisibility();
+ public final TrackingVisibility returnTransition = new TrackingVisibility();
+ public final TrackingTransition sharedElementEnter = new TrackingTransition();
+ public final TrackingTransition sharedElementReturn = new TrackingTransition();
+
+ private Transition.TransitionListener mListener = mock(Transition.TransitionListener.class);
+
+ public TransitionFragment() {
+ setEnterTransition(enterTransition);
+ setReenterTransition(reenterTransition);
+ setExitTransition(exitTransition);
+ setReturnTransition(returnTransition);
+ setSharedElementEnterTransition(sharedElementEnter);
+ setSharedElementReturnTransition(sharedElementReturn);
+ enterTransition.addListener(mListener);
+ reenterTransition.addListener(mListener);
+ exitTransition.addListener(mListener);
+ returnTransition.addListener(mListener);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ checkGetActivity();
+ checkState("onCreateView", CREATED);
+ mOnCreateViewCalled = true;
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ void waitForTransition() throws InterruptedException {
+ verify(mListener, within(300)).onTransitionEnd((Transition) any());
+ reset(mListener);
+ }
+
+ void waitForNoTransition() throws InterruptedException {
+ SystemClock.sleep(250);
+ verify(mListener, never()).onTransitionStart((Transition) any());
+ }
+}
diff --git a/fragment/tests/res/layout/double_container.xml b/fragment/tests/res/layout/double_container.xml
new file mode 100644
index 0000000..c24d5ce
--- /dev/null
+++ b/fragment/tests/res/layout/double_container.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <FrameLayout
+ android:id="@+id/fragmentContainer1"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_height="0px"/>
+ <FrameLayout
+ android:id="@+id/fragmentContainer2"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_height="0px"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/fragment/tests/res/layout/scene1.xml b/fragment/tests/res/layout/scene1.xml
new file mode 100644
index 0000000..d0509c3
--- /dev/null
+++ b/fragment/tests/res/layout/scene1.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/squareContainer"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <View android:id="@+id/greenSquare"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:transitionName="greenSquare"
+ android:background="#080"/>
+ <View android:id="@+id/blueSquare"
+ android:transitionName="blueSquare"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#008"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/fragment/tests/res/layout/scene2.xml b/fragment/tests/res/layout/scene2.xml
new file mode 100644
index 0000000..ef809a4
--- /dev/null
+++ b/fragment/tests/res/layout/scene2.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/squareContainer"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <View android:id="@+id/blueSquare"
+ android:transitionName="blueSquare"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#008"/>
+ <View android:id="@+id/greenSquare"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:transitionName="greenSquare"
+ android:background="#080"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/fragment/tests/res/layout/scene3.xml b/fragment/tests/res/layout/scene3.xml
new file mode 100644
index 0000000..15519fd
--- /dev/null
+++ b/fragment/tests/res/layout/scene3.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/squareContainer"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <View android:id="@+id/redSquare"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#800"/>
+ <View android:id="@+id/blueSquare"
+ android:transitionName="shared"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#008"/>
+ <View android:id="@+id/greenSquare"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:transitionName="greenSquare"
+ android:background="#080"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/documents-archive/AndroidManifest.xml b/fragment/tests/res/layout/simple_container.xml
similarity index 61%
copy from documents-archive/AndroidManifest.xml
copy to fragment/tests/res/layout/simple_container.xml
index 2cd0f7a..f231c1e 100644
--- a/documents-archive/AndroidManifest.xml
+++ b/fragment/tests/res/layout/simple_container.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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
+ 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,
@@ -13,8 +13,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.support.provider">
- <uses-sdk android:minSdkVersion="19"/>
- <application />
-</manifest>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/fragmentContainer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+</FrameLayout>
\ No newline at end of file
diff --git a/documents-archive/AndroidManifest.xml b/fragment/tests/res/layout/text_a.xml
similarity index 64%
rename from documents-archive/AndroidManifest.xml
rename to fragment/tests/res/layout/text_a.xml
index 2cd0f7a..b164469 100644
--- a/documents-archive/AndroidManifest.xml
+++ b/fragment/tests/res/layout/text_a.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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
+ 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,
@@ -13,8 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.support.provider">
- <uses-sdk android:minSdkVersion="19"/>
- <application />
-</manifest>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/textA"/>
diff --git a/documents-archive/AndroidManifest.xml b/fragment/tests/res/layout/text_b.xml
similarity index 64%
copy from documents-archive/AndroidManifest.xml
copy to fragment/tests/res/layout/text_b.xml
index 2cd0f7a..3d307fc 100644
--- a/documents-archive/AndroidManifest.xml
+++ b/fragment/tests/res/layout/text_b.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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
+ 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,
@@ -13,8 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.support.provider">
- <uses-sdk android:minSdkVersion="19"/>
- <application />
-</manifest>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/textB"/>
diff --git a/documents-archive/AndroidManifest.xml b/fragment/tests/res/layout/text_c.xml
similarity index 64%
copy from documents-archive/AndroidManifest.xml
copy to fragment/tests/res/layout/text_c.xml
index 2cd0f7a..e40b5ab 100644
--- a/documents-archive/AndroidManifest.xml
+++ b/fragment/tests/res/layout/text_c.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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
+ 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,
@@ -13,8 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.support.provider">
- <uses-sdk android:minSdkVersion="19"/>
- <application />
-</manifest>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/textC"/>
diff --git a/graphics/drawable/Android.mk b/graphics/drawable/Android.mk
index 61039fb..f58493b 100644
--- a/graphics/drawable/Android.mk
+++ b/graphics/drawable/Android.mk
@@ -20,13 +20,17 @@
#
# ---------------------------------------------
include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-vectordrawable
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under, static/src)
-
-LOCAL_JAVA_LIBRARIES := android-support-compat
-
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/static/res
+LOCAL_MANIFEST_FILE := static/AndroidManifest.xml
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-compat \
+ android-support-annotations
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
# ---------------------------------------------
@@ -35,12 +39,16 @@
#
# ---------------------------------------------
include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-animatedvectordrawable
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under, animated/src)
-
-LOCAL_JAVA_LIBRARIES := android-support-compat android-support-vectordrawable
-
-LOCAL_AAPT_FLAGS := --no-version-vectors
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/animated/res
+LOCAL_MANIFEST_FILE := animated/AndroidManifest.xml
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-compat \
+ android-support-vectordrawable \
+ android-support-annotations
+LOCAL_AAPT_FLAGS := --no-version-vectors --add-javadoc-annotation doconly
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/graphics/drawable/animated/api/current.txt b/graphics/drawable/animated/api/current.txt
deleted file mode 100644
index 1461956..0000000
--- a/graphics/drawable/animated/api/current.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-package android.support.graphics.drawable {
-
- public class AnimatedVectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon {
- method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat create(android.content.Context, int);
- method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method public void draw(android.graphics.Canvas);
- method public int getOpacity();
- method public boolean isRunning();
- method public void setAlpha(int);
- method public void setColorFilter(android.graphics.ColorFilter);
- method public void start();
- method public void stop();
- }
-
- abstract class VectorDrawableCommon extends android.graphics.drawable.Drawable {
- }
-
-}
-
diff --git a/graphics/drawable/animated/api/removed.txt b/graphics/drawable/animated/api/removed.txt
deleted file mode 100644
index e69de29..0000000
--- a/graphics/drawable/animated/api/removed.txt
+++ /dev/null
diff --git a/graphics/drawable/animated/build.gradle b/graphics/drawable/animated/build.gradle
index df11178..31cb13d 100644
--- a/graphics/drawable/animated/build.gradle
+++ b/graphics/drawable/animated/build.gradle
@@ -52,6 +52,10 @@
testOptions {
unitTests.returnDefaultValues = true
}
+
+ buildTypes.all {
+ consumerProguardFiles 'proguard-rules.pro'
+ }
}
android.libraryVariants.all { variant ->
diff --git a/graphics/drawable/animated/proguard-rules.pro b/graphics/drawable/animated/proguard-rules.pro
new file mode 100644
index 0000000..4695a39
--- /dev/null
+++ b/graphics/drawable/animated/proguard-rules.pro
@@ -0,0 +1,19 @@
+# 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.
+
+# keep setters in VectorDrawables so that animations can still work.
+-keepclassmembers class android.support.graphics.drawable.VectorDrawableCompat$* {
+ void set*(***);
+ *** get*();
+}
diff --git a/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java b/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
index 0b0522e..b0b5fa1 100644
--- a/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
+++ b/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
@@ -19,7 +19,6 @@
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -62,7 +61,6 @@
* API. In order to refer to AnimatedVectorDrawableCompat inside a XML file, you can use
* app:srcCompat attribute in AppCompat library's ImageButton or ImageView.
*/
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class AnimatedVectorDrawableCompat extends VectorDrawableCommon implements Animatable {
private static final String LOGTAG = "AnimatedVDCompat";
@@ -339,18 +337,6 @@
mAnimatedVectorState.mVectorDrawable.setAutoMirrored(mirrored);
}
- /**
- * Obtains styled attributes from the theme, if available, or unstyled
- * resources if the theme is null.
- */
- static TypedArray obtainAttributes(
- Resources res, Theme theme, AttributeSet set, int[] attrs) {
- if (theme == null) {
- return res.obtainAttributes(set, attrs);
- }
- return theme.obtainStyledAttributes(set, attrs, 0, 0);
- }
-
@Override
public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
@@ -371,7 +357,7 @@
}
if (ANIMATED_VECTOR.equals(tagName)) {
final TypedArray a =
- obtainAttributes(res, theme, attrs,
+ VectorDrawableCommon.obtainAttributes(res, theme, attrs,
AndroidResources.styleable_AnimatedVectorDrawable);
int drawableRes = a.getResourceId(
diff --git a/graphics/drawable/static/api/current.txt b/graphics/drawable/static/api/current.txt
deleted file mode 100644
index db07bf2..0000000
--- a/graphics/drawable/static/api/current.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-package android.support.graphics.drawable {
-
- abstract class VectorDrawableCommon extends android.graphics.drawable.Drawable {
- }
-
- public class VectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon {
- method public static android.support.graphics.drawable.VectorDrawableCompat create(android.content.res.Resources, int, android.content.res.Resources.Theme);
- method public static android.support.graphics.drawable.VectorDrawableCompat createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method public void draw(android.graphics.Canvas);
- method public int getOpacity();
- method public void setAlpha(int);
- method public void setColorFilter(android.graphics.ColorFilter);
- }
-
-}
-
diff --git a/graphics/drawable/static/api/removed.txt b/graphics/drawable/static/api/removed.txt
deleted file mode 100644
index e69de29..0000000
--- a/graphics/drawable/static/api/removed.txt
+++ /dev/null
diff --git a/graphics/drawable/static/build.gradle b/graphics/drawable/static/build.gradle
index 2564a27..24d0d50 100644
--- a/graphics/drawable/static/build.gradle
+++ b/graphics/drawable/static/build.gradle
@@ -1,8 +1,8 @@
apply plugin: 'com.android.library'
-
archivesBaseName = 'support-vector-drawable'
dependencies {
+ compile project(':support-annotations')
compile project(':support-compat')
androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
exclude module: 'support-annotations'
@@ -14,11 +14,12 @@
}
android {
- compileSdkVersion 23
+ compileSdkVersion project.ext.currentSdk
defaultConfig {
minSdkVersion 9
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
// This disables the builds tools automatic vector -> PNG generation
generatedDensities = []
}
diff --git a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java
index bcabd99..12b0383 100644
--- a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java
+++ b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java
@@ -14,7 +14,6 @@
package android.support.graphics.drawable;
-import android.annotation.TargetApi;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.ColorFilter;
@@ -30,13 +29,14 @@
/**
* Internal common delegation shared by VectorDrawableCompat and AnimatedVectorDrawableCompat
*/
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
abstract class VectorDrawableCommon extends Drawable implements TintAwareDrawable {
/**
* Obtains styled attributes from the theme, if available, or unstyled
* resources if the theme is null.
+ *
+ * @hide
*/
- static TypedArray obtainAttributes(
+ protected static TypedArray obtainAttributes(
Resources res, Resources.Theme theme, AttributeSet set, int[] attrs) {
if (theme == null) {
return res.obtainAttributes(set, attrs);
diff --git a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
index f4d1198..8c93919 100644
--- a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
+++ b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
@@ -16,7 +16,6 @@
import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-import android.annotation.TargetApi;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -52,9 +51,6 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.Stack;
@@ -221,8 +217,6 @@
* </dl></dd>
* </dl>
*/
-
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class VectorDrawableCompat extends VectorDrawableCommon {
static final String LOGTAG = "VectorDrawableCompat";
@@ -678,7 +672,11 @@
case 15:
return Mode.SCREEN;
case 16:
- return Mode.ADD;
+ if (Build.VERSION.SDK_INT >= 11) {
+ return Mode.ADD;
+ } else {
+ return defaultMode;
+ }
default:
return defaultMode;
}
diff --git a/media-compat/Android.mk b/media-compat/Android.mk
index d1c2569..da9686c 100644
--- a/media-compat/Android.mk
+++ b/media-compat/Android.mk
@@ -14,109 +14,35 @@
LOCAL_PATH := $(call my-dir)
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Ice Cream Sandwich APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-ics
-LOCAL_SDK_VERSION := 14
-LOCAL_SRC_FILES := $(call all-java-files-under, ics)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-annotations android-support-compat
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JellyBean MR2 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-jellybean-mr2
-LOCAL_SDK_VERSION := 18
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr2)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-media-compat-ics
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of KitKat APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-kitkat
-LOCAL_SDK_VERSION := 19
-LOCAL_SRC_FILES := $(call all-java-files-under, kitkat)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-media-compat-jellybean-mr2
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Lollipop APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-media-compat-kitkat
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V22 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-api22
-LOCAL_SDK_VERSION := 22
-LOCAL_SRC_FILES := $(call all-java-files-under, api22)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-media-compat-api21
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V23 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-api23
-LOCAL_SDK_VERSION := 23
-LOCAL_SRC_FILES := $(call all-java-files-under, api23)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-media-compat-api22
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V24 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-api24
-LOCAL_SDK_VERSION := 24
-LOCAL_SRC_FILES := $(call all-java-files-under, api24)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-media-compat-api23
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V25 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-api25
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, api25)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-media-compat-api24
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
# Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+# LOCAL_STATIC_ANDROID_LIBRARIES := \
+# android-support-media-compat \
+# android-support-compat \
+#
+# in their makefiles to include the resources and their dependencies in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-media-compat
-LOCAL_SDK_VERSION := 9
-LOCAL_AIDL_INCLUDES := frameworks/support/media-compat/java
-LOCAL_SRC_FILES := $(call all-java-files-under, java) \
- $(call all-Iaidl-files-under, java)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/java
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under,ics) \
+ $(call all-java-files-under,jellybean-mr2) \
+ $(call all-java-files-under,kitkat) \
+ $(call all-java-files-under,api21) \
+ $(call all-java-files-under,api22) \
+ $(call all-java-files-under,api23) \
+ $(call all-java-files-under,api24) \
+ $(call all-java-files-under,api25) \
+ $(call all-java-files-under,java) \
+ $(call all-Iaidl-files-under,java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-media-compat-api25
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-compat \
android-support-annotations
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/media-compat/api21/android/support/v4/media/MediaBrowserCompatApi21.java b/media-compat/api21/android/support/v4/media/MediaBrowserCompatApi21.java
index 1a17dae..9a0b0f3 100644
--- a/media-compat/api21/android/support/v4/media/MediaBrowserCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/MediaBrowserCompatApi21.java
@@ -16,15 +16,18 @@
package android.support.v4.media;
+import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
import android.media.browse.MediaBrowser;
import android.os.Bundle;
import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
-import java.util.ArrayList;
import java.util.List;
+@RequiresApi(21)
+@TargetApi(21)
class MediaBrowserCompatApi21 {
static final String NULL_MEDIA_ITEM_ID =
"android.support.v4.media.MediaBrowserCompat.NULL_MEDIA_ITEM";
diff --git a/media-compat/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java b/media-compat/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
index 2a7eaf3..affb130 100644
--- a/media-compat/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
@@ -16,6 +16,7 @@
package android.support.v4.media;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.media.browse.MediaBrowser;
@@ -24,10 +25,13 @@
import android.os.IBinder;
import android.os.Parcel;
import android.service.media.MediaBrowserService;
+import android.support.annotation.RequiresApi;
import java.util.ArrayList;
import java.util.List;
+@RequiresApi(21)
+@TargetApi(21)
class MediaBrowserServiceCompatApi21 {
public static Object createService(Context context, ServiceCompatProxy serviceProxy) {
diff --git a/media-compat/api21/android/support/v4/media/MediaDescriptionCompatApi21.java b/media-compat/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
index 234a77a..556d092 100644
--- a/media-compat/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
@@ -15,12 +15,16 @@
*/
package android.support.v4.media;
+import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.media.MediaDescription;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
+import android.support.annotation.RequiresApi;
+@RequiresApi(21)
+@TargetApi(21)
class MediaDescriptionCompatApi21 {
public static String getMediaId(Object descriptionObj) {
diff --git a/media-compat/api21/android/support/v4/media/MediaMetadataCompatApi21.java b/media-compat/api21/android/support/v4/media/MediaMetadataCompatApi21.java
index fd51f78..ed30c29 100644
--- a/media-compat/api21/android/support/v4/media/MediaMetadataCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/MediaMetadataCompatApi21.java
@@ -16,13 +16,17 @@
package android.support.v4.media;
+import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.media.MediaMetadata;
import android.media.Rating;
import android.os.Parcel;
+import android.support.annotation.RequiresApi;
import java.util.Set;
+@RequiresApi(21)
+@TargetApi(21)
class MediaMetadataCompatApi21 {
public static Set<String> keySet(Object metadataObj) {
return ((MediaMetadata)metadataObj).keySet();
diff --git a/media-compat/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java b/media-compat/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
index 9292b93..ab5e4ef 100644
--- a/media-compat/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
+++ b/media-compat/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
@@ -16,7 +16,9 @@
package android.support.v4.media;
+import android.annotation.TargetApi;
import android.media.browse.MediaBrowser;
+import android.support.annotation.RequiresApi;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -25,6 +27,8 @@
/**
* An adapter class for accessing the hidden framework classes, ParceledListSlice using reflection.
*/
+@RequiresApi(21)
+@TargetApi(21)
class ParceledListSliceAdapterApi21 {
private static Constructor sConstructor;
static {
diff --git a/media-compat/api21/android/support/v4/media/VolumeProviderCompatApi21.java b/media-compat/api21/android/support/v4/media/VolumeProviderCompatApi21.java
index c0f21c5..66f5144 100644
--- a/media-compat/api21/android/support/v4/media/VolumeProviderCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/VolumeProviderCompatApi21.java
@@ -16,8 +16,12 @@
package android.support.v4.media;
+import android.annotation.TargetApi;
import android.media.VolumeProvider;
+import android.support.annotation.RequiresApi;
+@RequiresApi(21)
+@TargetApi(21)
class VolumeProviderCompatApi21 {
public static Object createVolumeProvider(int volumeControl, int maxVolume, int currentVolume,
final Delegate delegate) {
diff --git a/media-compat/api21/android/support/v4/media/session/MediaControllerCompatApi21.java b/media-compat/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
index 8de40dc..9b00e8c 100644
--- a/media-compat/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
@@ -16,6 +16,7 @@
package android.support.v4.media.session;
+import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.content.Context;
import android.media.AudioAttributes;
@@ -27,13 +28,15 @@
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Parcel;
import android.os.ResultReceiver;
+import android.support.annotation.RequiresApi;
import android.view.KeyEvent;
import java.util.ArrayList;
import java.util.List;
+@RequiresApi(21)
+@TargetApi(21)
class MediaControllerCompatApi21 {
public static Object fromToken(Context context, Object sessionToken) {
return new MediaController(context, (MediaSession.Token) sessionToken);
diff --git a/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java b/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
index 1b3f8fd..38c42cb 100644
--- a/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
@@ -16,6 +16,7 @@
package android.support.v4.media.session;
+import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -30,10 +31,13 @@
import android.os.Handler;
import android.os.Parcelable;
import android.os.ResultReceiver;
+import android.support.annotation.RequiresApi;
import java.util.ArrayList;
import java.util.List;
+@RequiresApi(21)
+@TargetApi(21)
class MediaSessionCompatApi21 {
public static Object createSession(Context context, String tag) {
return new MediaSession(context, tag);
diff --git a/media-compat/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java b/media-compat/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
index 09d3e32..df6a203 100644
--- a/media-compat/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
@@ -16,13 +16,15 @@
package android.support.v4.media.session;
+import android.annotation.TargetApi;
import android.media.session.PlaybackState;
import android.os.Bundle;
-import android.os.SystemClock;
+import android.support.annotation.RequiresApi;
-import java.util.ArrayList;
import java.util.List;
+@RequiresApi(21)
+@TargetApi(21)
class PlaybackStateCompatApi21 {
public static int getState(Object stateObj) {
return ((PlaybackState)stateObj).getState();
diff --git a/media-compat/api22/android/support/v4/media/session/MediaSessionCompatApi22.java b/media-compat/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
index b847778..687e965 100644
--- a/media-compat/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
+++ b/media-compat/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
@@ -15,8 +15,12 @@
*/
package android.support.v4.media.session;
+import android.annotation.TargetApi;
import android.media.session.MediaSession;
+import android.support.annotation.RequiresApi;
+@RequiresApi(22)
+@TargetApi(22)
class MediaSessionCompatApi22 {
public static void setRatingType(Object sessionObj, int type) {
diff --git a/media-compat/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java b/media-compat/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
index 55d4b83..ff398b9 100644
--- a/media-compat/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
+++ b/media-compat/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
@@ -16,12 +16,15 @@
package android.support.v4.media.session;
+import android.annotation.TargetApi;
import android.media.session.PlaybackState;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
-import java.util.ArrayList;
import java.util.List;
+@RequiresApi(22)
+@TargetApi(22)
class PlaybackStateCompatApi22 {
public static Bundle getExtras(Object stateObj) {
return ((PlaybackState)stateObj).getExtras();
diff --git a/media-compat/api23/android/support/v4/media/MediaBrowserCompatApi23.java b/media-compat/api23/android/support/v4/media/MediaBrowserCompatApi23.java
index 1e9df1a..308c490 100644
--- a/media-compat/api23/android/support/v4/media/MediaBrowserCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/MediaBrowserCompatApi23.java
@@ -16,10 +16,14 @@
package android.support.v4.media;
+import android.annotation.TargetApi;
import android.media.browse.MediaBrowser;
import android.os.Parcel;
import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+@RequiresApi(23)
+@TargetApi(23)
class MediaBrowserCompatApi23 {
public static Object createItemCallback(ItemCallback callback) {
diff --git a/media-compat/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java b/media-compat/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java
index 4eab9c2..1091dec 100644
--- a/media-compat/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java
@@ -16,11 +16,15 @@
package android.support.v4.media;
+import android.annotation.TargetApi;
import android.content.Context;
import android.media.browse.MediaBrowser;
import android.os.Parcel;
+import android.support.annotation.RequiresApi;
import android.support.v4.media.MediaBrowserServiceCompatApi21.ResultWrapper;
+@RequiresApi(23)
+@TargetApi(23)
class MediaBrowserServiceCompatApi23 {
public static Object createService(Context context, ServiceCompatProxy serviceProxy) {
diff --git a/media-compat/api23/android/support/v4/media/MediaDescriptionCompatApi23.java b/media-compat/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
index 2c1bda3..862fbbf 100644
--- a/media-compat/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
@@ -15,9 +15,13 @@
*/
package android.support.v4.media;
+import android.annotation.TargetApi;
import android.media.MediaDescription;
import android.net.Uri;
+import android.support.annotation.RequiresApi;
+@RequiresApi(23)
+@TargetApi(23)
class MediaDescriptionCompatApi23 extends MediaDescriptionCompatApi21 {
public static Uri getMediaUri(Object descriptionObj) {
return ((MediaDescription) descriptionObj).getMediaUri();
diff --git a/media-compat/api23/android/support/v4/media/session/MediaControllerCompatApi23.java b/media-compat/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
index d7e6669..92e49fc 100644
--- a/media-compat/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
@@ -16,10 +16,14 @@
package android.support.v4.media.session;
+import android.annotation.TargetApi;
import android.media.session.MediaController;
import android.net.Uri;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+@RequiresApi(23)
+@TargetApi(23)
class MediaControllerCompatApi23 {
public static class TransportControls extends MediaControllerCompatApi21.TransportControls {
diff --git a/media-compat/api23/android/support/v4/media/session/MediaSessionCompatApi23.java b/media-compat/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
index cc8bba3..ddb25fc 100644
--- a/media-compat/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
@@ -16,9 +16,13 @@
package android.support.v4.media.session;
+import android.annotation.TargetApi;
import android.net.Uri;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+@RequiresApi(23)
+@TargetApi(23)
class MediaSessionCompatApi23 {
public static Object createCallback(Callback callback) {
diff --git a/media-compat/api24/android/support/v4/media/MediaBrowserCompatApi24.java b/media-compat/api24/android/support/v4/media/MediaBrowserCompatApi24.java
index 1e364a6..45a428c 100644
--- a/media-compat/api24/android/support/v4/media/MediaBrowserCompatApi24.java
+++ b/media-compat/api24/android/support/v4/media/MediaBrowserCompatApi24.java
@@ -16,12 +16,16 @@
package android.support.v4.media;
+import android.annotation.TargetApi;
import android.media.browse.MediaBrowser;
import android.os.Bundle;
import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
import java.util.List;
+@RequiresApi(24)
+@TargetApi(24)
class MediaBrowserCompatApi24 {
public static Object createSubscriptionCallback(SubscriptionCallback callback) {
return new SubscriptionCallbackProxy<>(callback);
diff --git a/media-compat/api24/android/support/v4/media/MediaBrowserServiceCompatApi24.java b/media-compat/api24/android/support/v4/media/MediaBrowserServiceCompatApi24.java
index a1a4f48..2440864 100644
--- a/media-compat/api24/android/support/v4/media/MediaBrowserServiceCompatApi24.java
+++ b/media-compat/api24/android/support/v4/media/MediaBrowserServiceCompatApi24.java
@@ -16,17 +16,21 @@
package android.support.v4.media;
+import android.annotation.TargetApi;
import android.content.Context;
import android.media.browse.MediaBrowser;
import android.os.Bundle;
import android.os.Parcel;
import android.service.media.MediaBrowserService;
+import android.support.annotation.RequiresApi;
import android.util.Log;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
+@RequiresApi(24)
+@TargetApi(24)
class MediaBrowserServiceCompatApi24 {
private static final String TAG = "MBSCompatApi24";
diff --git a/media-compat/api24/android/support/v4/media/session/MediaControllerCompatApi24.java b/media-compat/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
index 04bf843..3c8b650 100644
--- a/media-compat/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
+++ b/media-compat/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
@@ -16,10 +16,14 @@
package android.support.v4.media.session;
+import android.annotation.TargetApi;
import android.media.session.MediaController;
import android.net.Uri;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+@RequiresApi(24)
+@TargetApi(24)
class MediaControllerCompatApi24 {
public static class TransportControls extends MediaControllerCompatApi23.TransportControls {
diff --git a/media-compat/api24/android/support/v4/media/session/MediaSessionCompatApi24.java b/media-compat/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
index e03a3ee..506b04f 100644
--- a/media-compat/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
+++ b/media-compat/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
@@ -16,14 +16,18 @@
package android.support.v4.media.session;
+import android.annotation.TargetApi;
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+@RequiresApi(24)
+@TargetApi(24)
class MediaSessionCompatApi24 {
private static final String TAG = "MediaSessionCompatApi24";
diff --git a/media-compat/build.gradle b/media-compat/build.gradle
index 3e547b3..4b569a4 100644
--- a/media-compat/build.gradle
+++ b/media-compat/build.gradle
@@ -1,9 +1,8 @@
apply plugin: 'com.android.library'
archivesBaseName = 'support-media-compat'
-
-createApiSourceSets(project, gradle.ext.studioCompat.modules.mediacompat.apiTargets)
dependencies {
+ compile project(':support-annotations')
compile project(':support-compat')
androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
exclude module: 'support-annotations'
@@ -18,20 +17,27 @@
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
-setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.mediacompat.dependencies)
android {
- compileSdkVersion 9
+ compileSdkVersion project.ext.currentSdk
defaultConfig {
minSdkVersion 9
- // TODO: get target from branch
- //targetSdkVersion 19
}
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = ['java']
+ main.java.srcDirs = [
+ 'ics',
+ 'jellybean-mr2',
+ 'kitkat',
+ 'api21',
+ 'api22',
+ 'api23',
+ 'api24',
+ 'api25',
+ 'java'
+ ]
main.aidl.srcDirs = ['java']
}
@@ -77,11 +83,6 @@
exclude('android/service/media/**')
}
- project.ext.allSS.each { ss ->
- javadocTask.source ss.java
- sourcesJarTask.from ss.java.srcDirs
- }
-
artifacts.add('archives', javadocJarTask);
artifacts.add('archives', sourcesJarTask);
}
diff --git a/media-compat/ics/android/support/v4/media/session/MediaSessionCompatApi14.java b/media-compat/ics/android/support/v4/media/session/MediaSessionCompatApi14.java
index a0e0e7d..b6f7a38 100644
--- a/media-compat/ics/android/support/v4/media/session/MediaSessionCompatApi14.java
+++ b/media-compat/ics/android/support/v4/media/session/MediaSessionCompatApi14.java
@@ -15,16 +15,18 @@
*/
package android.support.v4.media.session;
+import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.content.Context;
-import android.content.Intent;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.MediaMetadataRetriever;
import android.media.RemoteControlClient;
import android.os.Bundle;
-import android.os.ResultReceiver;
+import android.support.annotation.RequiresApi;
+@RequiresApi(14)
+@TargetApi(14)
class MediaSessionCompatApi14 {
/***** RemoteControlClient States, we only need none as the others were public *******/
final static int RCC_PLAYSTATE_NONE = 0;
diff --git a/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorCallback.java b/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorCallback.java
index 7dae7dc..4abf568 100644
--- a/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorCallback.java
+++ b/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorCallback.java
@@ -16,8 +16,12 @@
package android.support.v4.media;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.view.KeyEvent;
+@RequiresApi(18)
+@TargetApi(18)
interface TransportMediatorCallback {
public void handleKey(KeyEvent key);
public void handleAudioFocusChange(int focusChange);
diff --git a/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java b/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java
index be3555e..aebf7ca 100644
--- a/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java
+++ b/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java
@@ -16,6 +16,7 @@
package android.support.v4.media;
+import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -23,11 +24,14 @@
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.RemoteControlClient;
+import android.support.annotation.RequiresApi;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewTreeObserver;
+@RequiresApi(18)
+@TargetApi(18)
class TransportMediatorJellybeanMR2 {
final Context mContext;
final AudioManager mAudioManager;
diff --git a/media-compat/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java b/media-compat/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java
index 693eb39..3f323a1 100644
--- a/media-compat/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java
+++ b/media-compat/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java
@@ -15,14 +15,18 @@
*/
package android.support.v4.media.session;
+import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.media.AudioManager;
import android.media.RemoteControlClient;
-import android.util.Log;
import android.os.SystemClock;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+@RequiresApi(18)
+@TargetApi(18)
class MediaSessionCompatApi18 {
private static final String TAG = "MediaSessionCompatApi18";
diff --git a/media-compat/kitkat/android/support/v4/media/RatingCompatKitkat.java b/media-compat/kitkat/android/support/v4/media/RatingCompatKitkat.java
index b15c15e..5efdc58 100644
--- a/media-compat/kitkat/android/support/v4/media/RatingCompatKitkat.java
+++ b/media-compat/kitkat/android/support/v4/media/RatingCompatKitkat.java
@@ -16,8 +16,12 @@
package android.support.v4.media;
+import android.annotation.TargetApi;
import android.media.Rating;
+import android.support.annotation.RequiresApi;
+@RequiresApi(19)
+@TargetApi(19)
class RatingCompatKitkat {
public static Object newUnratedRating(int ratingStyle) {
return Rating.newUnratedRating(ratingStyle);
diff --git a/media-compat/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java b/media-compat/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java
index 43c22af..94b446b 100644
--- a/media-compat/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java
+++ b/media-compat/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java
@@ -15,12 +15,16 @@
*/
package android.support.v4.media.session;
+import android.annotation.TargetApi;
import android.media.MediaMetadataEditor;
import android.media.MediaMetadataRetriever;
import android.media.Rating;
import android.media.RemoteControlClient;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+@RequiresApi(19)
+@TargetApi(19)
class MediaSessionCompatApi19 {
/***** PlaybackState actions *****/
private static final long ACTION_SET_RATING = 1 << 7;
diff --git a/percent/Android.mk b/percent/Android.mk
index ecec417..b569224 100644
--- a/percent/Android.mk
+++ b/percent/Android.mk
@@ -24,8 +24,7 @@
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-percent
-LOCAL_SDK_VERSION := 9
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_SHARED_ANDROID_LIBRARIES := android-support-v4
diff --git a/percent/src/android/support/percent/PercentFrameLayout.java b/percent/src/android/support/percent/PercentFrameLayout.java
index 3763d2f..679ffbe 100644
--- a/percent/src/android/support/percent/PercentFrameLayout.java
+++ b/percent/src/android/support/percent/PercentFrameLayout.java
@@ -16,8 +16,10 @@
package android.support.percent;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
+import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -144,7 +146,10 @@
gravity = source.gravity;
}
+ @RequiresApi(19)
+ @TargetApi(19)
public LayoutParams(LayoutParams source) {
+ // The copy constructor used here is only supported on API 19+.
this((FrameLayout.LayoutParams) source);
mPercentLayoutInfo = source.mPercentLayoutInfo;
}
diff --git a/recommendation/Android.mk b/recommendation/Android.mk
index 85b0817..0e0a9d7 100644
--- a/recommendation/Android.mk
+++ b/recommendation/Android.mk
@@ -1,18 +1,37 @@
-LOCAL_PATH:= $(call my-dir)
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# Here is the final static library that apps can link against.
+# Applications that use this library must include it with
+#
+# LOCAL_STATIC_ANDROID_LIBRARIES := \
+# android-support-recommendation \
+# android-support-v4
+#
include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-LOCAL_SDK_VERSION := 21
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-
-LOCAL_JAVA_LIBRARIES := \
- android-support-v4
-
+LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-recommendation
-
+LOCAL_SDK_VERSION := 21
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-v4 \
+ android-support-annotations
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
# ===========================================================
@@ -38,7 +57,7 @@
LOCAL_IS_HOST_MODULE := false
LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := build/tools/droiddoc/templates-sdk
-LOCAL_JAVA_LIBRARIES := $(recommendation.docs.java_libraries)
+LOCAL_SHARED_ANDROID_LIBRARIES := $(recommendation.docs.java_libraries)
LOCAL_DROIDDOC_OPTIONS := \
-offlinemode \
diff --git a/samples/Support7Demos/res/menu/sample_media_router_menu.xml b/samples/Support7Demos/res/menu/sample_media_router_menu.xml
index c93111f..8057fa8 100644
--- a/samples/Support7Demos/res/menu/sample_media_router_menu.xml
+++ b/samples/Support7Demos/res/menu/sample_media_router_menu.xml
@@ -19,6 +19,5 @@
<item android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:showAsAction="always"
- app:actionProviderClass=
- "com.example.android.supportv7.media.SampleMediaRouterActivity$MyMediaRouteActionProvider"/>
+ app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"/>
</menu>
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java
index 375d4a0..bae5362 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java
@@ -28,6 +28,7 @@
import android.os.Environment;
import android.os.Handler;
import android.os.SystemClock;
+import android.support.v4.app.FragmentManager;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
@@ -35,10 +36,12 @@
import android.support.v7.app.MediaRouteControllerDialog;
import android.support.v7.app.MediaRouteControllerDialogFragment;
import android.support.v7.app.MediaRouteDialogFactory;
+import android.support.v7.app.MediaRouteDiscoveryFragment;
import android.support.v7.media.MediaControlIntent;
import android.support.v7.media.MediaItemStatus;
import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
+import android.support.v7.media.MediaRouter.Callback;
import android.support.v7.media.MediaRouter.ProviderInfo;
import android.support.v7.media.MediaRouter.RouteInfo;
import android.util.Log;
@@ -212,7 +215,23 @@
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
.addControlCategory(SampleMediaRouteProvider.CATEGORY_SAMPLE_ROUTE)
.build();
- mMediaRouter.addCallback(mSelector, mMediaRouterCB);
+
+ // Add a fragment to take care of media route discovery.
+ // This fragment automatically adds or removes a callback whenever the activity
+ // is started or stopped.
+ FragmentManager fm = getSupportFragmentManager();
+ DiscoveryFragment fragment = (DiscoveryFragment) fm.findFragmentByTag(
+ DISCOVERY_FRAGMENT_TAG);
+ if (fragment == null) {
+ fragment = new DiscoveryFragment(mMediaRouterCB);
+ fragment.setRouteSelector(mSelector);
+ fm.beginTransaction()
+ .add(fragment, DISCOVERY_FRAGMENT_TAG)
+ .commit();
+ } else {
+ fragment.setCallback(mMediaRouterCB);
+ fragment.setRouteSelector(mSelector);
+ }
// Populate an array adapter with streaming media items.
String[] mediaNames = getResources().getStringArray(R.array.media_names);
@@ -500,8 +519,8 @@
getMenuInflater().inflate(R.menu.sample_media_router_menu, menu);
MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
- MyMediaRouteActionProvider mediaRouteActionProvider =
- (MyMediaRouteActionProvider)MenuItemCompat.getActionProvider(mediaRouteMenuItem);
+ MediaRouteActionProvider mediaRouteActionProvider =
+ (MediaRouteActionProvider) MenuItemCompat.getActionProvider(mediaRouteMenuItem);
mediaRouteActionProvider.setRouteSelector(mSelector);
mediaRouteActionProvider.setDialogFactory(new MediaRouteDialogFactory() {
@Override
@@ -602,6 +621,41 @@
return null;
}
+ /**
+ * Media route discovery fragment.
+ */
+ public static final class DiscoveryFragment extends MediaRouteDiscoveryFragment {
+ private static final String TAG = "DiscoveryFragment";
+ private Callback mCallback;
+
+ public DiscoveryFragment() {
+ mCallback = null;
+ }
+
+ public DiscoveryFragment(Callback cb) {
+ mCallback = cb;
+ }
+
+ public void setCallback(Callback cb) {
+ mCallback = cb;
+ }
+
+ @Override
+ public Callback onCreateCallback() {
+ return mCallback;
+ }
+
+ @Override
+ public int onPrepareCallbackFlags() {
+ // Add the CALLBACK_FLAG_UNFILTERED_EVENTS flag to ensure that we will
+ // observe and log all route events including those that are for routes
+ // that do not match our selector. This is only for demonstration purposes
+ // and should not be needed by most applications.
+ return super.onPrepareCallbackFlags()
+ | MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS;
+ }
+ }
+
private static final class MediaItem {
public final String mName;
public final Uri mUri;
@@ -703,17 +757,6 @@
public static class LightWithDarkActionBar extends SampleMediaRouterActivity {
}
- public static class MyMediaRouteActionProvider extends MediaRouteActionProvider {
- public MyMediaRouteActionProvider(Context context) {
- super(context);
- }
-
- @Override
- public boolean isVisible() {
- return true;
- }
- }
-
public static class ControllerDialogFragment extends MediaRouteControllerDialogFragment {
private MediaRouteControllerDialog mControllerDialog;
private Player mPlayer;
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/NestedRecyclerViewActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/NestedRecyclerViewActivity.java
index a9caf40..88b9992 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/NestedRecyclerViewActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/NestedRecyclerViewActivity.java
@@ -17,6 +17,7 @@
package com.example.android.supportv7.widget;
import android.graphics.Color;
+import android.os.Parcelable;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -47,7 +48,7 @@
static class InnerAdapter extends RecyclerView.Adapter<InnerAdapter.ViewHolder> {
public static class ViewHolder extends RecyclerView.ViewHolder {
- public TextView mTextView;
+ TextView mTextView;
public ViewHolder(TextView itemView) {
super(itemView);
@@ -57,7 +58,7 @@
private String[] mData;
- public InnerAdapter(String[] data) {
+ InnerAdapter(String[] data) {
mData = data;
}
@@ -93,15 +94,17 @@
}
ArrayList<InnerAdapter> mAdapters = new ArrayList<>();
+ ArrayList<Parcelable> mSavedStates = new ArrayList<>();
RecyclerView.RecycledViewPool mSharedPool = new RecyclerView.RecycledViewPool();
- public OuterAdapter(String[] data) {
+ OuterAdapter(String[] data) {
int currentCharIndex = 0;
char currentChar = data[0].charAt(0);
for (int i = 1; i <= data.length; i++) {
if (i == data.length || data[i].charAt(0) != currentChar) {
mAdapters.add(new InnerAdapter(
Arrays.copyOfRange(data, currentCharIndex, i - 1)));
+ mSavedStates.add(null);
if (i < data.length) {
currentChar = data[i].charAt(0);
currentCharIndex = i;
@@ -121,7 +124,20 @@
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
- holder.mRecyclerView.setAdapter(mAdapters.get(position));
+ // Note: would be equally valid to replace adapter content instead of swapping adapter
+ holder.mRecyclerView.swapAdapter(mAdapters.get(position), true);
+
+ Parcelable savedState = mSavedStates.get(position);
+ if (savedState != null) {
+ holder.mRecyclerView.getLayoutManager().onRestoreInstanceState(savedState);
+ mSavedStates.set(position, null);
+ }
+ }
+
+ @Override
+ public void onViewRecycled(ViewHolder holder) {
+ mSavedStates.set(holder.getAdapterPosition(),
+ holder.mRecyclerView.getLayoutManager().onSaveInstanceState());
}
@Override
diff --git a/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml b/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml
index c868430..3add119 100644
--- a/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml
+++ b/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml
@@ -64,6 +64,6 @@
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_gravity="bottom"
- android:background="#eee"
+ android:background="#fafafa"
app:menu="@menu/sample_bottom_menu"/>
</FrameLayout>
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java
index 4cfef7a..3442218 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java
@@ -94,7 +94,7 @@
default:
selectedItem.setText("Selected " + item.getTitle());
}
- return false;
+ return true;
}
});
}
diff --git a/transition/Android.mk b/transition/Android.mk
index 2de641a..c468ef1 100644
--- a/transition/Android.mk
+++ b/transition/Android.mk
@@ -14,91 +14,30 @@
LOCAL_PATH := $(call my-dir)
-# Build the resources using the latest applicable SDK version.
-# We do this here because the final static library must be compiled with an older
-# SDK version than the resources. The resources library and the R class that it
-# contains will not be linked into the final static library.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-transition-res
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library to resolve cyclic dependencies between Transition and platform dependent
-# implementations
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-transition-base
-LOCAL_SDK_VERSION := 14
-LOCAL_SRC_FILES := $(call all-java-files-under, base)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_JAVA_LIBRARIES := android-support-transition-res \
- android-support-compat
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Ice Cream Sandwich APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-transition-ics
-LOCAL_SDK_VERSION := 14
-LOCAL_SRC_FILES := $(call all-java-files-under, ics)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-transition-base
-LOCAL_JAVA_LIBRARIES := android-support-transition-res \
- android-support-compat
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of KitKat APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-transition-kitkat
-LOCAL_SDK_VERSION := 19
-LOCAL_SRC_FILES := $(call all-java-files-under, kitkat)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-transition-ics
-LOCAL_JAVA_LIBRARIES := android-support-transition-res \
- android-support-compat
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Lollipop APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-transition-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-transition-kitkat
-LOCAL_JAVA_LIBRARIES := android-support-transition-res \
- android-support-compat
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Marshmallow APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-transition-api23
-LOCAL_SDK_VERSION := 23
-LOCAL_SRC_FILES := $(call all-java-files-under, api23)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-transition-api21
-LOCAL_JAVA_LIBRARIES := android-support-transition-res \
- android-support-compat
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
# Here is the final static library that apps can link against.
# Applications that use this library must specify
#
# LOCAL_STATIC_ANDROID_LIBRARIES := \
# android-support-transition \
-# android-support-compat
+# android-support-v4
#
# in their makefiles to include the resources and their dependencies in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-transition
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-transition-api23
-LOCAL_STATIC_ANDROID_LIBRARIES := android-support-transition-res
-LOCAL_SHARED_ANDROID_LIBRARIES := android-support-compat
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under,base) \
+ $(call all-java-files-under,ics) \
+ $(call all-java-files-under,kitkat) \
+ $(call all-java-files-under,api21) \
+ $(call all-java-files-under,api23) \
+ $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-annotations \
+ android-support-v4
LOCAL_JAR_EXCLUDE_FILES := none
+LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/transition/api21/android/support/transition/SceneApi21.java b/transition/api21/android/support/transition/SceneApi21.java
index 5c44836..1e8f0ba 100644
--- a/transition/api21/android/support/transition/SceneApi21.java
+++ b/transition/api21/android/support/transition/SceneApi21.java
@@ -16,9 +16,13 @@
package android.support.transition;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.view.View;
import android.view.ViewGroup;
+@RequiresApi(21)
+@TargetApi(21)
class SceneApi21 extends SceneWrapper {
@Override
diff --git a/transition/api21/android/support/transition/SceneStaticsApi21.java b/transition/api21/android/support/transition/SceneStaticsApi21.java
index d6c8a28..547ca70 100644
--- a/transition/api21/android/support/transition/SceneStaticsApi21.java
+++ b/transition/api21/android/support/transition/SceneStaticsApi21.java
@@ -16,9 +16,13 @@
package android.support.transition;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.support.annotation.RequiresApi;
import android.view.ViewGroup;
+@RequiresApi(21)
+@TargetApi(21)
class SceneStaticsApi21 extends SceneStaticsImpl {
@Override
diff --git a/transition/api23/android/support/transition/TransitionApi23.java b/transition/api23/android/support/transition/TransitionApi23.java
index 3f06f43..0df0ec5 100644
--- a/transition/api23/android/support/transition/TransitionApi23.java
+++ b/transition/api23/android/support/transition/TransitionApi23.java
@@ -16,6 +16,11 @@
package android.support.transition;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(23)
+@TargetApi(23)
class TransitionApi23 extends TransitionKitKat {
@Override
diff --git a/transition/build.gradle b/transition/build.gradle
index f06b0bd..4d4e96f 100644
--- a/transition/build.gradle
+++ b/transition/build.gradle
@@ -3,7 +3,8 @@
archivesBaseName = 'transition'
dependencies {
- compile project(':support-compat')
+ compile project(':support-annotations')
+ compile project(':support-v4')
androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
exclude module: 'support-annotations'
@@ -27,10 +28,18 @@
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = ['base', 'ics', 'kitkat', 'api21', 'api23', 'src']
- main.res.srcDirs 'res', 'res-public'
- main.assets.srcDir 'assets'
- main.resources.srcDir 'src'
+ main.java.srcDirs = [
+ 'base',
+ 'ics',
+ 'kitkat',
+ 'api21',
+ 'api23',
+ 'src'
+ ]
+ main.res.srcDirs = [
+ 'res',
+ 'res-public'
+ ]
androidTest.setRoot('tests')
androidTest.java.srcDir 'tests/src'
diff --git a/transition/ics/android/support/transition/AutoTransitionPort.java b/transition/ics/android/support/transition/AutoTransitionPort.java
index 071813f..f3d4583 100644
--- a/transition/ics/android/support/transition/AutoTransitionPort.java
+++ b/transition/ics/android/support/transition/AutoTransitionPort.java
@@ -16,6 +16,9 @@
package android.support.transition;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
/**
* Utility class for creating a default transition that automatically fades,
* moves, and resizes views during a scene change.
@@ -24,6 +27,8 @@
* tag <code>autoTransition</code>, along with the other standard
* attributes of {@link android.R.styleable#Transition}.</p>
*/
+@RequiresApi(14)
+@TargetApi(14)
class AutoTransitionPort extends TransitionSetPort {
/**
diff --git a/transition/ics/android/support/transition/ChangeBoundsIcs.java b/transition/ics/android/support/transition/ChangeBoundsIcs.java
index 4f52c7d..61b7ac1 100644
--- a/transition/ics/android/support/transition/ChangeBoundsIcs.java
+++ b/transition/ics/android/support/transition/ChangeBoundsIcs.java
@@ -16,6 +16,11 @@
package android.support.transition;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(14)
+@TargetApi(14)
class ChangeBoundsIcs extends TransitionIcs implements ChangeBoundsInterface {
public ChangeBoundsIcs(TransitionInterface transition) {
diff --git a/transition/ics/android/support/transition/ChangeBoundsPort.java b/transition/ics/android/support/transition/ChangeBoundsPort.java
index 6151018..d9db2c7 100644
--- a/transition/ics/android/support/transition/ChangeBoundsPort.java
+++ b/transition/ics/android/support/transition/ChangeBoundsPort.java
@@ -20,10 +20,12 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
+import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
+import android.support.annotation.RequiresApi;
import android.view.View;
import android.view.ViewGroup;
@@ -37,6 +39,8 @@
* tag <code>changeBounds</code>, along with the other standard
* attributes of {@link android.R.styleable#Transition}.</p>
*/
+@RequiresApi(14)
+@TargetApi(14)
class ChangeBoundsPort extends TransitionPort {
private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds";
diff --git a/transition/ics/android/support/transition/FadeIcs.java b/transition/ics/android/support/transition/FadeIcs.java
index 28ed45d..ead8c00 100644
--- a/transition/ics/android/support/transition/FadeIcs.java
+++ b/transition/ics/android/support/transition/FadeIcs.java
@@ -17,8 +17,12 @@
package android.support.transition;
import android.animation.Animator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.view.ViewGroup;
+@RequiresApi(14)
+@TargetApi(14)
class FadeIcs extends TransitionIcs implements VisibilityImpl {
public FadeIcs(TransitionInterface transition) {
diff --git a/transition/ics/android/support/transition/FadePort.java b/transition/ics/android/support/transition/FadePort.java
index c131449..79673f5 100644
--- a/transition/ics/android/support/transition/FadePort.java
+++ b/transition/ics/android/support/transition/FadePort.java
@@ -19,7 +19,8 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.support.v4.view.ViewCompat;
import android.util.Log;
import android.view.View;
@@ -55,6 +56,8 @@
* attributes of {@link android.R.styleable#Fade} and
* {@link android.R.styleable#Transition}.</p>
*/
+@RequiresApi(14)
+@TargetApi(14)
class FadePort extends VisibilityPort {
/**
diff --git a/transition/ics/android/support/transition/RectEvaluator.java b/transition/ics/android/support/transition/RectEvaluator.java
index e0892e5..c85ed18 100644
--- a/transition/ics/android/support/transition/RectEvaluator.java
+++ b/transition/ics/android/support/transition/RectEvaluator.java
@@ -17,11 +17,15 @@
package android.support.transition;
import android.animation.TypeEvaluator;
+import android.annotation.TargetApi;
import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
/**
* This evaluator can be used to perform type interpolation between <code>Rect</code> values.
*/
+@RequiresApi(14)
+@TargetApi(14)
class RectEvaluator implements TypeEvaluator<Rect> {
/**
diff --git a/transition/ics/android/support/transition/SceneIcs.java b/transition/ics/android/support/transition/SceneIcs.java
index 0d0d84e..01e0508 100644
--- a/transition/ics/android/support/transition/SceneIcs.java
+++ b/transition/ics/android/support/transition/SceneIcs.java
@@ -16,9 +16,13 @@
package android.support.transition;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.view.View;
import android.view.ViewGroup;
+@RequiresApi(14)
+@TargetApi(14)
class SceneIcs extends SceneImpl {
/* package */ ScenePort mScene;
diff --git a/transition/ics/android/support/transition/ScenePort.java b/transition/ics/android/support/transition/ScenePort.java
index 6fc5c25..34b2d7b 100644
--- a/transition/ics/android/support/transition/ScenePort.java
+++ b/transition/ics/android/support/transition/ScenePort.java
@@ -16,7 +16,9 @@
package android.support.transition;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.support.annotation.RequiresApi;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -28,6 +30,8 @@
* animate the various property changes that take place during the
* scene change.
*/
+@RequiresApi(14)
+@TargetApi(14)
final class ScenePort {
Runnable mEnterAction, mExitAction;
diff --git a/transition/ics/android/support/transition/SceneStaticsIcs.java b/transition/ics/android/support/transition/SceneStaticsIcs.java
index 87f39e2..bcc7451 100644
--- a/transition/ics/android/support/transition/SceneStaticsIcs.java
+++ b/transition/ics/android/support/transition/SceneStaticsIcs.java
@@ -16,9 +16,13 @@
package android.support.transition;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.support.annotation.RequiresApi;
import android.view.ViewGroup;
+@RequiresApi(14)
+@TargetApi(14)
class SceneStaticsIcs extends SceneStaticsImpl {
@Override
diff --git a/transition/ics/android/support/transition/TransitionIcs.java b/transition/ics/android/support/transition/TransitionIcs.java
index 8cd5fae..832b59e 100644
--- a/transition/ics/android/support/transition/TransitionIcs.java
+++ b/transition/ics/android/support/transition/TransitionIcs.java
@@ -18,12 +18,16 @@
import android.animation.Animator;
import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
+@RequiresApi(14)
+@TargetApi(14)
class TransitionIcs extends TransitionImpl {
/* package */ TransitionPort mTransition;
diff --git a/transition/ics/android/support/transition/TransitionManagerIcs.java b/transition/ics/android/support/transition/TransitionManagerIcs.java
index 19b0175..d277ae7 100644
--- a/transition/ics/android/support/transition/TransitionManagerIcs.java
+++ b/transition/ics/android/support/transition/TransitionManagerIcs.java
@@ -16,6 +16,11 @@
package android.support.transition;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(14)
+@TargetApi(14)
class TransitionManagerIcs extends TransitionManagerImpl {
private final TransitionManagerPort mTransitionManager = new TransitionManagerPort();
diff --git a/transition/ics/android/support/transition/TransitionManagerPort.java b/transition/ics/android/support/transition/TransitionManagerPort.java
index 47eaff0..3acad62 100644
--- a/transition/ics/android/support/transition/TransitionManagerPort.java
+++ b/transition/ics/android/support/transition/TransitionManagerPort.java
@@ -16,6 +16,8 @@
package android.support.transition;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.v4.util.ArrayMap;
import android.support.v4.view.ViewCompat;
@@ -29,6 +31,8 @@
import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+@RequiresApi(14)
+@TargetApi(14)
class TransitionManagerPort {
// TODO: how to handle enter/exit?
diff --git a/transition/ics/android/support/transition/TransitionManagerStaticsIcs.java b/transition/ics/android/support/transition/TransitionManagerStaticsIcs.java
index 656ff47..aab7083 100644
--- a/transition/ics/android/support/transition/TransitionManagerStaticsIcs.java
+++ b/transition/ics/android/support/transition/TransitionManagerStaticsIcs.java
@@ -16,8 +16,12 @@
package android.support.transition;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.view.ViewGroup;
+@RequiresApi(14)
+@TargetApi(14)
class TransitionManagerStaticsIcs extends TransitionManagerStaticsImpl {
@Override
diff --git a/transition/ics/android/support/transition/TransitionPort.java b/transition/ics/android/support/transition/TransitionPort.java
index 516f93b..4e6a9d7 100644
--- a/transition/ics/android/support/transition/TransitionPort.java
+++ b/transition/ics/android/support/transition/TransitionPort.java
@@ -19,6 +19,8 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.v4.util.ArrayMap;
import android.support.v4.util.LongSparseArray;
@@ -33,6 +35,8 @@
import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+@RequiresApi(14)
+@TargetApi(14)
abstract class TransitionPort implements Cloneable {
static final boolean DBG = false;
diff --git a/transition/ics/android/support/transition/TransitionSetIcs.java b/transition/ics/android/support/transition/TransitionSetIcs.java
index 744033e..b3fcd8f 100644
--- a/transition/ics/android/support/transition/TransitionSetIcs.java
+++ b/transition/ics/android/support/transition/TransitionSetIcs.java
@@ -16,6 +16,11 @@
package android.support.transition;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(14)
+@TargetApi(14)
class TransitionSetIcs extends TransitionIcs implements TransitionSetImpl {
private TransitionSetPort mTransitionSet;
diff --git a/transition/ics/android/support/transition/TransitionSetPort.java b/transition/ics/android/support/transition/TransitionSetPort.java
index a834bca..1d5ddd6 100644
--- a/transition/ics/android/support/transition/TransitionSetPort.java
+++ b/transition/ics/android/support/transition/TransitionSetPort.java
@@ -17,6 +17,8 @@
package android.support.transition;
import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.util.AndroidRuntimeException;
import android.view.View;
@@ -26,6 +28,8 @@
import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+@RequiresApi(14)
+@TargetApi(14)
class TransitionSetPort extends TransitionPort {
/**
diff --git a/transition/ics/android/support/transition/TransitionValuesMaps.java b/transition/ics/android/support/transition/TransitionValuesMaps.java
index e1274ec..ddf05da 100644
--- a/transition/ics/android/support/transition/TransitionValuesMaps.java
+++ b/transition/ics/android/support/transition/TransitionValuesMaps.java
@@ -16,11 +16,15 @@
package android.support.transition;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.support.v4.util.ArrayMap;
import android.support.v4.util.LongSparseArray;
import android.util.SparseArray;
import android.view.View;
+@RequiresApi(14)
+@TargetApi(14)
class TransitionValuesMaps {
public ArrayMap<View, TransitionValues> viewValues = new ArrayMap<>();
diff --git a/transition/ics/android/support/transition/ViewGroupOverlay.java b/transition/ics/android/support/transition/ViewGroupOverlay.java
index 07c9853..da91466 100644
--- a/transition/ics/android/support/transition/ViewGroupOverlay.java
+++ b/transition/ics/android/support/transition/ViewGroupOverlay.java
@@ -16,11 +16,15 @@
package android.support.transition;
+import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
import android.view.View;
import android.view.ViewGroup;
+@RequiresApi(14)
+@TargetApi(14)
class ViewGroupOverlay extends ViewOverlay {
ViewGroupOverlay(Context context, ViewGroup hostView, View requestingView) {
diff --git a/transition/ics/android/support/transition/ViewOverlay.java b/transition/ics/android/support/transition/ViewOverlay.java
index ef6793c..a95d8f1 100644
--- a/transition/ics/android/support/transition/ViewOverlay.java
+++ b/transition/ics/android/support/transition/ViewOverlay.java
@@ -17,10 +17,12 @@
package android.support.transition;
import android.R;
+import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.v4.view.ViewCompat;
import android.view.MotionEvent;
@@ -34,6 +36,8 @@
import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+@RequiresApi(14)
+@TargetApi(14)
class ViewOverlay {
/**
diff --git a/transition/ics/android/support/transition/VisibilityIcs.java b/transition/ics/android/support/transition/VisibilityIcs.java
index 28c6e61..f2290cf 100644
--- a/transition/ics/android/support/transition/VisibilityIcs.java
+++ b/transition/ics/android/support/transition/VisibilityIcs.java
@@ -17,8 +17,12 @@
package android.support.transition;
import android.animation.Animator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.view.ViewGroup;
+@RequiresApi(14)
+@TargetApi(14)
class VisibilityIcs extends TransitionIcs implements VisibilityImpl {
@Override
diff --git a/transition/ics/android/support/transition/VisibilityPort.java b/transition/ics/android/support/transition/VisibilityPort.java
index ebe5cb8..e740b19 100644
--- a/transition/ics/android/support/transition/VisibilityPort.java
+++ b/transition/ics/android/support/transition/VisibilityPort.java
@@ -17,6 +17,8 @@
package android.support.transition;
import android.animation.Animator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.view.View;
import android.view.ViewGroup;
@@ -31,6 +33,8 @@
* {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)},
* {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)},
*/
+@RequiresApi(14)
+@TargetApi(14)
abstract class VisibilityPort extends TransitionPort {
private static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
diff --git a/transition/ics/android/support/transition/WindowIdPort.java b/transition/ics/android/support/transition/WindowIdPort.java
index f768b2e..148332e 100644
--- a/transition/ics/android/support/transition/WindowIdPort.java
+++ b/transition/ics/android/support/transition/WindowIdPort.java
@@ -16,8 +16,10 @@
package android.support.transition;
+import android.annotation.TargetApi;
import android.os.IBinder;
import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
import android.view.View;
@@ -27,6 +29,8 @@
* <p>Since the use of WindowId in Transition API is limited to identifying windows, we can just
* wrap a window token and use it as an identifier.</p>
*/
+@RequiresApi(14)
+@TargetApi(14)
class WindowIdPort {
private final IBinder mToken;
diff --git a/transition/kitkat/android/support/transition/ChangeBoundsKitKat.java b/transition/kitkat/android/support/transition/ChangeBoundsKitKat.java
index 704c32f..e8575d4 100644
--- a/transition/kitkat/android/support/transition/ChangeBoundsKitKat.java
+++ b/transition/kitkat/android/support/transition/ChangeBoundsKitKat.java
@@ -16,6 +16,11 @@
package android.support.transition;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
class ChangeBoundsKitKat extends TransitionKitKat implements ChangeBoundsInterface {
public ChangeBoundsKitKat(TransitionInterface transition) {
diff --git a/transition/kitkat/android/support/transition/FadeKitKat.java b/transition/kitkat/android/support/transition/FadeKitKat.java
index 7edd220..26992af 100644
--- a/transition/kitkat/android/support/transition/FadeKitKat.java
+++ b/transition/kitkat/android/support/transition/FadeKitKat.java
@@ -19,6 +19,11 @@
import android.animation.Animator;
import android.view.ViewGroup;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
class FadeKitKat extends TransitionKitKat implements VisibilityImpl {
public FadeKitKat(TransitionInterface transition) {
diff --git a/transition/kitkat/android/support/transition/SceneKitKat.java b/transition/kitkat/android/support/transition/SceneKitKat.java
index 50d3c47..ae7ad10 100644
--- a/transition/kitkat/android/support/transition/SceneKitKat.java
+++ b/transition/kitkat/android/support/transition/SceneKitKat.java
@@ -23,6 +23,11 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
class SceneKitKat extends SceneWrapper {
private static Field sEnterAction;
diff --git a/transition/kitkat/android/support/transition/SceneStaticsKitKat.java b/transition/kitkat/android/support/transition/SceneStaticsKitKat.java
index b6e3659..94868d0 100644
--- a/transition/kitkat/android/support/transition/SceneStaticsKitKat.java
+++ b/transition/kitkat/android/support/transition/SceneStaticsKitKat.java
@@ -19,6 +19,11 @@
import android.content.Context;
import android.view.ViewGroup;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
class SceneStaticsKitKat extends SceneStaticsImpl {
@Override
diff --git a/transition/kitkat/android/support/transition/SceneWrapper.java b/transition/kitkat/android/support/transition/SceneWrapper.java
index 982db19..cad5db9 100644
--- a/transition/kitkat/android/support/transition/SceneWrapper.java
+++ b/transition/kitkat/android/support/transition/SceneWrapper.java
@@ -18,6 +18,11 @@
import android.view.ViewGroup;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
abstract class SceneWrapper extends SceneImpl {
/* package */ android.transition.Scene mScene;
diff --git a/transition/kitkat/android/support/transition/TransitionKitKat.java b/transition/kitkat/android/support/transition/TransitionKitKat.java
index e9e60e5..c608f66 100644
--- a/transition/kitkat/android/support/transition/TransitionKitKat.java
+++ b/transition/kitkat/android/support/transition/TransitionKitKat.java
@@ -25,6 +25,11 @@
import java.util.ArrayList;
import java.util.List;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
class TransitionKitKat extends TransitionImpl {
/* package */ android.transition.Transition mTransition;
diff --git a/transition/kitkat/android/support/transition/TransitionManagerKitKat.java b/transition/kitkat/android/support/transition/TransitionManagerKitKat.java
index 9c33cba..3326600 100644
--- a/transition/kitkat/android/support/transition/TransitionManagerKitKat.java
+++ b/transition/kitkat/android/support/transition/TransitionManagerKitKat.java
@@ -18,6 +18,11 @@
import android.transition.TransitionManager;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
class TransitionManagerKitKat extends TransitionManagerImpl {
private final android.transition.TransitionManager mTransitionManager = new TransitionManager();
diff --git a/transition/kitkat/android/support/transition/TransitionManagerStaticsKitKat.java b/transition/kitkat/android/support/transition/TransitionManagerStaticsKitKat.java
index 6c2b618..e7c927b 100644
--- a/transition/kitkat/android/support/transition/TransitionManagerStaticsKitKat.java
+++ b/transition/kitkat/android/support/transition/TransitionManagerStaticsKitKat.java
@@ -18,6 +18,11 @@
import android.view.ViewGroup;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
class TransitionManagerStaticsKitKat extends TransitionManagerStaticsImpl {
@Override
diff --git a/transition/kitkat/android/support/transition/TransitionSetKitKat.java b/transition/kitkat/android/support/transition/TransitionSetKitKat.java
index 591df63..f880d71 100644
--- a/transition/kitkat/android/support/transition/TransitionSetKitKat.java
+++ b/transition/kitkat/android/support/transition/TransitionSetKitKat.java
@@ -16,6 +16,11 @@
package android.support.transition;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
class TransitionSetKitKat extends TransitionKitKat implements TransitionSetImpl {
private android.transition.TransitionSet mTransitionSet;
diff --git a/transition/kitkat/android/support/transition/VisibilityKitKat.java b/transition/kitkat/android/support/transition/VisibilityKitKat.java
index 8437a76..ca603ae 100644
--- a/transition/kitkat/android/support/transition/VisibilityKitKat.java
+++ b/transition/kitkat/android/support/transition/VisibilityKitKat.java
@@ -19,6 +19,11 @@
import android.animation.Animator;
import android.view.ViewGroup;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
class VisibilityKitKat extends TransitionKitKat implements VisibilityImpl {
@Override
diff --git a/v13/Android.mk b/v13/Android.mk
index 00675b7..1b30d99 100644
--- a/v13/Android.mk
+++ b/v13/Android.mk
@@ -14,63 +14,34 @@
LOCAL_PATH := $(call my-dir)
-# Note: the source code is in java/, not src/, because this code is also part of
-# the framework library, and build/core/pathmap.mk expects a java/ subdirectory.
-
-# A helper sub-library that makes direct use of Ice Cream Sandwich APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v13-ics
-LOCAL_SDK_VERSION := 14
-LOCAL_SRC_FILES := $(call all-java-files-under, ics)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Ice Cream Sandwich APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v13-ics-mr1
-LOCAL_SDK_VERSION := 15
-LOCAL_SRC_FILES := $(call all-java-files-under, ics-mr1)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13-ics
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of MNC APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v13-mnc
-LOCAL_SDK_VERSION := 23
-LOCAL_SRC_FILES := $(call all-java-files-under, api23)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13-ics-mr1
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of NYC APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v13-nyc
-LOCAL_SDK_VERSION := 24
-LOCAL_SRC_FILES := $(call all-java-files-under, api24)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13-mnc
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of NYC MR-1 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v13-nyc-mr1
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, api25)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13-nyc
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
+# Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+# LOCAL_STATIC_ANDROID_LIBRARIES := \
+# android-support-v13 \
+# android-support-v4
+#
+# in their makefiles to include the resources and their dependencies in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-v13
-LOCAL_SDK_VERSION := 13
-LOCAL_SRC_FILES := $(call all-java-files-under, java)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, ics) \
+ $(call all-java-files-under, ics-mr1) \
+ $(call all-java-files-under, api23) \
+ $(call all-java-files-under, api24) \
+ $(call all-java-files-under, api25) \
+ $(call all-java-files-under, java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4 \
- android-support-v13-nyc-mr1
+# Some projects expect to inherit android-support-v4 from
+# android-support-v13, so we need to keep it static until they can be fixed.
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ android-support-v4
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-v4 \
+ android-support-annotations
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/v13/api23/android/support/v13/app/FragmentCompat23.java b/v13/api23/android/support/v13/app/FragmentCompat23.java
index 5027240..1364e84 100644
--- a/v13/api23/android/support/v13/app/FragmentCompat23.java
+++ b/v13/api23/android/support/v13/app/FragmentCompat23.java
@@ -16,8 +16,12 @@
package android.support.v13.app;
+import android.annotation.TargetApi;
import android.app.Fragment;
+import android.support.annotation.RequiresApi;
+@RequiresApi(23)
+@TargetApi(23)
class FragmentCompat23 {
public static void requestPermissions(Fragment fragment, String[] permissions,
int requestCode) {
diff --git a/v13/api24/android/support/v13/app/FragmentCompatApi24.java b/v13/api24/android/support/v13/app/FragmentCompatApi24.java
index 4baf02a..2b41d25 100644
--- a/v13/api24/android/support/v13/app/FragmentCompatApi24.java
+++ b/v13/api24/android/support/v13/app/FragmentCompatApi24.java
@@ -17,8 +17,12 @@
package android.support.v13.app;
+import android.annotation.TargetApi;
import android.app.Fragment;
+import android.support.annotation.RequiresApi;
+@RequiresApi(24)
+@TargetApi(24)
class FragmentCompatApi24 {
public static void setUserVisibleHint(Fragment f, boolean isVisible) {
f.setUserVisibleHint(isVisible);
diff --git a/v13/api24/android/support/v13/view/DragAndDropPermissionsCompatApi24.java b/v13/api24/android/support/v13/view/DragAndDropPermissionsCompatApi24.java
index 363cf8d..fd38b79 100644
--- a/v13/api24/android/support/v13/view/DragAndDropPermissionsCompatApi24.java
+++ b/v13/api24/android/support/v13/view/DragAndDropPermissionsCompatApi24.java
@@ -16,10 +16,14 @@
package android.support.v13.view;
+import android.annotation.TargetApi;
import android.app.Activity;
+import android.support.annotation.RequiresApi;
import android.view.DragAndDropPermissions;
import android.view.DragEvent;
+@RequiresApi(24)
+@TargetApi(24)
class DragAndDropPermissionsCompatApi24 {
public static Object request(Activity activity, DragEvent dragEvent) {
return activity.requestDragAndDropPermissions(dragEvent);
diff --git a/v13/api24/android/support/v13/view/ViewCompatApi24.java b/v13/api24/android/support/v13/view/ViewCompatApi24.java
index 725cad0..422dbb9 100644
--- a/v13/api24/android/support/v13/view/ViewCompatApi24.java
+++ b/v13/api24/android/support/v13/view/ViewCompatApi24.java
@@ -16,9 +16,13 @@
package android.support.v13.view;
+import android.annotation.TargetApi;
import android.content.ClipData;
+import android.support.annotation.RequiresApi;
import android.view.View;
+@RequiresApi(24)
+@TargetApi(24)
class ViewCompatApi24 {
public static boolean startDragAndDrop(View v, ClipData data,
View.DragShadowBuilder shadowBuilder, Object localState, int flags) {
diff --git a/v13/api25/android/support/v13/view/inputmethod/EditorInfoCompatApi25.java b/v13/api25/android/support/v13/view/inputmethod/EditorInfoCompatApi25.java
index e5d20a5..3eef2ba 100644
--- a/v13/api25/android/support/v13/view/inputmethod/EditorInfoCompatApi25.java
+++ b/v13/api25/android/support/v13/view/inputmethod/EditorInfoCompatApi25.java
@@ -16,9 +16,12 @@
package android.support.v13.view.inputmethod;
-import android.os.Bundle;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.view.inputmethod.EditorInfo;
+@RequiresApi(25)
+@TargetApi(25)
final class EditorInfoCompatApi25 {
public static void setContentMimeTypes(EditorInfo editorInfo, String[] contentMimeTypes) {
editorInfo.contentMimeTypes = contentMimeTypes;
diff --git a/v13/api25/android/support/v13/view/inputmethod/InputConnectionCompatApi25.java b/v13/api25/android/support/v13/view/inputmethod/InputConnectionCompatApi25.java
index a29bedd..41de756 100644
--- a/v13/api25/android/support/v13/view/inputmethod/InputConnectionCompatApi25.java
+++ b/v13/api25/android/support/v13/view/inputmethod/InputConnectionCompatApi25.java
@@ -16,12 +16,15 @@
package android.support.v13.view.inputmethod;
+import android.annotation.TargetApi;
import android.os.Bundle;
-import android.view.inputmethod.BaseInputConnection;
+import android.support.annotation.RequiresApi;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
import android.view.inputmethod.InputContentInfo;
+@RequiresApi(25)
+@TargetApi(25)
final class InputConnectionCompatApi25 {
public static boolean commitContent(InputConnection ic, Object inputContentInfo, int flags,
diff --git a/v13/api25/android/support/v13/view/inputmethod/InputContentInfoCompatApi25.java b/v13/api25/android/support/v13/view/inputmethod/InputContentInfoCompatApi25.java
index c485ca6..c51199e 100644
--- a/v13/api25/android/support/v13/view/inputmethod/InputContentInfoCompatApi25.java
+++ b/v13/api25/android/support/v13/view/inputmethod/InputContentInfoCompatApi25.java
@@ -16,11 +16,14 @@
package android.support.v13.view.inputmethod;
+import android.annotation.TargetApi;
import android.content.ClipDescription;
import android.net.Uri;
-import android.os.Parcel;
+import android.support.annotation.RequiresApi;
import android.view.inputmethod.InputContentInfo;
+@RequiresApi(25)
+@TargetApi(25)
final class InputContentInfoCompatApi25 {
public static Object create(Uri contentUri, ClipDescription description, Uri linkUri) {
diff --git a/v13/build.gradle b/v13/build.gradle
index 4baaa27..5e1a026 100644
--- a/v13/build.gradle
+++ b/v13/build.gradle
@@ -1,33 +1,28 @@
apply plugin: 'com.android.library'
-
archivesBaseName = 'support-v13'
-sourceCompatibility = JavaVersion.VERSION_1_7
-targetCompatibility = JavaVersion.VERSION_1_7
+dependencies {
+ compile project(':support-annotations')
+ compile project(':support-v4')
+}
-createApiSourceSets(project, gradle.ext.studioCompat.modules.v13.apiTargets)
-setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.v13.dependencies)
android {
- compileSdkVersion 13
+ compileSdkVersion project.ext.currentSdk
defaultConfig {
minSdkVersion 13
- // TODO: get target from branch
- //targetSdkVersion 19
}
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = ['java']
- main.aidl.srcDirs = ['java']
-
- androidTest.setRoot('tests')
- androidTest.java.srcDir 'tests/java'
- }
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_7
- targetCompatibility JavaVersion.VERSION_1_7
+ main.java.srcDirs = [
+ 'ics',
+ 'ics-mr1',
+ 'api23',
+ 'api24',
+ 'api25',
+ 'java'
+ ]
}
lintOptions {
@@ -70,11 +65,6 @@
from android.sourceSets.main.java.srcDirs
}
- project.ext.allSS.each { ss ->
- javadocTask.source ss.java
- sourcesJarTask.from ss.java.srcDirs
- }
-
artifacts.add('archives', javadocJarTask);
artifacts.add('archives', sourcesJarTask);
}
diff --git a/v13/ics-mr1/android/support/v13/app/FragmentCompatICSMR1.java b/v13/ics-mr1/android/support/v13/app/FragmentCompatICSMR1.java
index ea40376..2da10ae 100644
--- a/v13/ics-mr1/android/support/v13/app/FragmentCompatICSMR1.java
+++ b/v13/ics-mr1/android/support/v13/app/FragmentCompatICSMR1.java
@@ -16,8 +16,12 @@
package android.support.v13.app;
+import android.annotation.TargetApi;
import android.app.Fragment;
+import android.support.annotation.RequiresApi;
+@RequiresApi(15)
+@TargetApi(15)
class FragmentCompatICSMR1 {
public static void setUserVisibleHint(Fragment f, boolean isVisible) {
if (f.getFragmentManager() != null) {
diff --git a/v13/ics/android/support/v13/app/FragmentCompatICS.java b/v13/ics/android/support/v13/app/FragmentCompatICS.java
index 1beb704..ff40337 100644
--- a/v13/ics/android/support/v13/app/FragmentCompatICS.java
+++ b/v13/ics/android/support/v13/app/FragmentCompatICS.java
@@ -16,8 +16,12 @@
package android.support.v13.app;
+import android.annotation.TargetApi;
import android.app.Fragment;
+import android.support.annotation.RequiresApi;
+@RequiresApi(14)
+@TargetApi(14)
class FragmentCompatICS {
public static void setMenuVisibility(Fragment f, boolean visible) {
f.setMenuVisibility(visible);
diff --git a/v13/java/android/support/v13/app/ActivityCompat.java b/v13/java/android/support/v13/app/ActivityCompat.java
index 8059a68..8f9a767 100644
--- a/v13/java/android/support/v13/app/ActivityCompat.java
+++ b/v13/java/android/support/v13/app/ActivityCompat.java
@@ -16,7 +16,9 @@
package android.support.v13.app;
+import android.annotation.TargetApi;
import android.app.Activity;
+import android.support.annotation.RequiresApi;
import android.support.v13.view.DragAndDropPermissionsCompat;
import android.view.DragEvent;
@@ -24,6 +26,8 @@
* Helper for accessing features in {@link android.app.Activity}
* introduced after API level 13 in a backwards compatible fashion.
*/
+@RequiresApi(13)
+@TargetApi(13)
public class ActivityCompat extends android.support.v4.app.ActivityCompat {
/**
diff --git a/v13/java/android/support/v13/app/FragmentCompat.java b/v13/java/android/support/v13/app/FragmentCompat.java
index 38e4a48..99e4a80 100644
--- a/v13/java/android/support/v13/app/FragmentCompat.java
+++ b/v13/java/android/support/v13/app/FragmentCompat.java
@@ -16,6 +16,7 @@
package android.support.v13.app;
+import android.annotation.TargetApi;
import android.app.Fragment;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -23,6 +24,7 @@
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
import android.support.v4.os.BuildCompat;
import java.util.Arrays;
@@ -31,6 +33,8 @@
* Helper for accessing features in {@link Fragment} introduced after
* API level 13 in a backwards compatible fashion.
*/
+@RequiresApi(13)
+@TargetApi(13)
public class FragmentCompat {
interface FragmentCompatImpl {
void setMenuVisibility(Fragment f, boolean visible);
diff --git a/v13/java/android/support/v13/app/FragmentPagerAdapter.java b/v13/java/android/support/v13/app/FragmentPagerAdapter.java
index 4ea48ab..78d8b89 100644
--- a/v13/java/android/support/v13/app/FragmentPagerAdapter.java
+++ b/v13/java/android/support/v13/app/FragmentPagerAdapter.java
@@ -16,10 +16,12 @@
package android.support.v13.app;
+import android.annotation.TargetApi;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.util.Log;
@@ -62,6 +64,8 @@
* {@sample frameworks/support/samples/Support13Demos/res/layout/fragment_pager_list.xml
* complete}
*/
+@RequiresApi(13)
+@TargetApi(13)
public abstract class FragmentPagerAdapter extends PagerAdapter {
private static final String TAG = "FragmentPagerAdapter";
private static final boolean DEBUG = false;
diff --git a/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java b/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
index ca316c9..2579688 100644
--- a/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
+++ b/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
@@ -16,11 +16,13 @@
package android.support.v13.app;
+import android.annotation.TargetApi;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.util.Log;
@@ -65,6 +67,8 @@
* {@sample frameworks/support/samples/Support4Demos/res/layout/fragment_pager_list.xml
* complete}
*/
+@RequiresApi(13)
+@TargetApi(13)
public abstract class FragmentStatePagerAdapter extends PagerAdapter {
private static final String TAG = "FragmentStatePagerAdapter";
private static final boolean DEBUG = false;
diff --git a/v13/java/android/support/v13/app/FragmentTabHost.java b/v13/java/android/support/v13/app/FragmentTabHost.java
index 6cfd0ad..ba5d659 100644
--- a/v13/java/android/support/v13/app/FragmentTabHost.java
+++ b/v13/java/android/support/v13/app/FragmentTabHost.java
@@ -16,6 +16,7 @@
package android.support.v13.app;
+import android.annotation.TargetApi;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
@@ -24,6 +25,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -39,6 +41,8 @@
* used with the platform {@link android.app.Fragment} APIs. You will not
* normally use this, instead using action bar tabs.
*/
+@RequiresApi(13)
+@TargetApi(13)
public class FragmentTabHost extends TabHost
implements TabHost.OnTabChangeListener {
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
diff --git a/v13/java/android/support/v13/view/DragAndDropPermissionsCompat.java b/v13/java/android/support/v13/view/DragAndDropPermissionsCompat.java
index 4f5d9d8..f10a548 100644
--- a/v13/java/android/support/v13/view/DragAndDropPermissionsCompat.java
+++ b/v13/java/android/support/v13/view/DragAndDropPermissionsCompat.java
@@ -16,7 +16,9 @@
package android.support.v13.view;
+import android.annotation.TargetApi;
import android.app.Activity;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.v4.os.BuildCompat;
import android.view.DragEvent;
@@ -27,6 +29,8 @@
* Helper for accessing features in {@link android.view.DragAndDropPermissions}
* introduced after API level 13 in a backwards compatible fashion.
*/
+@RequiresApi(13)
+@TargetApi(13)
public final class DragAndDropPermissionsCompat {
interface DragAndDropPermissionsCompatImpl {
diff --git a/v13/java/android/support/v13/view/DragStartHelper.java b/v13/java/android/support/v13/view/DragStartHelper.java
index d3b51cc..b1fd913 100644
--- a/v13/java/android/support/v13/view/DragStartHelper.java
+++ b/v13/java/android/support/v13/view/DragStartHelper.java
@@ -17,7 +17,9 @@
package android.support.v13.view;
+import android.annotation.TargetApi;
import android.graphics.Point;
+import android.support.annotation.RequiresApi;
import android.support.v4.view.InputDeviceCompat;
import android.support.v4.view.MotionEventCompat;
import android.view.MotionEvent;
@@ -68,6 +70,8 @@
* }
* </pre>
*/
+@RequiresApi(13)
+@TargetApi(13)
public class DragStartHelper {
final private View mView;
final private OnDragStartListener mListener;
diff --git a/v13/java/android/support/v13/view/ViewCompat.java b/v13/java/android/support/v13/view/ViewCompat.java
index 971d70d..0fd3234 100644
--- a/v13/java/android/support/v13/view/ViewCompat.java
+++ b/v13/java/android/support/v13/view/ViewCompat.java
@@ -16,7 +16,9 @@
package android.support.v13.view;
+import android.annotation.TargetApi;
import android.content.ClipData;
+import android.support.annotation.RequiresApi;
import android.support.v4.os.BuildCompat;
import android.view.View;
@@ -24,6 +26,8 @@
* Helper for accessing features in {@link View} introduced after API
* level 13 in a backwards compatible fashion.
*/
+@RequiresApi(13)
+@TargetApi(13)
public class ViewCompat extends android.support.v4.view.ViewCompat {
interface ViewCompatImpl {
boolean startDragAndDrop(View v, ClipData data, View.DragShadowBuilder shadowBuilder,
diff --git a/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java b/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
index 79d7066..17976a8 100644
--- a/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
+++ b/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
@@ -16,9 +16,11 @@
package android.support.v13.view.inputmethod;
+import android.annotation.TargetApi;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.support.v4.os.BuildCompat;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -27,6 +29,8 @@
* Helper for accessing features in {@link EditorInfo} introduced after API level 13 in a backwards
* compatible fashion.
*/
+@RequiresApi(13)
+@TargetApi(13)
public final class EditorInfoCompat {
private interface EditorInfoCompatImpl {
diff --git a/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java b/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
index 07a76aa..5f7b012 100644
--- a/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
+++ b/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
@@ -16,12 +16,14 @@
package android.support.v13.view.inputmethod;
+import android.annotation.TargetApi;
import android.content.ClipDescription;
import android.net.Uri;
import android.os.Bundle;
import android.os.ResultReceiver;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.support.v4.os.BuildCompat;
import android.text.TextUtils;
import android.view.inputmethod.EditorInfo;
@@ -32,6 +34,8 @@
* Helper for accessing features in {@link InputConnection} introduced after API level 13 in a
* backwards compatible fashion.
*/
+@RequiresApi(13)
+@TargetApi(13)
public final class InputConnectionCompat {
private interface InputConnectionCompatImpl {
diff --git a/v13/java/android/support/v13/view/inputmethod/InputContentInfoCompat.java b/v13/java/android/support/v13/view/inputmethod/InputContentInfoCompat.java
index 43ad7ff..9379020 100644
--- a/v13/java/android/support/v13/view/inputmethod/InputContentInfoCompat.java
+++ b/v13/java/android/support/v13/view/inputmethod/InputContentInfoCompat.java
@@ -16,16 +16,20 @@
package android.support.v13.view.inputmethod;
+import android.annotation.TargetApi;
import android.content.ClipDescription;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.support.v4.os.BuildCompat;
/**
* Helper for accessing features in InputContentInfo introduced after API level 13 in a backwards
* compatible fashion.
*/
+@RequiresApi(13)
+@TargetApi(13)
public final class InputContentInfoCompat {
private interface InputContentInfoCompatImpl {
diff --git a/v14/preference/Android.mk b/v14/preference/Android.mk
index f359486..7a0b846 100644
--- a/v14/preference/Android.mk
+++ b/v14/preference/Android.mk
@@ -22,15 +22,13 @@
# android-support-v7-preference \
# android-support-v7-appcompat \
# android-support-v7-recyclerview \
-# android-support-v4 \
-# android-support-annotations
+# android-support-v4
#
# in their makefiles to include the resources in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-v14-preference
-LOCAL_SDK_VERSION := 14
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_SHARED_ANDROID_LIBRARIES := \
diff --git a/v14/preference/build.gradle b/v14/preference/build.gradle
index b834dd5..33d616e 100644
--- a/v14/preference/build.gradle
+++ b/v14/preference/build.gradle
@@ -30,6 +30,10 @@
android {
compileSdkVersion project.ext.currentSdk
+ defaultConfig {
+ minSdkVersion 14
+ }
+
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
main.java.srcDir 'src'
diff --git a/v14/preference/res/drawable-v21/preference_list_divider_material.xml b/v14/preference/res/drawable-v21/preference_list_divider_material.xml
new file mode 100644
index 0000000..a5913de
--- /dev/null
+++ b/v14/preference/res/drawable-v21/preference_list_divider_material.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="?android:attr/colorForeground">
+ <solid android:color="#1f000000" />
+ <size
+ android:height="1dp"
+ android:width="1dp" />
+</shape>
diff --git a/v14/preference/res/drawable/preference_list_divider_material.xml b/v14/preference/res/drawable/preference_list_divider_material.xml
index a5913de..fc91d07 100644
--- a/v14/preference/res/drawable/preference_list_divider_material.xml
+++ b/v14/preference/res/drawable/preference_list_divider_material.xml
@@ -15,8 +15,7 @@
limitations under the License
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:tint="?android:attr/colorForeground">
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#1f000000" />
<size
android:height="1dp"
diff --git a/v17/leanback/Android.mk b/v17/leanback/Android.mk
index b550fe0..c6a50b4 100644
--- a/v17/leanback/Android.mk
+++ b/v17/leanback/Android.mk
@@ -14,111 +14,27 @@
LOCAL_PATH:= $(call my-dir)
-# Build the resources using the latest applicable SDK version.
-# We do this here because the final static library must be compiled with an older
-# SDK version than the resources. The resources library and the R class that it
-# contains will not be linked into the final static library.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-v17-leanback-res
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# Base sub-library contains classes both needed by api-level specific libraries
-# (e.g. KitKat) and final static library.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v17-leanback-common
-LOCAL_SDK_VERSION := 17
-LOCAL_SRC_FILES := $(call all-java-files-under, common)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of API 23.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v17-leanback-api23
-LOCAL_SDK_VERSION := 23
-LOCAL_SRC_FILES := $(call all-java-files-under, api23)
-LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of API 21.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v17-leanback-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of KitKat APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v17-leanback-kitkat
-LOCAL_SDK_VERSION := 19
-LOCAL_SRC_FILES := $(call all-java-files-under, kitkat)
-LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JBMR2 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v17-leanback-jbmr2
-LOCAL_SDK_VERSION := 18
-LOCAL_SRC_FILES := $(call all-java-files-under, jbmr2)
-LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
# Here is the final static library that apps can link against.
# Applications that use this library must specify
#
# LOCAL_STATIC_ANDROID_LIBRARIES := \
# android-support-v17-leanback \
# android-support-v7-recyclerview \
-# android-support-v4 \
-# android-support-annotations
+# android-support-v4
#
# in their makefiles to include the resources and their dependencies in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-v17-leanback
-LOCAL_SDK_VERSION := 17
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-v17-leanback-kitkat \
- android-support-v17-leanback-jbmr2 \
- android-support-v17-leanback-api23 \
- android-support-v17-leanback-api21 \
- android-support-v17-leanback-common
-LOCAL_STATIC_ANDROID_LIBRARIES := \
- android-support-v17-leanback-res
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, common) \
+ $(call all-java-files-under, jbmr2) \
+ $(call all-java-files-under, kitkat) \
+ $(call all-java-files-under, api21) \
+ $(call all-java-files-under, api23) \
+ $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-v7-recyclerview \
android-support-compat \
@@ -131,51 +47,30 @@
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
-# ===========================================================
-# Common Droiddoc vars
-leanback.docs.src_files := \
- $(call all-java-files-under, src) \
- $(call all-html-files-under, src)
-leanback.docs.java_libraries := \
- android-support-annotations \
- android-support-v4 \
- android-support-v7-recyclerview \
- android-support-v17-leanback-res \
- android-support-v17-leanback
-
# Documentation
# ===========================================================
include $(CLEAR_VARS)
-
+LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-v17-leanback
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_MODULE_TAGS := optional
-
-gen_res_src_dirs := $(call intermediates-dir-for,JAVA_LIBRARIES,android-support-v17-leanback-res,,COMMON)/src
-
-LOCAL_SRC_FILES := $(leanback.docs.src_files)
-LOCAL_ADDITIONAL_JAVA_DIR := $(gen_res_src_dirs)
-
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-html-files-under, src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations \
+ android-support-v4 \
+ android-support-v7-recyclerview \
+ android-support-v17-leanback
+LOCAL_ADDITIONAL_JAVA_DIR := $(call intermediates-dir-for,JAVA_LIBRARIES,android-support-v17-leanback-res,,COMMON)/src
LOCAL_IS_HOST_MODULE := false
LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := build/tools/droiddoc/templates-sdk
-
-LOCAL_JAVA_LIBRARIES := $(leanback.docs.java_libraries)
-
LOCAL_DROIDDOC_OPTIONS := \
-offlinemode \
-hdf android.whichdoc offline \
-federate Android http://developer.android.com \
-federationapi Android prebuilts/sdk/api/17.txt \
-hide 113
-
include $(BUILD_DROIDDOC)
-
-# Cleanup temp vars
-# ===========================================================
-leanback.docs.src_files :=
-leanback.docs.java_libraries :=
-gen_res_src_dirs :=
-leanback_internal_api_file :=
-leanback_stubs_stamp :=
-leanback.docs.stubpackages :=
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java b/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
index 3cfc94f..d4f063d 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
@@ -18,9 +18,11 @@
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.v17.leanback.R;
import android.transition.Fade;
@@ -39,6 +41,8 @@
* Execute horizontal slide of 1/4 width and fade (to workaround bug 23718734)
* @hide
*/
+@RequiresApi(21)
+@TargetApi(21)
@RestrictTo(GROUP_ID)
public class FadeAndShortSlide extends Visibility {
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/SlideNoPropagation.java b/v17/leanback/api21/android/support/v17/leanback/transition/SlideNoPropagation.java
index 866b904..f5a1cc7 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/SlideNoPropagation.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/SlideNoPropagation.java
@@ -13,7 +13,9 @@
*/
package android.support.v17.leanback.transition;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.transition.Slide;
import android.util.AttributeSet;
@@ -23,6 +25,8 @@
/**
* @hide
*/
+@RequiresApi(21)
+@TargetApi(21)
@RestrictTo(GROUP_ID)
public class SlideNoPropagation extends Slide {
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
index 5c279b5..1fe0874 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
@@ -14,9 +14,10 @@
package android.support.v17.leanback.transition;
import android.R;
-import android.app.Fragment;
+import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
import android.transition.ChangeTransform;
import android.transition.Transition;
import android.transition.TransitionManager;
@@ -25,6 +26,8 @@
import android.view.Window;
import android.view.animation.AnimationUtils;
+@RequiresApi(21)
+@TargetApi(21)
final class TransitionHelperApi21 {
TransitionHelperApi21() {
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java b/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
index a423995..d493bdd 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
@@ -1,5 +1,7 @@
package android.support.v17.leanback.transition;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.v17.leanback.R;
@@ -21,6 +23,8 @@
* will not blink out or shift suddenly when the transition is interrupted.
* @hide
*/
+@RequiresApi(21)
+@TargetApi(21)
@RestrictTo(GROUP_ID)
class TranslationAnimationCreator {
diff --git a/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java
index a013ba1..c0ba7dd 100644
--- a/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java
+++ b/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java
@@ -13,11 +13,15 @@
*/
package android.support.v17.leanback.widget;
-import android.util.SparseArray;
+import android.annotation.TargetApi;
import android.graphics.Outline;
-import android.view.ViewOutlineProvider;
+import android.support.annotation.RequiresApi;
+import android.util.SparseArray;
import android.view.View;
+import android.view.ViewOutlineProvider;
+@RequiresApi(21)
+@TargetApi(21)
class RoundedRectHelperApi21 {
private static SparseArray<ViewOutlineProvider> sRoundedRectProvider;
diff --git a/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java
index 66f7687..35f2c51 100644
--- a/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java
+++ b/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java
@@ -13,11 +13,14 @@
*/
package android.support.v17.leanback.widget;
+import android.annotation.TargetApi;
import android.graphics.Outline;
+import android.support.annotation.RequiresApi;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
+@RequiresApi(21)
+@TargetApi(21)
class ShadowHelperApi21 {
static class ShadowImpl {
diff --git a/v17/leanback/api23/android/support/v17/leanback/app/PermissionHelper23.java b/v17/leanback/api23/android/support/v17/leanback/app/PermissionHelper23.java
index 19cf5d6..82ba369 100644
--- a/v17/leanback/api23/android/support/v17/leanback/app/PermissionHelper23.java
+++ b/v17/leanback/api23/android/support/v17/leanback/app/PermissionHelper23.java
@@ -13,6 +13,11 @@
*/
package android.support.v17.leanback.app;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(23)
+@TargetApi(23)
class PermissionHelper23 {
public static void requestPermissions(android.app.Fragment fragment, String[] permissions,
diff --git a/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java b/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java
index c4760d4..f00f43d 100644
--- a/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java
+++ b/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java
@@ -13,16 +13,13 @@
*/
package android.support.v17.leanback.widget;
-import android.support.v17.leanback.R;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.Outline;
-import android.graphics.drawable.ColorDrawable;
+import android.annotation.TargetApi;
import android.graphics.drawable.Drawable;
-import android.view.ViewGroup;
+import android.support.annotation.RequiresApi;
import android.view.View;
-import android.view.ViewOutlineProvider;
+@RequiresApi(23)
+@TargetApi(23)
class ForegroundHelperApi23 {
public static Drawable getForeground(View view) {
diff --git a/v17/leanback/build.gradle b/v17/leanback/build.gradle
index d7eea85..270f552 100644
--- a/v17/leanback/build.gradle
+++ b/v17/leanback/build.gradle
@@ -1,5 +1,4 @@
apply plugin: 'com.android.library'
-
archivesBaseName = 'leanback-v17'
dependencies {
@@ -21,22 +20,24 @@
}
android {
- // WARNING: should be 17
compileSdkVersion project.ext.currentSdk
defaultConfig {
minSdkVersion 17
- // TODO: get target from branch
- //targetSdkVersion 19
-
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = ['common', 'jbmr2', 'kitkat', 'api21', 'api23', 'src']
- main.aidl.srcDirs = ['common', 'jbmr2', 'kitkat', 'api21', 'api23', 'src']
- main.res.srcDirs = ['res']
+ main.java.srcDirs = [
+ 'common',
+ 'jbmr2',
+ 'kitkat',
+ 'api21',
+ 'api23',
+ 'src'
+ ]
+ main.res.srcDir 'res'
androidTest.setRoot('tests')
androidTest.java.srcDir 'tests/java'
diff --git a/v17/leanback/jbmr2/android/support/v17/leanback/widget/ShadowHelperJbmr2.java b/v17/leanback/jbmr2/android/support/v17/leanback/widget/ShadowHelperJbmr2.java
index ad53425..4ee6b29 100644
--- a/v17/leanback/jbmr2/android/support/v17/leanback/widget/ShadowHelperJbmr2.java
+++ b/v17/leanback/jbmr2/android/support/v17/leanback/widget/ShadowHelperJbmr2.java
@@ -13,11 +13,15 @@
*/
package android.support.v17.leanback.widget;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.support.v17.leanback.R;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+@RequiresApi(18)
+@TargetApi(18)
class ShadowHelperJbmr2 {
static class ShadowImpl {
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java
index 0cc9081..b6a82b7 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java
@@ -13,13 +13,15 @@
*/
package android.support.v17.leanback.transition;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.support.annotation.RequiresApi;
import android.support.v17.leanback.R;
import android.view.Gravity;
import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
+@RequiresApi(19)
+@TargetApi(19)
class LeanbackTransitionHelperKitKat {
static public Object loadTitleInTransition(Context context) {
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/Scale.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/Scale.java
index 2bdc3aa..5fbf414 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/Scale.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/Scale.java
@@ -17,11 +17,15 @@
import android.animation.Animator;
import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.view.View;
import android.view.ViewGroup;
import android.transition.Transition;
import android.transition.TransitionValues;
+@RequiresApi(19)
+@TargetApi(19)
class Scale extends Transition {
private static final String PROPNAME_SCALE = "android:leanback:scale";
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/SlideKitkat.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/SlideKitkat.java
index 686c4fa..e8e4c10 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/SlideKitkat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/SlideKitkat.java
@@ -17,15 +17,16 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
+import android.support.v17.leanback.R;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.Property;
import android.view.Gravity;
import android.view.View;
@@ -33,15 +34,13 @@
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
-import android.transition.Visibility;
-import android.transition.Transition;
-import android.transition.TransitionValues;
-import android.support.v17.leanback.R;
/**
* Slide distance toward/from a edge.
* This is a limited Slide implementation for KitKat without propagation support.
*/
+@RequiresApi(19)
+@TargetApi(19)
class SlideKitkat extends Visibility {
private static final String TAG = "SlideKitkat";
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java
index 221b84a..777b34b 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java
@@ -15,7 +15,9 @@
import android.animation.Animator;
import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.support.annotation.RequiresApi;
import android.transition.AutoTransition;
import android.transition.ChangeBounds;
import android.transition.Fade;
@@ -33,6 +35,8 @@
import java.util.ArrayList;
import java.util.HashMap;
+@RequiresApi(19)
+@TargetApi(19)
final class TransitionHelperKitkat {
TransitionHelperKitkat() {
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/widget/BackgroundHelperKitkat.java b/v17/leanback/kitkat/android/support/v17/leanback/widget/BackgroundHelperKitkat.java
index 2b095fa..49cb35e 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/widget/BackgroundHelperKitkat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/widget/BackgroundHelperKitkat.java
@@ -15,9 +15,13 @@
*/
package android.support.v17.leanback.widget;
+import android.annotation.TargetApi;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
import android.view.View;
+@RequiresApi(19)
+@TargetApi(19)
class BackgroundHelperKitkat {
public static void setBackgroundPreservingAlpha(View view, Drawable drawable) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
index 98bcb3b..0bd03f1 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
@@ -30,13 +30,34 @@
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
+import java.lang.ref.WeakReference;
import java.util.List;
final class DetailsOverviewSharedElementHelper extends SharedElementCallback {
- static final String TAG = "DetailsOverviewSharedElementHelper";
+ static final String TAG = "DetailsTransitionHelper";
static final boolean DEBUG = false;
+ static class TransitionTimeOutRunnable implements Runnable {
+ WeakReference<DetailsOverviewSharedElementHelper> mHelperRef;
+
+ TransitionTimeOutRunnable(DetailsOverviewSharedElementHelper helper) {
+ mHelperRef = new WeakReference<DetailsOverviewSharedElementHelper>(helper);
+ }
+
+ @Override
+ public void run() {
+ DetailsOverviewSharedElementHelper helper = mHelperRef.get();
+ if (helper == null) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "timeout " + helper.mActivityToRunTransition);
+ }
+ helper.startPostponedEnterTransition();
+ }
+ }
+
ViewHolder mViewHolder;
Activity mActivityToRunTransition;
boolean mStartedPostpone;
@@ -180,18 +201,7 @@
ActivityCompat.setEnterSharedElementCallback(mActivityToRunTransition, this);
ActivityCompat.postponeEnterTransition(mActivityToRunTransition);
if (timeoutMs > 0) {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- if (mStartedPostpone) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "timeout " + mActivityToRunTransition);
- }
- startPostponedEnterTransition();
- }
- }, timeoutMs);
+ new Handler().postDelayed(new TransitionTimeOutRunnable(this), timeoutMs);
}
}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
index 96379d7..570a7f2 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
@@ -23,6 +23,8 @@
import android.text.TextUtils;
import android.util.Log;
+import java.lang.ref.WeakReference;
+
/**
* Helper class to assist delayed shared element activity transition for view created by
* {@link FullWidthDetailsOverviewRowPresenter}. User must call
@@ -35,11 +37,31 @@
public class FullWidthDetailsOverviewSharedElementHelper extends
FullWidthDetailsOverviewRowPresenter.Listener {
- static final String TAG = "FullWidthDetailsOverviewSharedElementHelper";
+ static final String TAG = "DetailsTransitionHelper";
static final boolean DEBUG = false;
private static final long DEFAULT_TIMEOUT = 5000;
+ static class TransitionTimeOutRunnable implements Runnable {
+ WeakReference<FullWidthDetailsOverviewSharedElementHelper> mHelperRef;
+
+ TransitionTimeOutRunnable(FullWidthDetailsOverviewSharedElementHelper helper) {
+ mHelperRef = new WeakReference<FullWidthDetailsOverviewSharedElementHelper>(helper);
+ }
+
+ @Override
+ public void run() {
+ FullWidthDetailsOverviewSharedElementHelper helper = mHelperRef.get();
+ if (helper == null) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "timeout " + helper.mActivityToRunTransition);
+ }
+ helper.startPostponedEnterTransition();
+ }
+ }
+
ViewHolder mViewHolder;
Activity mActivityToRunTransition;
private boolean mStartedPostpone;
@@ -69,15 +91,7 @@
setAutoStartSharedElementTransition(transition != null);
ActivityCompat.postponeEnterTransition(mActivityToRunTransition);
if (timeoutMs > 0) {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- if (DEBUG) {
- Log.d(TAG, "timeout " + mActivityToRunTransition);
- }
- startPostponedEnterTransitionInternal();
- }
- }, timeoutMs);
+ new Handler().postDelayed(new TransitionTimeOutRunnable(this), timeoutMs);
}
}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
index 7f28705..888cc8f 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
@@ -184,11 +184,17 @@
if (mGuidanceContainer != null) {
CharSequence contentDescription = mGuidanceContainer.getContentDescription();
if (TextUtils.isEmpty(contentDescription)) {
- mGuidanceContainer.setContentDescription(new StringBuilder()
- .append(guidance.getBreadcrumb()).append('\n')
- .append(guidance.getTitle()).append('\n')
- .append(guidance.getDescription())
- .toString());
+ StringBuilder builder = new StringBuilder();
+ if (!TextUtils.isEmpty(guidance.getBreadcrumb())) {
+ builder.append(guidance.getBreadcrumb()).append('\n');
+ }
+ if (!TextUtils.isEmpty(guidance.getTitle())) {
+ builder.append(guidance.getTitle()).append('\n');
+ }
+ if (!TextUtils.isEmpty(guidance.getDescription())) {
+ builder.append(guidance.getDescription()).append('\n');
+ }
+ mGuidanceContainer.setContentDescription(builder);
}
}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTest.java
index 4f43de0..192b20e 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTest.java
@@ -171,17 +171,13 @@
}
});
PollingCheck.waitFor(new EnterTransitionFinish(second));
- verify(first, times(2)).onCreate(any(Bundle.class));
verify(first, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
any(Bundle.class), any(View.class));
- verify(first, times(2)).onCreateActions(any(List.class), any(Bundle.class));
verify(first, times(1)).onDestroy();
verify(second, times(2)).onCreate(any(Bundle.class));
verify(second, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
any(Bundle.class), any(View.class));
verify(second, times(1)).onDestroy();
- assertEquals("modified text", first.getFragment().findActionById(1001).getTitle());
- assertEquals("text", first.getFragment().findActionById(1002).getTitle());
sendKey(KeyEvent.KEYCODE_BACK);
PollingCheck.waitFor(new EnterTransitionFinish(first));
@@ -189,6 +185,10 @@
verify(second, times(2)).onStop();
verify(second, times(2)).onDestroyView();
verify(second, times(2)).onDestroy();
+ assertEquals("modified text", first.getFragment().findActionById(1001).getTitle());
+ assertEquals("text", first.getFragment().findActionById(1002).getTitle());
+ verify(first, times(2)).onCreate(any(Bundle.class));
+ verify(first, times(2)).onCreateActions(any(List.class), any(Bundle.class));
verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
any(Bundle.class), any(View.class));
}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTest.java
index 66ade14..879292f 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTest.java
@@ -174,17 +174,13 @@
}
});
PollingCheck.waitFor(new EnterTransitionFinish(second));
- verify(first, times(2)).onCreate(any(Bundle.class));
verify(first, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
any(Bundle.class), any(View.class));
- verify(first, times(2)).onCreateActions(any(List.class), any(Bundle.class));
verify(first, times(1)).onDestroy();
verify(second, times(2)).onCreate(any(Bundle.class));
verify(second, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
any(Bundle.class), any(View.class));
verify(second, times(1)).onDestroy();
- assertEquals("modified text", first.getFragment().findActionById(1001).getTitle());
- assertEquals("text", first.getFragment().findActionById(1002).getTitle());
sendKey(KeyEvent.KEYCODE_BACK);
PollingCheck.waitFor(new EnterTransitionFinish(first));
@@ -192,6 +188,10 @@
verify(second, times(2)).onStop();
verify(second, times(2)).onDestroyView();
verify(second, times(2)).onDestroy();
+ assertEquals("modified text", first.getFragment().findActionById(1001).getTitle());
+ assertEquals("text", first.getFragment().findActionById(1002).getTitle());
+ verify(first, times(2)).onCreate(any(Bundle.class));
+ verify(first, times(2)).onCreateActions(any(List.class), any(Bundle.class));
verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
any(Bundle.class), any(View.class));
}
diff --git a/v17/preference-leanback/Android.mk b/v17/preference-leanback/Android.mk
index 2058002..263d334 100644
--- a/v17/preference-leanback/Android.mk
+++ b/v17/preference-leanback/Android.mk
@@ -14,45 +14,6 @@
LOCAL_PATH := $(call my-dir)
-# Android libraries referenced by this module's resources.
-resource_libs := \
- android-support-v17-leanback \
- android-support-v14-preference \
- android-support-v7-preference \
- android-support-v7-appcompat \
- android-support-v7-recyclerview \
- android-support-annotations
-
-# Build the resources using the latest applicable SDK version.
-# We do this here because the final static library must be compiled with an older
-# SDK version than the resources. The resources library and the R class that it
-# contains will not be linked into the final static library.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-v17-preference-leanback-res
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := $(resource_libs)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_JAR_EXCLUDE_FILES := none
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of API 21.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v17-preference-leanback-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_JAVA_LIBRARIES := \
- android-support-v17-preference-leanback-res \
- android-support-v17-leanback
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
# Here is the final static library that apps can link against.
# Applications that use this library must specify
#
@@ -63,23 +24,25 @@
# android-support-v7-preference \
# android-support-v7-appcompat \
# android-support-v7-recyclerview \
-# android-support-v4 \
-# android-support-annotations
+# android-support-v4
#
# in their makefiles to include the resources in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-v17-preference-leanback
-LOCAL_SDK_VERSION := 17
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-v17-preference-leanback-api21
-LOCAL_STATIC_ANDROID_LIBRARIES := \
- android-support-v17-preference-leanback-res
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under,api21) \
+ $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_SHARED_ANDROID_LIBRARIES := \
- $(resource_libs) \
- android-support-v4
+ android-support-v17-leanback \
+ android-support-v14-preference \
+ android-support-v7-preference \
+ android-support-v7-appcompat \
+ android-support-v7-recyclerview \
+ android-support-v4 \
+ android-support-annotations
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/v17/preference-leanback/api21/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java b/v17/preference-leanback/api21/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java
index b67a56f..1054949 100644
--- a/v17/preference-leanback/api21/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java
+++ b/v17/preference-leanback/api21/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java
@@ -16,8 +16,10 @@
package android.support.v17.internal.widget;
+import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Outline;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.util.AttributeSet;
import android.view.View;
@@ -32,6 +34,8 @@
*
* @hide
*/
+@RequiresApi(21)
+@TargetApi(21)
@RestrictTo(GROUP_ID)
public class OutlineOnlyWithChildrenFrameLayout extends FrameLayout {
diff --git a/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java b/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
index 322fbe3..1b320a5 100644
--- a/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
+++ b/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
@@ -16,6 +16,8 @@
package android.support.v17.preference;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.v17.leanback.transition.FadeAndShortSlide;
import android.app.Fragment;
@@ -27,6 +29,8 @@
/**
* @hide
*/
+@RequiresApi(21)
+@TargetApi(21)
@RestrictTo(GROUP_ID)
public class LeanbackPreferenceFragmentTransitionHelperApi21 {
diff --git a/v17/preference-leanback/build.gradle b/v17/preference-leanback/build.gradle
index dbdce7e..2f4ec5b 100644
--- a/v17/preference-leanback/build.gradle
+++ b/v17/preference-leanback/build.gradle
@@ -1,23 +1,4 @@
-/*
- * 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
- */
-
-
-
apply plugin: 'com.android.library'
-
archivesBaseName = 'preference-leanback-v17'
dependencies {
@@ -34,16 +15,14 @@
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = ['src', 'api21']
+ main.java.srcDirs = [
+ 'api21',
+ 'src'
+ ]
main.res.srcDir 'res'
- main.assets.srcDir 'assets'
- main.resources.srcDir 'src'
+ }
- // this moves src/instrumentTest to tests so all folders follow:
- // tests/java, tests/res, tests/assets, ...
- // This is a *reset* so it replaces the default paths
- androidTest.setRoot('tests')
- androidTest.java.srcDir 'tests/src'
+ lintOptions {
}
compileOptions {
diff --git a/v17/preference-leanback/res/layout-v21/leanback_preference_category.xml b/v17/preference-leanback/res/layout-v21/leanback_preference_category.xml
new file mode 100644
index 0000000..3e04793
--- /dev/null
+++ b/v17/preference-leanback/res/layout-v21/leanback_preference_category.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/lb_preference_category_height"
+ android:clipToPadding="false"
+ android:paddingStart="@dimen/lb_preference_item_padding_start"
+ android:paddingEnd="@dimen/lb_preference_item_padding_end">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|center_vertical"
+ android:fontFamily="sans-serif-condensed"
+ android:textColor="?android:attr/colorAccent"
+ android:textSize="@dimen/lb_preference_category_text_size"/>
+</FrameLayout>
diff --git a/v17/preference-leanback/res/layout/leanback_preference_category.xml b/v17/preference-leanback/res/layout/leanback_preference_category.xml
index 97bb3f0..cfc5f1a 100644
--- a/v17/preference-leanback/res/layout/leanback_preference_category.xml
+++ b/v17/preference-leanback/res/layout/leanback_preference_category.xml
@@ -28,6 +28,6 @@
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:fontFamily="sans-serif-condensed"
- android:textColor="?android:attr/colorAccent"
+ android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/lb_preference_category_text_size"/>
</FrameLayout>
diff --git a/v4/Android.mk b/v4/Android.mk
index d183f4d..a9c9145 100644
--- a/v4/Android.mk
+++ b/v4/Android.mk
@@ -24,14 +24,18 @@
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-v4
-LOCAL_SDK_VERSION := 9
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+# Some projects expect to inherit android-support-annotations from
+# android-support-v4, so we need to keep it static until they can be fixed.
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-compat \
android-support-media-compat \
android-support-core-utils \
android-support-core-ui \
- android-support-fragment
+ android-support-fragment \
+ android-support-annotations
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/v7/appcompat/Android.mk b/v7/appcompat/Android.mk
index 21e9e8b..93baa95 100644
--- a/v7/appcompat/Android.mk
+++ b/v7/appcompat/Android.mk
@@ -31,7 +31,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-vectordrawable \
android-support-animatedvectordrawable
-LOCAL_JAVA_LIBRARIES := android-support-v4
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-v4
LOCAL_AAPT_FLAGS := --no-version-vectors
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
diff --git a/v7/appcompat/build.gradle b/v7/appcompat/build.gradle
index 45ec21f..e6bb42b 100644
--- a/v7/appcompat/build.gradle
+++ b/v7/appcompat/build.gradle
@@ -3,6 +3,7 @@
archivesBaseName = 'appcompat-v7'
dependencies {
+ compile project(':support-annotations')
compile project(':support-v4')
compile project(':support-vector-drawable')
compile project(':support-animated-vector-drawable')
diff --git a/v7/appcompat/res/layout/notification_media_action.xml b/v7/appcompat/res/layout-v11/notification_media_action.xml
similarity index 100%
rename from v7/appcompat/res/layout/notification_media_action.xml
rename to v7/appcompat/res/layout-v11/notification_media_action.xml
diff --git a/v7/appcompat/res/layout/notification_media_cancel_action.xml b/v7/appcompat/res/layout-v11/notification_media_cancel_action.xml
similarity index 100%
rename from v7/appcompat/res/layout/notification_media_cancel_action.xml
rename to v7/appcompat/res/layout-v11/notification_media_cancel_action.xml
diff --git a/v7/appcompat/res/layout/notification_template_big_media.xml b/v7/appcompat/res/layout-v11/notification_template_big_media.xml
similarity index 100%
rename from v7/appcompat/res/layout/notification_template_big_media.xml
rename to v7/appcompat/res/layout-v11/notification_template_big_media.xml
diff --git a/v7/appcompat/res/layout/notification_template_big_media_custom.xml b/v7/appcompat/res/layout-v11/notification_template_big_media_custom.xml
similarity index 100%
rename from v7/appcompat/res/layout/notification_template_big_media_custom.xml
rename to v7/appcompat/res/layout-v11/notification_template_big_media_custom.xml
diff --git a/v7/appcompat/res/layout/notification_template_big_media_narrow.xml b/v7/appcompat/res/layout-v11/notification_template_big_media_narrow.xml
similarity index 100%
rename from v7/appcompat/res/layout/notification_template_big_media_narrow.xml
rename to v7/appcompat/res/layout-v11/notification_template_big_media_narrow.xml
diff --git a/v7/appcompat/res/layout/notification_template_big_media_narrow_custom.xml b/v7/appcompat/res/layout-v11/notification_template_big_media_narrow_custom.xml
similarity index 100%
rename from v7/appcompat/res/layout/notification_template_big_media_narrow_custom.xml
rename to v7/appcompat/res/layout-v11/notification_template_big_media_narrow_custom.xml
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java b/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java
index 3db19dc..1fe51b8 100644
--- a/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java
+++ b/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java
@@ -15,6 +15,7 @@
*/
package android.support.v7.app;
+import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
@@ -24,6 +25,7 @@
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.support.annotation.StringRes;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
@@ -207,6 +209,8 @@
mActivityImpl = ((DelegateProvider) activity).getDrawerToggleDelegate();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mActivityImpl = new JellybeanMr2Delegate(activity);
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ mActivityImpl = new IcsDelegate(activity);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mActivityImpl = new HoneycombDelegate(activity);
} else {
@@ -492,8 +496,10 @@
}
/**
- * Delegate if SDK version is between honeycomb and JBMR2
+ * Delegate if SDK version is between Honeycomb and ICS
*/
+ @RequiresApi(11)
+ @TargetApi(11)
private static class HoneycombDelegate implements Delegate {
final Activity mActivity;
@@ -510,14 +516,7 @@
@Override
public Context getActionBarThemedContext() {
- final ActionBar actionBar = mActivity.getActionBar();
- final Context context;
- if (actionBar != null) {
- context = actionBar.getThemedContext();
- } else {
- context = mActivity;
- }
- return context;
+ return mActivity;
}
@Override
@@ -546,8 +545,34 @@
}
/**
+ * Delegate if SDK version is between ICS and JBMR2
+ */
+ @RequiresApi(14)
+ @TargetApi(14)
+ private static class IcsDelegate extends HoneycombDelegate {
+
+ IcsDelegate(Activity activity) {
+ super(activity);
+ }
+
+ @Override
+ public Context getActionBarThemedContext() {
+ final ActionBar actionBar = mActivity.getActionBar();
+ final Context context;
+ if (actionBar != null) {
+ context = actionBar.getThemedContext();
+ } else {
+ context = mActivity;
+ }
+ return context;
+ }
+ }
+
+ /**
* Delegate if SDK version is JB MR2 or newer
*/
+ @RequiresApi(18)
+ @TargetApi(18)
private static class JellybeanMr2Delegate implements Delegate {
final Activity mActivity;
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java b/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java
index f25365e..1463ecb 100644
--- a/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java
+++ b/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java
@@ -18,11 +18,13 @@
package android.support.v7.app;
import android.R;
+import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.support.annotation.RequiresApi;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -39,6 +41,8 @@
*
* Moved from Support-v4
*/
+@RequiresApi(11)
+@TargetApi(11)
class ActionBarDrawerToggleHoneycomb {
private static final String TAG = "ActionBarDrawerToggleHoneycomb";
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
index b26f3df..6da5250 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
@@ -16,6 +16,7 @@
package android.support.v7.app;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
@@ -23,6 +24,7 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
+import android.support.annotation.RequiresApi;
import android.support.v7.appcompat.R;
import android.support.v7.view.ActionMode;
import android.support.v7.view.SupportMenuInflater;
@@ -36,6 +38,8 @@
import android.view.View;
import android.view.Window;
+@RequiresApi(9)
+@TargetApi(9)
abstract class AppCompatDelegateImplBase extends AppCompatDelegate {
static final boolean DEBUG = false;
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplN.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplN.java
index 8313e49..9f162dd 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplN.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplN.java
@@ -16,13 +16,17 @@
package android.support.v7.app;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.support.annotation.RequiresApi;
import android.view.KeyboardShortcutGroup;
import android.view.Menu;
import android.view.Window;
import java.util.List;
+@RequiresApi(24)
+@TargetApi(24)
class AppCompatDelegateImplN extends AppCompatDelegateImplV23 {
AppCompatDelegateImplN(Context context, Window window, AppCompatCallback callback) {
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java
index 9d882c8..f3fda8e 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java
@@ -16,11 +16,15 @@
package android.support.v7.app;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
import android.view.Window;
+@RequiresApi(11)
+@TargetApi(11)
class AppCompatDelegateImplV11 extends AppCompatDelegateImplV9 {
AppCompatDelegateImplV11(Context context, Window window, AppCompatCallback callback) {
@@ -28,8 +32,13 @@
}
@Override
+ public boolean hasWindowFeature(int featureId) {
+ return super.hasWindowFeature(featureId) || mWindow.hasFeature(featureId);
+ }
+
+ @Override
View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) {
- // On Honeycomb+, Activity's private inflater factory will handle calling it's
+ // On Honeycomb+, Activity's private inflater factory will handle calling its
// onCreateView(...)
return null;
}
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
index 631fc35..8f5a1d2 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
@@ -16,6 +16,7 @@
package android.support.v7.app;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -28,6 +29,7 @@
import android.content.res.Resources;
import android.os.Bundle;
import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
import android.support.annotation.VisibleForTesting;
import android.support.v7.view.SupportActionModeWrapper;
import android.util.DisplayMetrics;
@@ -35,6 +37,8 @@
import android.view.ActionMode;
import android.view.Window;
+@RequiresApi(14)
+@TargetApi(14)
class AppCompatDelegateImplV14 extends AppCompatDelegateImplV11 {
private static final String KEY_LOCAL_NIGHT_MODE = "appcompat:local_night_mode";
@@ -231,7 +235,7 @@
return true;
} else {
if (DEBUG) {
- Log.d(TAG, "applyNightMode() | Night mode has not changed. Skipping");
+ Log.d(TAG, "applyNightMode() | Skipping. Night mode has not changed: " + mode);
}
}
return false;
@@ -321,6 +325,7 @@
@ApplyableNightMode
final int getApplyableNightMode() {
+ mIsNight = mTwilightManager.isNight();
return mIsNight ? MODE_NIGHT_YES : MODE_NIGHT_NO;
}
@@ -336,7 +341,7 @@
cleanup();
// If we're set to AUTO, we register a receiver to be notified on time changes. The
- // system only send the tick out every minute, but that's enough fidelity for our use
+ // system only sends the tick out every minute, but that's enough fidelity for our use
// case
if (mAutoTimeChangeReceiver == null) {
mAutoTimeChangeReceiver = new BroadcastReceiver() {
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java
index 5b3beec..d061114 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java
@@ -16,11 +16,15 @@
package android.support.v7.app;
+import android.annotation.TargetApi;
import android.app.UiModeManager;
import android.content.Context;
+import android.support.annotation.RequiresApi;
import android.view.ActionMode;
import android.view.Window;
+@RequiresApi(23)
+@TargetApi(23)
class AppCompatDelegateImplV23 extends AppCompatDelegateImplV14 {
private final UiModeManager mUiModeManager;
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV9.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV9.java
index 899d8b1..b52c0ba 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV9.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV9.java
@@ -20,6 +20,7 @@
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.Window.FEATURE_OPTIONS_PANEL;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
@@ -36,6 +37,7 @@
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.support.v4.app.NavUtils;
import android.support.v4.os.ParcelableCompat;
import android.support.v4.os.ParcelableCompatCreatorCallbacks;
@@ -90,6 +92,8 @@
import android.widget.PopupWindow;
import android.widget.TextView;
+@RequiresApi(9)
+@TargetApi(9)
class AppCompatDelegateImplV9 extends AppCompatDelegateImplBase
implements MenuBuilder.Callback, LayoutInflaterFactory {
@@ -619,7 +623,7 @@
case Window.FEATURE_NO_TITLE:
return mWindowNoTitle;
}
- return mWindow.hasFeature(featureId);
+ return false;
}
@Override
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompat.java b/v7/appcompat/src/android/support/v7/app/NotificationCompat.java
index 9d8cf07..d1c3f45 100644
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompat.java
+++ b/v7/appcompat/src/android/support/v7/app/NotificationCompat.java
@@ -18,6 +18,7 @@
import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+import android.annotation.TargetApi;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
@@ -27,6 +28,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.v4.app.BundleCompat;
import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
@@ -82,6 +84,8 @@
return null;
}
+ @RequiresApi(24)
+ @TargetApi(24)
private static void addStyleToBuilderApi24(NotificationBuilderWithBuilderAccessor builder,
android.support.v4.app.NotificationCompat.Builder b) {
if (b.mStyle instanceof DecoratedCustomViewStyle) {
@@ -93,6 +97,8 @@
}
}
+ @RequiresApi(21)
+ @TargetApi(21)
private static RemoteViews addStyleGetContentViewLollipop(
NotificationBuilderWithBuilderAccessor builder,
android.support.v4.app.NotificationCompat.Builder b) {
@@ -131,6 +137,8 @@
return addStyleGetContentViewJellybean(builder, b);
}
+ @RequiresApi(16)
+ @TargetApi(16)
private static RemoteViews addStyleGetContentViewJellybean(
NotificationBuilderWithBuilderAccessor builder,
android.support.v4.app.NotificationCompat.Builder b) {
@@ -217,6 +225,8 @@
return false;
}
+ @RequiresApi(14)
+ @TargetApi(14)
private static RemoteViews addStyleGetContentViewIcs(
NotificationBuilderWithBuilderAccessor builder,
android.support.v4.app.NotificationCompat.Builder b) {
@@ -240,6 +250,8 @@
return null;
}
+ @RequiresApi(16)
+ @TargetApi(16)
private static void addBigStyleToBuilderJellybean(Notification n,
android.support.v4.app.NotificationCompat.Builder b) {
if (b.mStyle instanceof MediaStyle) {
@@ -259,7 +271,7 @@
innerView);
}
} else if (b.mStyle instanceof DecoratedCustomViewStyle) {
- addDecoratedBigStyleToBuilder(n, b);
+ addDecoratedBigStyleToBuilderJellybean(n, b);
}
}
@@ -279,7 +291,9 @@
return remoteViews;
}
- private static void addDecoratedBigStyleToBuilder(Notification n,
+ @RequiresApi(16)
+ @TargetApi(16)
+ private static void addDecoratedBigStyleToBuilderJellybean(Notification n,
android.support.v4.app.NotificationCompat.Builder b) {
RemoteViews bigContentView = b.getBigContentView();
RemoteViews innerView = bigContentView != null ? bigContentView : b.getContentView();
@@ -296,7 +310,9 @@
n.bigContentView = remoteViews;
}
- private static void addDecoratedHeadsUpToBuilder(Notification n,
+ @RequiresApi(21)
+ @TargetApi(21)
+ private static void addDecoratedHeadsUpToBuilderLollipop(Notification n,
android.support.v4.app.NotificationCompat.Builder b) {
RemoteViews headsUp = b.getHeadsUpContentView();
RemoteViews innerView = headsUp != null ? headsUp : b.getContentView();
@@ -313,6 +329,8 @@
n.headsUpContentView = remoteViews;
}
+ @RequiresApi(21)
+ @TargetApi(21)
private static void addBigStyleToBuilderLollipop(Notification n,
android.support.v4.app.NotificationCompat.Builder b) {
RemoteViews innerView = b.getBigContentView() != null
@@ -328,7 +346,7 @@
innerView);
setBackgroundColor(b.mContext, n.bigContentView, b.getColor());
} else if (b.mStyle instanceof DecoratedCustomViewStyle) {
- addDecoratedBigStyleToBuilder(n, b);
+ addDecoratedBigStyleToBuilderJellybean(n, b);
}
}
@@ -340,6 +358,8 @@
views.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor", color);
}
+ @RequiresApi(21)
+ @TargetApi(21)
private static void addHeadsUpToBuilderLollipop(Notification n,
android.support.v4.app.NotificationCompat.Builder b) {
RemoteViews innerView = b.getHeadsUpContentView() != null
@@ -355,7 +375,7 @@
innerView);
setBackgroundColor(b.mContext, n.headsUpContentView, b.getColor());
} else if (b.mStyle instanceof DecoratedCustomViewStyle) {
- addDecoratedHeadsUpToBuilder(n, b);
+ addDecoratedHeadsUpToBuilderLollipop(n, b);
}
}
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl21.java b/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl21.java
index cfa8839..9b0f028 100644
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl21.java
+++ b/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl21.java
@@ -16,10 +16,14 @@
package android.support.v7.app;
+import android.annotation.TargetApi;
import android.app.Notification;
import android.media.session.MediaSession;
+import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
+@RequiresApi(21)
+@TargetApi(21)
class NotificationCompatImpl21 {
public static void addMediaStyle(NotificationBuilderWithBuilderAccessor b,
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl24.java b/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl24.java
index 0547ad4..a65751b 100644
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl24.java
+++ b/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl24.java
@@ -16,9 +16,13 @@
package android.support.v7.app;
+import android.annotation.TargetApi;
import android.app.Notification;
+import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
+@RequiresApi(24)
+@TargetApi(24)
class NotificationCompatImpl24 {
public static void addDecoratedCustomViewStyle(NotificationBuilderWithBuilderAccessor b) {
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompatImplBase.java b/v7/appcompat/src/android/support/v7/app/NotificationCompatImplBase.java
index a37c921..a6e73ef 100644
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompatImplBase.java
+++ b/v7/appcompat/src/android/support/v7/app/NotificationCompatImplBase.java
@@ -16,6 +16,7 @@
package android.support.v7.app;
+import android.annotation.TargetApi;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
@@ -28,6 +29,7 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.SystemClock;
+import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompatBase;
@@ -44,12 +46,16 @@
* Helper class to generate MediaStyle notifications for pre-Lollipop platforms. Overrides
* contentView and bigContentView of the notification.
*/
+@RequiresApi(9)
+@TargetApi(9)
class NotificationCompatImplBase {
static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
static final int MAX_MEDIA_BUTTONS = 5;
private static final int MAX_ACTION_BUTTONS = 3;
+ @RequiresApi(11)
+ @TargetApi(11)
public static <T extends NotificationCompatBase.Action> RemoteViews overrideContentViewMedia(
NotificationBuilderWithBuilderAccessor builder,
Context context, CharSequence contentTitle, CharSequence contentText,
@@ -68,6 +74,8 @@
return views;
}
+ @RequiresApi(11)
+ @TargetApi(11)
private static <T extends NotificationCompatBase.Action> RemoteViews generateContentViewMedia(
Context context, CharSequence contentTitle, CharSequence contentText,
CharSequence contentInfo, int number, Bitmap largeIcon, CharSequence subText,
@@ -112,6 +120,8 @@
return view;
}
+ @RequiresApi(16)
+ @TargetApi(16)
public static <T extends NotificationCompatBase.Action> void overrideMediaBigContentView(
Notification n, Context context, CharSequence contentTitle, CharSequence contentText,
CharSequence contentInfo, int number, Bitmap largeIcon, CharSequence subText,
@@ -119,13 +129,15 @@
boolean showCancelButton, PendingIntent cancelButtonIntent,
boolean decoratedCustomView) {
n.bigContentView = generateMediaBigView(context, contentTitle, contentText, contentInfo,
- number, largeIcon, subText, useChronometer, when, priority, color,
- actions, showCancelButton, cancelButtonIntent, decoratedCustomView);
+ number, largeIcon, subText, useChronometer, when, priority, color, actions,
+ showCancelButton, cancelButtonIntent, decoratedCustomView);
if (showCancelButton) {
n.flags |= Notification.FLAG_ONGOING_EVENT;
}
}
+ @RequiresApi(11)
+ @TargetApi(11)
public static <T extends NotificationCompatBase.Action> RemoteViews generateMediaBigView(
Context context, CharSequence contentTitle, CharSequence contentText,
CharSequence contentInfo, int number, Bitmap largeIcon, CharSequence subText,
@@ -156,6 +168,8 @@
return big;
}
+ @RequiresApi(11)
+ @TargetApi(11)
private static RemoteViews generateMediaActionButton(Context context,
NotificationCompatBase.Action action) {
final boolean tombstone = (action.getActionIntent() == null);
@@ -165,12 +179,14 @@
if (!tombstone) {
button.setOnClickPendingIntent(R.id.action0, action.getActionIntent());
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+ if (Build.VERSION.SDK_INT >= 15) {
button.setContentDescription(R.id.action0, action.getTitle());
}
return button;
}
+ @RequiresApi(11)
+ @TargetApi(11)
private static int getBigMediaLayoutResource(boolean decoratedCustomView, int actionCount) {
if (actionCount <= 3) {
return decoratedCustomView
@@ -223,7 +239,9 @@
if (!tombstone) {
button.setOnClickPendingIntent(R.id.action_container, action.actionIntent);
}
- button.setContentDescription(R.id.action_container, action.title);
+ if (Build.VERSION.SDK_INT >= 15) {
+ button.setContentDescription(R.id.action_container, action.title);
+ }
return button;
}
@@ -265,9 +283,7 @@
boolean showLine2 = false;
boolean minPriority = priority < NotificationCompat.PRIORITY_LOW;
- boolean afterJellyBean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
- boolean afterLollipop = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
- if (afterJellyBean && !afterLollipop) {
+ if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 21) {
// lets color the backgrounds
if (minPriority) {
contentView.setInt(R.id.notification_background,
@@ -285,7 +301,7 @@
if (largeIcon != null) {
// On versions before Jellybean, the large icon was shown by SystemUI, so we need to hide
// it here.
- if (afterJellyBean) {
+ if (Build.VERSION.SDK_INT >= 16) {
contentView.setViewVisibility(R.id.icon, View.VISIBLE);
contentView.setImageViewBitmap(R.id.icon, largeIcon);
} else {
@@ -296,7 +312,7 @@
R.dimen.notification_right_icon_size);
int iconSize = backgroundSize - res.getDimensionPixelSize(
R.dimen.notification_small_icon_background_padding) * 2;
- if (afterLollipop) {
+ if (Build.VERSION.SDK_INT >= 21) {
Bitmap smallBit = createIconWithBackground(context,
smallIcon,
backgroundSize,
@@ -311,7 +327,7 @@
}
} else if (smallIcon != 0) { // small icon at left
contentView.setViewVisibility(R.id.icon, View.VISIBLE);
- if (afterLollipop) {
+ if (Build.VERSION.SDK_INT >= 21) {
int backgroundSize = res.getDimensionPixelSize(
R.dimen.notification_large_icon_width)
- res.getDimensionPixelSize(R.dimen.notification_big_circle_margin);
@@ -336,7 +352,7 @@
showLine3 = true;
}
// If there is a large icon we have a right side
- boolean hasRightSide = !afterLollipop && largeIcon != null;
+ boolean hasRightSide = !(Build.VERSION.SDK_INT >= 21) && largeIcon != null;
if (contentInfo != null) {
contentView.setTextViewText(R.id.info, contentInfo);
contentView.setViewVisibility(R.id.info, View.VISIBLE);
@@ -360,7 +376,7 @@
}
// Need to show three lines? Only allow on Jellybean+
- if (subText != null && afterJellyBean) {
+ if (subText != null && Build.VERSION.SDK_INT >= 16) {
contentView.setTextViewText(R.id.text, subText);
if (contentText != null) {
contentView.setTextViewText(R.id.text2, contentText);
@@ -372,7 +388,7 @@
}
// RemoteViews.setViewPadding and RemoteViews.setTextViewTextSize is not available on ICS-
- if (showLine2 && afterJellyBean) {
+ if (showLine2 && Build.VERSION.SDK_INT >= 16) {
if (fitIn1U) {
// need to shrink all the type to make sure everything fits
final float subTextSize = res.getDimensionPixelSize(
@@ -384,7 +400,7 @@
}
if (when != 0) {
- if (useChronometer && afterJellyBean) {
+ if (useChronometer && Build.VERSION.SDK_INT >= 16) {
contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
contentView.setLong(R.id.chronometer, "setBase",
when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompatImplJellybean.java b/v7/appcompat/src/android/support/v7/app/NotificationCompatImplJellybean.java
index cf8d128..b600d43 100644
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompatImplJellybean.java
+++ b/v7/appcompat/src/android/support/v7/app/NotificationCompatImplJellybean.java
@@ -16,9 +16,13 @@
package android.support.v7.app;
+import android.annotation.TargetApi;
import android.app.Notification;
+import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
+@RequiresApi(16)
+@TargetApi(16)
class NotificationCompatImplJellybean {
public static void addBigTextStyle(NotificationBuilderWithBuilderAccessor b,
diff --git a/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java b/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java
index 0225400..b05de9a 100644
--- a/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java
+++ b/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java
@@ -16,6 +16,8 @@
package android.support.v7.view;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.view.ActionMode;
import android.view.KeyEvent;
@@ -57,6 +59,8 @@
return mWrapped.dispatchKeyEvent(event);
}
+ @RequiresApi(11)
+ @TargetApi(11)
@Override
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
return mWrapped.dispatchKeyShortcutEvent(event);
@@ -72,6 +76,8 @@
return mWrapped.dispatchTrackballEvent(event);
}
+ @RequiresApi(12)
+ @TargetApi(12)
@Override
public boolean dispatchGenericMotionEvent(MotionEvent event) {
return mWrapped.dispatchGenericMotionEvent(event);
@@ -137,6 +143,8 @@
mWrapped.onPanelClosed(featureId, menu);
}
+ @RequiresApi(23)
+ @TargetApi(23)
@Override
public boolean onSearchRequested(SearchEvent searchEvent) {
return mWrapped.onSearchRequested(searchEvent);
@@ -147,26 +155,36 @@
return mWrapped.onSearchRequested();
}
+ @RequiresApi(11)
+ @TargetApi(11)
@Override
public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
return mWrapped.onWindowStartingActionMode(callback);
}
+ @RequiresApi(23)
+ @TargetApi(23)
@Override
public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) {
return mWrapped.onWindowStartingActionMode(callback, type);
}
+ @RequiresApi(11)
+ @TargetApi(11)
@Override
public void onActionModeStarted(ActionMode mode) {
mWrapped.onActionModeStarted(mode);
}
+ @RequiresApi(11)
+ @TargetApi(11)
@Override
public void onActionModeFinished(ActionMode mode) {
mWrapped.onActionModeFinished(mode);
}
+ @RequiresApi(24)
+ @TargetApi(24)
@Override
public void onProvideKeyboardShortcuts(
List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java b/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java
index 1a9e4e2..649b253 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.v4.internal.view.SupportMenuItem;
import android.support.v4.view.ActionProvider;
@@ -42,7 +43,8 @@
* @hide
*/
@RestrictTo(GROUP_ID)
-@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+@TargetApi(14)
+@RequiresApi(14)
public class MenuItemWrapperICS extends BaseMenuWrapper<SupportMenuItem> implements MenuItem {
static final String LOG_TAG = "MenuItemWrapper";
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperJB.java b/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperJB.java
index bdcfc71..fd6c141 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperJB.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperJB.java
@@ -19,6 +19,7 @@
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.v4.internal.view.SupportMenuItem;
import android.support.v4.view.ActionProvider;
@@ -32,7 +33,8 @@
* @hide
*/
@RestrictTo(GROUP_ID)
-@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+@TargetApi(16)
+@RequiresApi(16)
class MenuItemWrapperJB extends MenuItemWrapperICS {
MenuItemWrapperJB(Context context, SupportMenuItem object) {
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperICS.java b/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperICS.java
index 9bf58fa..25208af 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperICS.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperICS.java
@@ -16,9 +16,11 @@
package android.support.v7.view.menu;
+import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.support.annotation.RequiresApi;
import android.support.v4.internal.view.SupportMenu;
import android.view.KeyEvent;
import android.view.Menu;
@@ -28,6 +30,8 @@
/**
* Wraps a support {@link SupportMenu} as a framework {@link android.view.Menu}
*/
+@TargetApi(14)
+@RequiresApi(14)
class MenuWrapperICS extends BaseMenuWrapper<SupportMenu> implements Menu {
MenuWrapperICS(Context context, SupportMenu object) {
diff --git a/v7/appcompat/src/android/support/v7/view/menu/SubMenuWrapperICS.java b/v7/appcompat/src/android/support/v7/view/menu/SubMenuWrapperICS.java
index 185d443..4952d5d 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/SubMenuWrapperICS.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/SubMenuWrapperICS.java
@@ -16,8 +16,10 @@
package android.support.v7.view.menu;
+import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.v4.internal.view.SupportSubMenu;
import android.view.MenuItem;
@@ -31,6 +33,8 @@
* @hide
*/
@RestrictTo(GROUP_ID)
+@RequiresApi(14)
+@TargetApi(14)
class SubMenuWrapperICS extends MenuWrapperICS implements SubMenu {
SubMenuWrapperICS(Context context, SupportSubMenu subMenu) {
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawable.java b/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawable.java
index b2fa191..d8b0f2d 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawable.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawable.java
@@ -16,10 +16,14 @@
package android.support.v7.widget;
+import android.annotation.TargetApi;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+@RequiresApi(9)
+@TargetApi(9)
class ActionBarBackgroundDrawable extends Drawable {
final ActionBarContainer mContainer;
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawableV21.java b/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawableV21.java
index 0b05fe7..56f2a09 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawableV21.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawableV21.java
@@ -16,9 +16,13 @@
package android.support.v7.widget;
+import android.annotation.TargetApi;
import android.graphics.Outline;
import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+@RequiresApi(21)
+@TargetApi(21)
class ActionBarBackgroundDrawableV21 extends ActionBarBackgroundDrawable {
public ActionBarBackgroundDrawableV21(ActionBarContainer container) {
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java b/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
index af5ff2d..613be53 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
@@ -16,12 +16,14 @@
package android.support.v7.widget;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.support.annotation.DrawableRes;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.v4.view.TintableBackgroundView;
import android.support.v7.appcompat.R;
@@ -168,6 +170,8 @@
event.setClassName(Button.class.getName());
}
+ @RequiresApi(14)
+ @TargetApi(14)
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java b/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
index b51df4b..1e943e9 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
@@ -23,6 +23,7 @@
import static android.support.v7.widget.ThemeUtils.getThemeAttrColor;
import static android.support.v7.widget.ThemeUtils.getThemeAttrColorStateList;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -37,6 +38,7 @@
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.graphics.drawable.AnimatedVectorDrawableCompat;
import android.support.graphics.drawable.VectorDrawableCompat;
@@ -87,13 +89,12 @@
}
private static void installDefaultInflateDelegates(@NonNull AppCompatDrawableManager manager) {
- final int sdk = Build.VERSION.SDK_INT;
// This sdk version check will affect src:appCompat code path.
// Although VectorDrawable exists in Android framework from Lollipop, AppCompat will use the
// VectorDrawableCompat before Nougat to utilize the bug fixes in VectorDrawableCompat.
- if (sdk < 24) {
+ if (Build.VERSION.SDK_INT < 24) {
manager.addDelegate("vector", new VdcInflateDelegate());
- if (sdk >= 11) {
+ if (Build.VERSION.SDK_INT >= 11) {
// AnimatedVectorDrawableCompat only works on API v11+
manager.addDelegate("animated-vector", new AvdcInflateDelegate());
}
@@ -750,6 +751,8 @@
}
}
+ @RequiresApi(11)
+ @TargetApi(11)
private static class AvdcInflateDelegate implements InflateDelegate {
AvdcInflateDelegate() {
}
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java
index 7053c39..bac1cb8 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java
@@ -16,8 +16,10 @@
package android.support.v7.widget;
+import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
+import android.support.annotation.RequiresApi;
import android.support.v7.appcompat.R;
import android.util.AttributeSet;
import android.widget.SeekBar;
@@ -59,6 +61,8 @@
mAppCompatSeekBarHelper.drawableStateChanged();
}
+ @RequiresApi(11)
+ @TargetApi(11)
@Override
public void jumpDrawablesToCurrentState() {
super.jumpDrawablesToCurrentState();
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.java
index 1d84401..93e1851 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.java
@@ -16,11 +16,13 @@
package android.support.v7.widget;
+import android.annotation.TargetApi;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.view.ViewCompat;
import android.support.v7.appcompat.R;
@@ -140,6 +142,8 @@
}
}
+ @RequiresApi(11)
+ @TargetApi(11)
void jumpDrawablesToCurrentState() {
if (mTickMark != null) {
mTickMark.jumpToCurrentState();
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java b/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java
index cb026ed..beb4b73 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java
@@ -67,9 +67,6 @@
*/
public class AppCompatSpinner extends Spinner implements TintableBackgroundView {
- static final boolean IS_AT_LEAST_M = Build.VERSION.SDK_INT >= 23;
- private static final boolean IS_AT_LEAST_JB = Build.VERSION.SDK_INT >= 16;
-
private static final int[] ATTRS_ANDROID_SPINNERMODE = {android.R.attr.spinnerMode};
private static final int MAX_ITEMS_MEASURED = 15;
@@ -211,7 +208,7 @@
} else {
// If we're running on a < M device, we'll use the current context and still handle
// any dropdown popup
- mPopupContext = !IS_AT_LEAST_M ? context : null;
+ mPopupContext = !(Build.VERSION.SDK_INT >= 23) ? context : null;
}
}
@@ -296,7 +293,7 @@
public Context getPopupContext() {
if (mPopup != null) {
return mPopupContext;
- } else if (IS_AT_LEAST_M) {
+ } else if (Build.VERSION.SDK_INT >= 23) {
return super.getPopupContext();
}
return null;
@@ -305,7 +302,7 @@
public void setPopupBackgroundDrawable(Drawable background) {
if (mPopup != null) {
mPopup.setBackgroundDrawable(background);
- } else if (IS_AT_LEAST_JB) {
+ } else if (Build.VERSION.SDK_INT >= 16) {
super.setPopupBackgroundDrawable(background);
}
}
@@ -317,7 +314,7 @@
public Drawable getPopupBackground() {
if (mPopup != null) {
return mPopup.getBackground();
- } else if (IS_AT_LEAST_JB) {
+ } else if (Build.VERSION.SDK_INT >= 16) {
return super.getPopupBackground();
}
return null;
@@ -326,7 +323,7 @@
public void setDropDownVerticalOffset(int pixels) {
if (mPopup != null) {
mPopup.setVerticalOffset(pixels);
- } else if (IS_AT_LEAST_JB) {
+ } else if (Build.VERSION.SDK_INT >= 16) {
super.setDropDownVerticalOffset(pixels);
}
}
@@ -334,7 +331,7 @@
public int getDropDownVerticalOffset() {
if (mPopup != null) {
return mPopup.getVerticalOffset();
- } else if (IS_AT_LEAST_JB) {
+ } else if (Build.VERSION.SDK_INT >= 16) {
return super.getDropDownVerticalOffset();
}
return 0;
@@ -343,7 +340,7 @@
public void setDropDownHorizontalOffset(int pixels) {
if (mPopup != null) {
mPopup.setHorizontalOffset(pixels);
- } else if (IS_AT_LEAST_JB) {
+ } else if (Build.VERSION.SDK_INT >= 16) {
super.setDropDownHorizontalOffset(pixels);
}
}
@@ -357,7 +354,7 @@
public int getDropDownHorizontalOffset() {
if (mPopup != null) {
return mPopup.getHorizontalOffset();
- } else if (IS_AT_LEAST_JB) {
+ } else if (Build.VERSION.SDK_INT >= 16) {
return super.getDropDownHorizontalOffset();
}
return 0;
@@ -366,7 +363,7 @@
public void setDropDownWidth(int pixels) {
if (mPopup != null) {
mDropDownWidth = pixels;
- } else if (IS_AT_LEAST_JB) {
+ } else if (Build.VERSION.SDK_INT >= 16) {
super.setDropDownWidth(pixels);
}
}
@@ -374,7 +371,7 @@
public int getDropDownWidth() {
if (mPopup != null) {
return mDropDownWidth;
- } else if (IS_AT_LEAST_JB) {
+ } else if (Build.VERSION.SDK_INT >= 16) {
return super.getDropDownWidth();
}
return 0;
@@ -607,7 +604,8 @@
}
if (dropDownTheme != null) {
- if (IS_AT_LEAST_M && adapter instanceof android.widget.ThemedSpinnerAdapter) {
+ if (Build.VERSION.SDK_INT >= 23
+ && adapter instanceof android.widget.ThemedSpinnerAdapter) {
final android.widget.ThemedSpinnerAdapter themedAdapter =
(android.widget.ThemedSpinnerAdapter) adapter;
if (themedAdapter.getDropDownViewTheme() != dropDownTheme) {
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
index 01c9849..c210036 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
@@ -16,16 +16,20 @@
package android.support.v7.widget;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.support.annotation.RequiresApi;
import android.support.v7.appcompat.R;
import android.support.v7.text.AllCapsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import android.util.AttributeSet;
import android.widget.TextView;
+@RequiresApi(9)
+@TargetApi(9)
class AppCompatTextHelper {
static AppCompatTextHelper create(TextView textView) {
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelperV17.java b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelperV17.java
index c8597fb..e022756 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelperV17.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelperV17.java
@@ -16,13 +16,17 @@
package android.support.v7.widget;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
import android.support.v7.appcompat.R;
import android.util.AttributeSet;
import android.widget.TextView;
+@RequiresApi(17)
+@TargetApi(17)
class AppCompatTextHelperV17 extends AppCompatTextHelper {
private TintInfo mDrawableStartTint;
private TintInfo mDrawableEndTint;
diff --git a/v7/appcompat/src/android/support/v7/widget/ForwardingListener.java b/v7/appcompat/src/android/support/v7/widget/ForwardingListener.java
index 9de02b2..91c0fd7 100644
--- a/v7/appcompat/src/android/support/v7/widget/ForwardingListener.java
+++ b/v7/appcompat/src/android/support/v7/widget/ForwardingListener.java
@@ -16,8 +16,12 @@
package android.support.v7.widget;
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
+import android.annotation.TargetApi;
import android.os.Build;
import android.os.SystemClock;
+import android.support.annotation.RequiresApi;
import android.support.annotation.RestrictTo;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
@@ -29,9 +33,6 @@
import android.view.ViewParent;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
-
/**
* Abstract class that forwards touch events to a {@link ShowableListMenu}.
*
@@ -86,6 +87,8 @@
mLongPressTimeout = (mTapTimeout + ViewConfiguration.getLongPressTimeout()) / 2;
}
+ @RequiresApi(12)
+ @TargetApi(12)
private void addDetachListenerApi12(View src) {
src.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
@Override
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java b/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java
index b021ade..905521d 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java
@@ -73,8 +73,9 @@
assertEquals("Left side should be white", Color.red(leftColor), 255);
assertEquals("Right side should be black", Color.red(rightColor), 0);
- if (Build.VERSION.SDK_INT >= 17) {
- // setLayoutDirection is only available after API 17.
+ if (Build.VERSION.SDK_INT >= 19) {
+ // setLayoutDirection is only available after API 17. However, it correctly set its
+ // drawable's layout direction until API 19.
view1.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
vectorDrawable.draw(mCanvas);
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithToolbar.java b/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithToolbar.java
index c7246d8..9533f38 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithToolbar.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithToolbar.java
@@ -21,13 +21,13 @@
import org.junit.Test;
+@SmallTest
public class BasicsTestCaseWithToolbar extends BaseBasicsTestCase<ToolbarAppCompatActivity> {
public BasicsTestCaseWithToolbar() {
super(ToolbarAppCompatActivity.class);
}
@Test
- @SmallTest
@UiThreadTest
public void testSupportActionModeAppCompatCallbacks() {
// Since we're using Toolbar, any action modes will be created from the window
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithWindowDecor.java b/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithWindowDecor.java
index 35cc04c..aee6a50 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithWindowDecor.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithWindowDecor.java
@@ -21,13 +21,13 @@
import org.junit.Test;
+@SmallTest
public class BasicsTestCaseWithWindowDecor extends BaseBasicsTestCase<WindowDecorAppCompatActivity> {
public BasicsTestCaseWithWindowDecor() {
super(WindowDecorAppCompatActivity.class);
}
@Test
- @SmallTest
@UiThreadTest
public void testSupportActionModeAppCompatCallbacks() {
// Since we're using the decor action bar, any action modes not will be created
diff --git a/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
index a9c8dc2..8dd1074 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
@@ -27,10 +27,10 @@
import android.app.Instrumentation;
import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
import android.support.test.filters.SdkSuppress;
import android.support.test.filters.Suppress;
import android.support.v7.appcompat.test.R;
+import android.test.suitebuilder.annotation.MediumTest;
import org.junit.Before;
import org.junit.Test;
@@ -123,6 +123,40 @@
onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)));
}
+ @Test
+ public void testNightModeAutoRecreatesOnResume() throws Throwable {
+ // Create a fake TwilightManager and set it as the app instance
+ final FakeTwilightManager twilightManager = new FakeTwilightManager();
+ TwilightManager.setInstance(twilightManager);
+
+ final NightModeActivity activity = getActivity();
+
+ // Set MODE_NIGHT_AUTO so that we will change to night mode automatically
+ setLocalNightModeAndWaitForRecreate(activity, AppCompatDelegate.MODE_NIGHT_AUTO);
+ // Verify that we're currently in day mode
+ onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY)));
+
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ final Instrumentation instrumentation = getInstrumentation();
+ // Now fool the Activity into thinking that it has gone into the background
+ instrumentation.callActivityOnPause(activity);
+ instrumentation.callActivityOnStop(activity);
+
+ // Now update the twilight manager while the Activity is in the 'background'
+ twilightManager.setIsNight(true);
+
+ // Now tell the Activity that it has gone into the foreground again
+ instrumentation.callActivityOnStart(activity);
+ instrumentation.callActivityOnResume(activity);
+ }
+ });
+
+ // finally check that the text has changed, signifying that night resources are being used
+ onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)));
+ }
+
private static class FakeTwilightManager extends TwilightManager {
private boolean mIsNight;
diff --git a/v7/cardview/Android.mk b/v7/cardview/Android.mk
index 7861dc1..cd3b407 100644
--- a/v7/cardview/Android.mk
+++ b/v7/cardview/Android.mk
@@ -14,63 +14,6 @@
LOCAL_PATH := $(call my-dir)
-# Build the resources using the latest applicable SDK version.
-# We do this here because the final static library must be compiled with an older
-# SDK version than the resources. The resources library and the R class that it
-# contains will not be linked into the final static library.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-v7-cardview-res
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library to resolve cyclic dependencies between CardView and platform dependent
-# implementations
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-cardview-base
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, base)
-LOCAL_JAVA_LIBRARIES := android-support-annotations
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Gingerbread APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-cardview-gingerbread
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, gingerbread)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-cardview-base
-LOCAL_JAVA_LIBRARIES := android-support-v7-cardview-res \
- android-support-annotations
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of JB MR1 APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-cardview-jellybean-mr1
-LOCAL_SDK_VERSION := 17
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr1)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-cardview-gingerbread
-LOCAL_JAVA_LIBRARIES := android-support-v7-cardview-res \
- android-support-annotations
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of L APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-cardview-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-cardview-jellybean-mr1
-LOCAL_JAVA_LIBRARIES := android-support-v7-cardview-res \
- android-support-annotations
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
# Here is the final static library that apps can link against.
# Applications that use this library must specify
#
@@ -80,12 +23,16 @@
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-v7-cardview
-LOCAL_SDK_VERSION := 9
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-cardview-api21
-LOCAL_JAVA_LIBRARIES := android-support-annotations
-LOCAL_STATIC_ANDROID_LIBRARIES := android-support-v7-cardview-res
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under,base) \
+ $(call all-java-files-under,gingerbread) \
+ $(call all-java-files-under,jellybean-mr1) \
+ $(call all-java-files-under,api21) \
+ $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-annotations
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/v7/cardview/api21/android/support/v7/widget/CardViewApi21.java b/v7/cardview/api21/android/support/v7/widget/CardViewApi21.java
index 413a287..b4f2460 100644
--- a/v7/cardview/api21/android/support/v7/widget/CardViewApi21.java
+++ b/v7/cardview/api21/android/support/v7/widget/CardViewApi21.java
@@ -15,11 +15,15 @@
*/
package android.support.v7.widget;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.view.View;
+@RequiresApi(21)
+@TargetApi(21)
class CardViewApi21 implements CardViewImpl {
@Override
diff --git a/v7/cardview/api21/android/support/v7/widget/RoundRectDrawable.java b/v7/cardview/api21/android/support/v7/widget/RoundRectDrawable.java
index 3a85d9c..7e85b7f 100644
--- a/v7/cardview/api21/android/support/v7/widget/RoundRectDrawable.java
+++ b/v7/cardview/api21/android/support/v7/widget/RoundRectDrawable.java
@@ -15,6 +15,7 @@
*/
package android.support.v7.widget;
+import android.annotation.TargetApi;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -28,6 +29,7 @@
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import static android.support.v7.widget.RoundRectDrawableWithShadow.calculateVerticalPadding;
import static android.support.v7.widget.RoundRectDrawableWithShadow.calculateHorizontalPadding;
@@ -38,6 +40,8 @@
* <p>
* Simpler and uses less resources compared to GradientDrawable or ShapeDrawable.
*/
+@RequiresApi(21)
+@TargetApi(21)
class RoundRectDrawable extends Drawable {
private float mRadius;
private final Paint mPaint;
diff --git a/v7/cardview/base/android/support/v7/widget/CardViewDelegate.java b/v7/cardview/base/android/support/v7/widget/CardViewDelegate.java
index b5be921..2573631 100644
--- a/v7/cardview/base/android/support/v7/widget/CardViewDelegate.java
+++ b/v7/cardview/base/android/support/v7/widget/CardViewDelegate.java
@@ -15,7 +15,9 @@
*/
package android.support.v7.widget;
+import android.annotation.TargetApi;
import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
import android.view.View;
/**
@@ -23,6 +25,8 @@
* <p>
* Necessary to resolve circular dependency between base CardView and platform implementations.
*/
+@RequiresApi(9)
+@TargetApi(9)
interface CardViewDelegate {
void setCardBackground(Drawable drawable);
Drawable getCardBackground();
diff --git a/v7/cardview/base/android/support/v7/widget/CardViewImpl.java b/v7/cardview/base/android/support/v7/widget/CardViewImpl.java
index 26799da..f36bd2b 100644
--- a/v7/cardview/base/android/support/v7/widget/CardViewImpl.java
+++ b/v7/cardview/base/android/support/v7/widget/CardViewImpl.java
@@ -15,13 +15,17 @@
*/
package android.support.v7.widget;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
/**
* Interface for platform specific CardView implementations.
*/
+@RequiresApi(9)
+@TargetApi(9)
interface CardViewImpl {
void initialize(CardViewDelegate cardView, Context context, ColorStateList backgroundColor,
float radius, float elevation, float maxElevation);
diff --git a/v7/cardview/build.gradle b/v7/cardview/build.gradle
index e9e0ab5..12f9e39 100644
--- a/v7/cardview/build.gradle
+++ b/v7/cardview/build.gradle
@@ -1,5 +1,4 @@
apply plugin: 'com.android.library'
-
archivesBaseName = 'cardview-v7'
dependencies {
@@ -7,25 +6,22 @@
}
android {
- // WARNING: should be 7
compileSdkVersion project.ext.currentSdk
defaultConfig {
minSdkVersion 9
- // TODO: get target from branch
- //targetSdkVersion 19
- }
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_7
- targetCompatibility JavaVersion.VERSION_1_7
}
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDirs = ['base', 'gingerbread', 'jellybean-mr1', 'api21', 'src']
- main.aidl.srcDirs = ['base', 'gingerbread', 'jellybean-mr1', 'api21', 'src']
- main.res.srcDirs = ['res']
+ main.java.srcDirs = [
+ 'base',
+ 'gingerbread',
+ 'jellybean-mr1',
+ 'api21',
+ 'src'
+ ]
+ main.res.srcDir 'res'
androidTest.setRoot('tests')
androidTest.java.srcDir 'tests/java'
diff --git a/v7/cardview/dummy/Dummy.java b/v7/cardview/dummy/Dummy.java
deleted file mode 100644
index be16dc7..0000000
--- a/v7/cardview/dummy/Dummy.java
+++ /dev/null
@@ -1 +0,0 @@
-// Dummy java file used to build the resource library.
diff --git a/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java b/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java
index 857cffa..f430213 100644
--- a/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java
+++ b/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java
@@ -15,6 +15,7 @@
*/
package android.support.v7.widget;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
@@ -23,7 +24,10 @@
import android.graphics.RectF;
import android.support.annotation.ColorInt;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+@RequiresApi(9)
+@TargetApi(9)
class CardViewGingerbread implements CardViewImpl {
final RectF sCornerRect = new RectF();
diff --git a/v7/cardview/gingerbread/android/support/v7/widget/RoundRectDrawableWithShadow.java b/v7/cardview/gingerbread/android/support/v7/widget/RoundRectDrawableWithShadow.java
index 5cefd8f..32bf877 100644
--- a/v7/cardview/gingerbread/android/support/v7/widget/RoundRectDrawableWithShadow.java
+++ b/v7/cardview/gingerbread/android/support/v7/widget/RoundRectDrawableWithShadow.java
@@ -15,6 +15,7 @@
*/
package android.support.v7.widget;
+import android.annotation.TargetApi;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Canvas;
@@ -30,11 +31,14 @@
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.support.v7.cardview.R;
/**
* A rounded rectangle drawable which also includes a shadow around.
*/
+@RequiresApi(9)
+@TargetApi(9)
class RoundRectDrawableWithShadow extends Drawable {
// used to calculate content padding
final static double COS_45 = Math.cos(Math.toRadians(45));
diff --git a/v7/cardview/jellybean-mr1/android/support/v7/widget/CardViewJellybeanMr1.java b/v7/cardview/jellybean-mr1/android/support/v7/widget/CardViewJellybeanMr1.java
index 4c32227..a9c0e0a 100644
--- a/v7/cardview/jellybean-mr1/android/support/v7/widget/CardViewJellybeanMr1.java
+++ b/v7/cardview/jellybean-mr1/android/support/v7/widget/CardViewJellybeanMr1.java
@@ -15,10 +15,14 @@
*/
package android.support.v7.widget;
+import android.annotation.TargetApi;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
+import android.support.annotation.RequiresApi;
+@RequiresApi(17)
+@TargetApi(17)
class CardViewJellybeanMr1 extends CardViewGingerbread {
@Override
diff --git a/v7/gridlayout/Android.mk b/v7/gridlayout/Android.mk
index 7938918..6eac23b4 100644
--- a/v7/gridlayout/Android.mk
+++ b/v7/gridlayout/Android.mk
@@ -26,10 +26,13 @@
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-v7-gridlayout
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SDK_VERSION := 9
-LOCAL_SHARED_ANDROID_LIBRARIES += android-support-compat android-support-core-ui
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-compat \
+ android-support-core-ui \
+ android-support-annotations
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/v7/mediarouter/Android.mk b/v7/mediarouter/Android.mk
index 83dedec..21b4a62 100644
--- a/v7/mediarouter/Android.mk
+++ b/v7/mediarouter/Android.mk
@@ -14,55 +14,6 @@
LOCAL_PATH := $(call my-dir)
-# Build the resources using the latest applicable SDK version.
-# We do this here because the final static library must be compiled with an older
-# SDK version than the resources.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-v7-mediarouter-res
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := android-support-v7-appcompat
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of JellyBean APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-mediarouter-jellybean
-LOCAL_SDK_VERSION := 16
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of JellyBean MR1 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-mediarouter-jellybean-mr1
-LOCAL_SDK_VERSION := 17
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr1)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-mediarouter-jellybean
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of JellyBean MR2 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-mediarouter-jellybean-mr2
-LOCAL_SDK_VERSION := 18
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr2)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-mediarouter-jellybean-mr1
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of V24 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-mediarouter-api24
-LOCAL_SDK_VERSION := 24
-LOCAL_SRC_FILES := $(call all-java-files-under, api24)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-mediarouter-jellybean-mr2
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
# Here is the final static library that apps can link against.
# Applications that use this library must specify
#
@@ -76,11 +27,14 @@
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-v7-mediarouter
-LOCAL_SDK_VERSION := current
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-mediarouter-api24
-LOCAL_STATIC_ANDROID_LIBRARIES := android-support-v7-mediarouter-res
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under,jellybean) \
+ $(call all-java-files-under,jellybean-mr1) \
+ $(call all-java-files-under,jellybean-mr2) \
+ $(call all-java-files-under,api24) \
+ $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-v7-appcompat \
android-support-v7-palette \
diff --git a/v7/mediarouter/api24/android/support/v7/media/MediaRouterApi24.java b/v7/mediarouter/api24/android/support/v7/media/MediaRouterApi24.java
index 3734b59..48bef17 100644
--- a/v7/mediarouter/api24/android/support/v7/media/MediaRouterApi24.java
+++ b/v7/mediarouter/api24/android/support/v7/media/MediaRouterApi24.java
@@ -16,6 +16,11 @@
package android.support.v7.media;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(24)
+@TargetApi(24)
final class MediaRouterApi24 {
public static final class RouteInfo {
public static int getDeviceType(Object routeObj) {
diff --git a/v7/mediarouter/build.gradle b/v7/mediarouter/build.gradle
index 16761c2..61a5985 100644
--- a/v7/mediarouter/build.gradle
+++ b/v7/mediarouter/build.gradle
@@ -1,36 +1,28 @@
apply plugin: 'com.android.library'
-
archivesBaseName = 'mediarouter-v7'
-
-// some of the source requires compiling against a newer API.
-// Right now, use normal Java source sets to compile those into a jar and
-// package it as a local dependencies inside the library aar.
-
-createApiSourceSets(project, gradle.ext.studioCompat.modules.mediaRouter.apiTargets)
-sourceCompatibility = JavaVersion.VERSION_1_7
-targetCompatibility = JavaVersion.VERSION_1_7
-setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.mediaRouter.dependencies)
-// keep these dependencies here since API specific implementations don't need to access them.
dependencies {
compile project(":support-appcompat-v7")
compile project(":support-palette-v7")
}
+
android {
compileSdkVersion project.ext.currentSdk
+ defaultConfig {
+ minSdkVersion 9
+ }
+
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
- main.java.srcDir 'src'
+ main.java.srcDirs = [
+ 'jellybean',
+ 'jellybean-mr1',
+ 'jellybean-mr2',
+ 'api24',
+ 'src'
+ ]
main.res.srcDir 'res'
- main.assets.srcDir 'assets'
- main.resources.srcDir 'src'
-
- // this moves src/instrumentTest to tests so all folders follow:
- // tests/java, tests/res, tests/assets, ...
- // This is a *reset* so it replaces the default paths
- androidTest.setRoot('tests')
- androidTest.java.srcDir 'tests/src'
}
compileOptions {
@@ -73,11 +65,6 @@
from android.sourceSets.main.java.srcDirs
}
- project.ext.allSS.each { ss ->
- javadocTask.source ss.java
- sourcesJarTask.from ss.java.srcDirs
- }
-
artifacts.add('archives', javadocJarTask);
artifacts.add('archives', sourcesJarTask);
}
diff --git a/v7/mediarouter/dummy/Dummy.java b/v7/mediarouter/dummy/Dummy.java
deleted file mode 100644
index be16dc7..0000000
--- a/v7/mediarouter/dummy/Dummy.java
+++ /dev/null
@@ -1 +0,0 @@
-// Dummy java file used to build the resource library.
diff --git a/v7/mediarouter/jellybean-mr1/android/support/v7/media/MediaRouterJellybeanMr1.java b/v7/mediarouter/jellybean-mr1/android/support/v7/media/MediaRouterJellybeanMr1.java
index 0ef744f..3a42a2f 100644
--- a/v7/mediarouter/jellybean-mr1/android/support/v7/media/MediaRouterJellybeanMr1.java
+++ b/v7/mediarouter/jellybean-mr1/android/support/v7/media/MediaRouterJellybeanMr1.java
@@ -16,10 +16,12 @@
package android.support.v7.media;
+import android.annotation.TargetApi;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Handler;
+import android.support.annotation.RequiresApi;
import android.util.Log;
import android.view.Display;
@@ -27,6 +29,8 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+@RequiresApi(17)
+@TargetApi(17)
final class MediaRouterJellybeanMr1 {
private static final String TAG = "MediaRouterJellybeanMr1";
diff --git a/v7/mediarouter/jellybean-mr2/android/support/v7/media/MediaRouterJellybeanMr2.java b/v7/mediarouter/jellybean-mr2/android/support/v7/media/MediaRouterJellybeanMr2.java
index f3c2966..6799faa 100644
--- a/v7/mediarouter/jellybean-mr2/android/support/v7/media/MediaRouterJellybeanMr2.java
+++ b/v7/mediarouter/jellybean-mr2/android/support/v7/media/MediaRouterJellybeanMr2.java
@@ -16,6 +16,11 @@
package android.support.v7.media;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(18)
+@TargetApi(18)
final class MediaRouterJellybeanMr2 {
public static Object getDefaultRoute(Object routerObj) {
return ((android.media.MediaRouter)routerObj).getDefaultRoute();
diff --git a/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java b/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java
index c030332..85071a4 100644
--- a/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java
+++ b/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java
@@ -16,16 +16,21 @@
package android.support.v7.media;
+import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.support.annotation.RequiresApi;
import android.util.Log;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
+@RequiresApi(16)
+@TargetApi(16)
final class MediaRouterJellybean {
private static final String TAG = "MediaRouterJellybean";
@@ -111,6 +116,21 @@
return new VolumeCallbackProxy<VolumeCallback>(callback);
}
+ static boolean isBluetoothA2dpOn(Object routerObj) {
+ try {
+ Field globalRouterField = routerObj.getClass().getDeclaredField("sStatic");
+ globalRouterField.setAccessible(true);
+ Object globalRouterObj = globalRouterField.get(null);
+ Method method = globalRouterObj.getClass().getDeclaredMethod("isBluetoothA2dpOn", null);
+ method.setAccessible(true);
+ Object result = method.invoke(globalRouterObj, null);
+ return (Boolean) result;
+ } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException
+ | NoSuchMethodException | InvocationTargetException e) {
+ return false;
+ }
+ }
+
public static final class RouteInfo {
public static CharSequence getName(Object routeObj, Context context) {
return ((android.media.MediaRouter.RouteInfo)routeObj).getName(context);
diff --git a/v7/mediarouter/res/values/attrs.xml b/v7/mediarouter/res/values/attrs.xml
index 5ebc0ee..cf6a7b5 100644
--- a/v7/mediarouter/res/values/attrs.xml
+++ b/v7/mediarouter/res/values/attrs.xml
@@ -21,6 +21,8 @@
and non-checked / non-checkable indicates
that media is playing to the local device only. -->
<attr name="externalRouteEnabledDrawable" format="reference" />
+ <!-- Tint to apply to the media route button -->
+ <attr name="buttonTint" format="color" />
<attr name="android:minWidth" />
<attr name="android:minHeight" />
@@ -36,4 +38,6 @@
<attr name="mediaRouteSpeakerIconDrawable" format="reference" />
<attr name="mediaRouteSpeakerGroupIconDrawable" format="reference" />
<attr name="mediaRouteControlPanelThemeOverlay" format="reference" />
+
+ <attr name="mediaRouteTheme" format="reference" />
</resources>
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
index 25288c0..150a3fd 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -29,10 +30,9 @@
import android.support.v4.app.FragmentManager;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.view.GravityCompat;
-import android.support.v7.media.MediaRouter;
import android.support.v7.media.MediaRouteSelector;
+import android.support.v7.media.MediaRouter;
import android.support.v7.mediarouter.R;
-import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
@@ -98,6 +98,7 @@
private boolean mCheatSheetEnabled;
private boolean mIsConnecting;
+ private ColorStateList mButtonTint;
private int mMinWidth;
private int mMinHeight;
@@ -129,6 +130,7 @@
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.MediaRouteButton, defStyleAttr, 0);
+ mButtonTint = a.getColorStateList(R.styleable.MediaRouteButton_buttonTint);
setRemoteIndicatorDrawable(a.getDrawable(
R.styleable.MediaRouteButton_externalRouteEnabledDrawable));
mMinWidth = a.getDimensionPixelSize(
@@ -362,6 +364,10 @@
mRemoteIndicator.setCallback(null);
unscheduleDrawable(mRemoteIndicator);
}
+ if (mButtonTint != null) {
+ d = DrawableCompat.wrap(d.mutate());
+ DrawableCompat.setTintList(d, mButtonTint);
+ }
mRemoteIndicator = d;
if (d != null) {
d.setCallback(this);
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java b/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
index e78c516..23f3bad 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
@@ -71,7 +71,12 @@
theme = R.style.Theme_MediaRouter;
}
}
- return new ContextThemeWrapper(context, theme);
+ int mediaRouteThemeResId = getThemeResource(context, R.attr.mediaRouteTheme);
+ Context themedContext = new ContextThemeWrapper(context, theme);
+ if (mediaRouteThemeResId != 0) {
+ themedContext = new ContextThemeWrapper(themedContext, mediaRouteThemeResId);
+ }
+ return themedContext;
}
public static int getThemeResource(Context context, int attr) {
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
index ab27c9e..daf73d0 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
@@ -16,6 +16,8 @@
package android.support.v7.media;
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -58,8 +60,6 @@
import java.util.Map;
import java.util.Set;
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
/**
* MediaRouter allows applications to control the routing of media channels
* and streams from the current device to external speakers and destination devices.
@@ -296,6 +296,16 @@
}
/**
+ * Gets a bluetooth route for playing media content on the system.
+ *
+ * @return A bluetooth route, if exist, otherwise null.
+ */
+ public RouteInfo getBluetoothRoute() {
+ checkCallingThread();
+ return sGlobal.getBluetoothRoute();
+ }
+
+ /**
* Gets the currently selected route.
* <p>
* The application should examine the route's
@@ -1054,6 +1064,30 @@
}
/**
+ * Returns true if this route is a bluetooth route.
+ *
+ * @return True if this route is a bluetooth route.
+ *
+ * @see MediaRouter#getBluetoothRoute
+ */
+ public boolean isBluetooth() {
+ checkCallingThread();
+ return sGlobal.getBluetoothRoute() == this;
+ }
+
+ /**
+ * Returns true if this route is the default route and the device speaker.
+ *
+ * @return True if this route is the default route and the device speaker.
+ */
+ public boolean isDeviceSpeaker() {
+ int defaultAudioRouteNameResourceId = Resources.getSystem().getIdentifier(
+ "default_audio_route_name", "string", "android");
+ return isDefault()
+ && Resources.getSystem().getText(defaultAudioRouteNameResourceId).equals(mName);
+ }
+
+ /**
* Gets a list of {@link MediaControlIntent media control intent} filters that
* describe the capabilities of this route and the media control actions that
* it supports.
@@ -1904,6 +1938,7 @@
private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher;
private RouteInfo mDefaultRoute;
+ private RouteInfo mBluetoothRoute;
RouteInfo mSelectedRoute;
private RouteController mSelectedRouteController;
// A map from route descriptor ID to RouteController for the member routes in the currently
@@ -2041,6 +2076,10 @@
return mDefaultRoute;
}
+ public RouteInfo getBluetoothRoute() {
+ return mBluetoothRoute;
+ }
+
public RouteInfo getSelectedRoute() {
if (mSelectedRoute == null) {
// This should never happen once the media router has been fully
@@ -2049,6 +2088,11 @@
throw new IllegalStateException("There is no currently selected route. "
+ "The media router has not yet been fully initialized.");
}
+ // A workaround for making this method work properly.
+ if (android.os.Build.VERSION.SDK_INT >= 16 && android.os.Build.VERSION.SDK_INT < 25
+ && RouteInfo.isSystemMediaRouteProvider(mSelectedRoute)) {
+ syncSystemRoutes();
+ }
return mSelectedRoute;
}
@@ -2066,6 +2110,11 @@
return;
}
+ // A workaround for making this method work properly.
+ if (android.os.Build.VERSION.SDK_INT >= 16 && android.os.Build.VERSION.SDK_INT < 25
+ && RouteInfo.isSystemMediaRouteProvider(route)) {
+ syncSystemRoutes();
+ }
setSelectedRouteInternal(route, unselectReason);
}
@@ -2200,6 +2249,35 @@
}
}
+ void syncSystemRoutes() {
+ Object routerObj = MediaRouterJellybean.getMediaRouter(mApplicationContext);
+ // If a2dp is enabled, this means a BT route is the selected route, otherwise
+ // the default route is the selected one.
+ boolean a2dpEnabled = MediaRouterJellybean.isBluetoothA2dpOn(routerObj);
+ Object selectedRouteObj = MediaRouterJellybean.getSelectedRoute(
+ routerObj, MediaRouterJellybean.ALL_ROUTE_TYPES);
+ Object defaultRouteObj = mSystemProvider.getDefaultRoute();
+
+ if (a2dpEnabled && selectedRouteObj == defaultRouteObj) {
+ // A BT route is the currently selected route, but MediaRouter think the default
+ // route is the selected one. By selecting the BT route via framework MediaRouter,
+ // MediaRouter could correct its selected route information.
+ for (Object routeObj : MediaRouterJellybean.getRoutes(routerObj)) {
+ if (routeObj != defaultRouteObj) {
+ MediaRouterJellybean.selectRoute(routerObj,
+ MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj);
+ break;
+ }
+ }
+ } else if (!a2dpEnabled && selectedRouteObj != defaultRouteObj) {
+ // The default route is the currently selected route, but MediaRouter think a BT
+ // route is the selected one. By selecting the default route via framework
+ // MediaRouter, MediaRouter could correct its selected route information.
+ MediaRouterJellybean.selectRoute(routerObj,
+ MediaRouterJellybean.ALL_ROUTE_TYPES, defaultRouteObj);
+ }
+ }
+
void updateProviderDescriptor(MediaRouteProvider providerInstance,
MediaRouteProviderDescriptor descriptor) {
int index = findProviderInfo(providerInstance);
@@ -2421,6 +2499,22 @@
}
}
+ // Update bluetooth route.
+ if (mBluetoothRoute != null && !isRouteSelectable(mBluetoothRoute)) {
+ Log.i(TAG, "Clearing the bluetooth route because it "
+ + "is no longer selectable: " + mBluetoothRoute);
+ mBluetoothRoute = null;
+ }
+ if (mBluetoothRoute == null && !mRoutes.isEmpty()) {
+ for (RouteInfo route : mRoutes) {
+ if (isSystemBluetoothRoute(route) && isRouteSelectable(route)) {
+ mBluetoothRoute = route;
+ Log.i(TAG, "Found bluetooth route: " + mBluetoothRoute);
+ break;
+ }
+ }
+ }
+
// Update selected route.
if (mSelectedRoute != null && !isRouteSelectable(mSelectedRoute)) {
Log.i(TAG, "Unselecting the current route because it "
@@ -2505,6 +2599,12 @@
SystemMediaRouteProvider.DEFAULT_ROUTE_ID);
}
+ private boolean isSystemBluetoothRoute(RouteInfo route) {
+ return route.getProviderInstance() == mSystemProvider
+ && !route.mDescriptorId.equals(
+ SystemMediaRouteProvider.DEFAULT_ROUTE_ID);
+ }
+
private void setSelectedRouteInternal(RouteInfo route, int unselectReason) {
if (mSelectedRoute != route) {
if (mSelectedRoute != null) {
@@ -2740,7 +2840,6 @@
public MediaSessionCompat.Token getToken() {
return mMsCompat.getSessionToken();
}
-
}
private final class RemoteControlClientRecord
diff --git a/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java b/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
index a31eb6f..5fcafa7 100644
--- a/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
+++ b/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
@@ -99,6 +99,10 @@
public MediaRouter.RouteInfo getSystemRouteByDescriptorId(String id);
}
+ protected Object getDefaultRoute() {
+ return null;
+ }
+
/**
* Legacy implementation for platform versions prior to Jellybean.
*/
@@ -652,6 +656,7 @@
MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj);
}
+ @Override
protected Object getDefaultRoute() {
if (mGetDefaultRouteWorkaround == null) {
mGetDefaultRouteWorkaround = new MediaRouterJellybean.GetDefaultRouteWorkaround();
diff --git a/v7/palette/Android.mk b/v7/palette/Android.mk
index c689297..c21dad3 100644
--- a/v7/palette/Android.mk
+++ b/v7/palette/Android.mk
@@ -26,11 +26,14 @@
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-v7-palette
-LOCAL_SDK_VERSION := 9
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under, src/main)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/src/main/res
LOCAL_MANIFEST_FILE := src/main/AndroidManifest.xml
-LOCAL_SHARED_ANDROID_LIBRARIES += android-support-compat android-support-core-utils
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-compat \
+ android-support-core-utils \
+ android-support-annotations
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/v7/preference/Android.mk b/v7/preference/Android.mk
index ca4fe02..e751e1c 100644
--- a/v7/preference/Android.mk
+++ b/v7/preference/Android.mk
@@ -14,23 +14,6 @@
LOCAL_PATH := $(call my-dir)
-# Build the resources separately because the constants built with the resources need to access
-# the latest SDK but the actual code needs to build against SDK 7.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-v7-preference-res
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under,constants)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-v7-appcompat \
- android-support-v7-recyclerview \
- android-support-v4 \
- android-support-annotations
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
# Here is the final static library that apps can link against.
# Applications that use this library must specify
#
@@ -38,18 +21,17 @@
# android-support-v7-preference \
# android-support-v7-appcompat \
# android-support-v7-recyclerview \
-# android-support-v4 \
-# android-support-annotations
+# android-support-v4
#
# in their makefiles to include the resources and their dependencies in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-v7-preference
-LOCAL_SDK_VERSION := 9
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_STATIC_ANDROID_LIBRARIES := \
- android-support-v7-preference-res
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under,constants) \
+ $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-v7-appcompat \
android-support-v7-recyclerview \
diff --git a/v7/preference/build.gradle b/v7/preference/build.gradle
index 61334f9..55fe020 100644
--- a/v7/preference/build.gradle
+++ b/v7/preference/build.gradle
@@ -37,6 +37,7 @@
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ minSdkVersion 9
}
sourceSets {
diff --git a/v7/recyclerview/Android.mk b/v7/recyclerview/Android.mk
index 52572fe..e434ab2 100644
--- a/v7/recyclerview/Android.mk
+++ b/v7/recyclerview/Android.mk
@@ -18,17 +18,15 @@
# Applications that use this library must specify
#
# LOCAL_STATIC_ANDROID_LIBRARIES := \
-# android-support-v7-recycler-view \
+# android-support-v7-recyclerview \
# android-support-compat \
-# android-support-core-ui \
-# android-support-annotations
+# android-support-core-ui
#
# in their makefiles to include the resources and their dependencies in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-v7-recyclerview
-LOCAL_SDK_VERSION := 9
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_SHARED_ANDROID_LIBRARIES := \
@@ -37,5 +35,5 @@
android-support-annotations
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
-
diff --git a/v7/recyclerview/src/android/support/v7/widget/GapWorker.java b/v7/recyclerview/src/android/support/v7/widget/GapWorker.java
index 3631e7d..ab51de7 100644
--- a/v7/recyclerview/src/android/support/v7/widget/GapWorker.java
+++ b/v7/recyclerview/src/android/support/v7/widget/GapWorker.java
@@ -24,7 +24,7 @@
import java.util.Comparator;
import java.util.concurrent.TimeUnit;
-class GapWorker implements Runnable {
+final class GapWorker implements Runnable {
static final ThreadLocal<GapWorker> sGapWorker = new ThreadLocal<>();
@@ -56,7 +56,7 @@
private ArrayList<Task> mTasks = new ArrayList<>();
/**
- * Prefetch information associated with a specfic RecyclerView.
+ * Prefetch information associated with a specific RecyclerView.
*/
static class PrefetchRegistryImpl implements RecyclerView.PrefetchRegistry {
private int mPrefetchDx;
@@ -70,7 +70,7 @@
mPrefetchDy = dy;
}
- void collectPrefetchPositionsFromView(RecyclerView view) {
+ void collectPrefetchPositionsFromView(RecyclerView view, boolean nested) {
mCount = 0;
if (mPrefetchArray != null) {
Arrays.fill(mPrefetchArray, -1);
@@ -79,9 +79,21 @@
final RecyclerView.LayoutManager layout = view.mLayout;
if (view.mAdapter != null
&& layout != null
- && layout.isItemPrefetchEnabled()
- && !view.hasPendingAdapterUpdates()) {
- layout.collectPrefetchPositions(mPrefetchDx, mPrefetchDy, view.mState, this);
+ && layout.isItemPrefetchEnabled()) {
+ if (nested) {
+ // nested prefetch, only if no adapter updates pending. Note: we don't query
+ // view.hasPendingAdapterUpdates(), as first layout may not have occurred
+ if (!view.mAdapterHelper.hasPendingUpdates()) {
+ layout.collectInitialPrefetchPositions(view.mAdapter.getItemCount(), this);
+ }
+ } else {
+ // momentum based prefetch, only if we trust current child/adapter state
+ if (!view.hasPendingAdapterUpdates()) {
+ layout.collectAdjacentPrefetchPositions(mPrefetchDx, mPrefetchDy,
+ view.mState, this);
+ }
+ }
+
if (mCount > layout.mPrefetchMaxCountObserved) {
layout.mPrefetchMaxCountObserved = mCount;
view.mRecycler.updateViewCacheSize();
@@ -195,7 +207,7 @@
int totalTaskCount = 0;
for (int i = 0; i < viewCount; i++) {
RecyclerView view = mRecyclerViews.get(i);
- view.mPrefetchRegistry.collectPrefetchPositionsFromView(view);
+ view.mPrefetchRegistry.collectPrefetchPositionsFromView(view, false);
totalTaskCount += view.mPrefetchRegistry.mCount;
}
@@ -244,36 +256,67 @@
return false;
}
+ private RecyclerView.ViewHolder flushWorkWithDeadline(RecyclerView view,
+ int position, long deadlineNs) {
+ if (isPrefetchPositionAttached(view, position)) {
+ // don't attempt to prefetch attached views
+ return null;
+ }
+
+ RecyclerView.Recycler recycler = view.mRecycler;
+ RecyclerView.ViewHolder holder = recycler.tryGetViewHolderForPositionByDeadline(
+ position, false, deadlineNs);
+
+ if (holder != null) {
+ if (holder.isBound()) {
+ // Only give the view a chance to go into the cache if binding succeeded
+ // Note that we must use public method, since item may need cleanup
+ recycler.recycleView(holder.itemView);
+ } else {
+ // Didn't bind, so we can't cache the view, but it will stay in the pool until
+ // next prefetch/traversal. If a View fails to bind, it means we didn't have
+ // enough time prior to the deadline (and won't for other instances of this
+ // type, during this GapWorker prefetch pass).
+ recycler.addViewHolderToRecycledViewPool(holder, false);
+ }
+ }
+ return holder;
+ }
+
+ private void flushTaskWithDeadline(Task task, long deadlineNs) {
+ long taskDeadlineNs = task.immediate ? RecyclerView.FOREVER_NS : deadlineNs;
+ RecyclerView.ViewHolder holder = flushWorkWithDeadline(task.view,
+ task.position, taskDeadlineNs);
+ if (holder != null && holder.mNestedRecyclerView != null) {
+ // do nested prefetch!
+ final RecyclerView innerView = holder.mNestedRecyclerView;
+ final PrefetchRegistryImpl innerPrefetchRegistry = innerView.mPrefetchRegistry;
+ innerPrefetchRegistry.collectPrefetchPositionsFromView(innerView, true);
+
+ if (innerPrefetchRegistry.mCount != 0) {
+ try {
+ TraceCompat.beginSection(RecyclerView.TRACE_NESTED_PREFETCH_TAG);
+ innerView.mState.prepareForNestedPrefetch(innerView.mAdapter);
+ for (int i = 0; i < innerPrefetchRegistry.mCount * 2; i += 2) {
+ // Note that we ignore immediate flag for inner items because
+ // we have lower confidence they're needed next frame.
+ final int innerPosition = innerPrefetchRegistry.mPrefetchArray[i];
+ flushWorkWithDeadline(innerView, innerPosition, deadlineNs);
+ }
+ } finally {
+ TraceCompat.endSection();
+ }
+ }
+ }
+ }
+
private void flushTasksWithDeadline(long deadlineNs) {
for (int i = 0; i < mTasks.size(); i++) {
final Task task = mTasks.get(i);
if (task.view == null) {
- // abort, only empty Tasks left
- return;
+ break; // done with populated tasks
}
-
- if (isPrefetchPositionAttached(task.view, task.position)) {
- // don't attempt to prefetch attached views
- continue;
- }
-
- RecyclerView.Recycler recycler = task.view.mRecycler;
- RecyclerView.ViewHolder holder = recycler.tryGetViewHolderForPositionByDeadline(
- task.position, false, task.immediate ? RecyclerView.FOREVER_NS : deadlineNs);
-
- if (holder != null) {
- if (holder.isBound()) {
- // Only give the view a chance to go into the cache if binding succeeded
- // Note that we must use public method, since item may need cleanup
- recycler.recycleView(holder.itemView);
- } else {
- // Didn't bind, so we can't cache the view, but it will stay in the pool until
- // next prefetch/traversal. If a View fails to bind, it means we didn't have
- // enough time prior to the deadline (and won't for other instances of this
- // type, during this GapWorker prefetch pass).
- recycler.addViewHolderToRecycledViewPool(holder);
- }
- }
+ flushTaskWithDeadline(task, deadlineNs);
task.clear();
}
}
diff --git a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
index 1f4682f..ceabf01 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
@@ -144,6 +144,11 @@
private final LayoutChunkResult mLayoutChunkResult = new LayoutChunkResult();
/**
+ * Number of items to prefetch when first coming on screen with new data.
+ */
+ private int mInitialItemPrefetchCount = 2;
+
+ /**
* Creates a vertical LinearLayoutManager
*
* @param context Current context, will be used to access resources.
@@ -1190,9 +1195,56 @@
}
}
+ /**
+ * TODO: we expand cache by largest prefetch seen - not appropriate for nested prefetch?
+ * @hide
+ */
+ @Override
+ public void collectInitialPrefetchPositions(int adapterItemCount,
+ RecyclerView.PrefetchRegistry prefetchRegistry) {
+ final boolean fromEnd;
+ final int anchorPos;
+ if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
+ // use restored state, since it hasn't been resolved yet
+ fromEnd = mPendingSavedState.mAnchorLayoutFromEnd;
+ anchorPos = mPendingSavedState.mAnchorPosition;
+ } else {
+ resolveShouldLayoutReverse();
+ fromEnd = mShouldReverseLayout;
+ if (mPendingScrollPosition == NO_POSITION) {
+ anchorPos = fromEnd ? adapterItemCount - 1 : 0;
+ } else {
+ anchorPos = mPendingScrollPosition;
+ }
+ }
+
+ final int direction = fromEnd
+ ? LayoutState.ITEM_DIRECTION_HEAD
+ : LayoutState.ITEM_DIRECTION_TAIL;
+ int targetPos = anchorPos;
+ for (int i = 0; i < mInitialItemPrefetchCount; i++) {
+ if (targetPos >= 0 && targetPos < adapterItemCount) {
+ prefetchRegistry.addPosition(targetPos, 0);
+ } else {
+ break; // no more to prefetch
+ }
+ targetPos += direction;
+ }
+ }
+
+ /** @hide */
+ public void setInitialPrefetchItemCount(int itemCount) {
+ mInitialItemPrefetchCount = itemCount;
+ }
+
+ /** @hide */
+ public int getInitialItemPrefetchCount() {
+ return mInitialItemPrefetchCount;
+ }
+
/** @hide */
@Override
- public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+ public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
RecyclerView.PrefetchRegistry prefetchRegistry) {
int delta = (mOrientation == HORIZONTAL) ? dx : dy;
if (getChildCount() == 0 || delta == 0) {
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index 11e94e5..dc6fb47 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -182,6 +182,12 @@
*/
private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
+ /**
+ * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
+ * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
+ */
+ private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
+
static final boolean DISPATCH_TEMP_DETACH = false;
public static final int HORIZONTAL = 0;
public static final int VERTICAL = 1;
@@ -250,6 +256,12 @@
static final String TRACE_PREFETCH_TAG = "RV Prefetch";
/**
+ * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
+ * RecyclerView.
+ */
+ static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
+
+ /**
* RecyclerView is creating a new View.
* If too many of these present in Systrace:
* - There might be a problem in Recycling (e.g. custom Animations that set transient state and
@@ -1575,6 +1587,7 @@
| AdapterHelper.UpdateOp.MOVE)) {
TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
eatRequestLayout();
+ onEnterLayoutOrScroll();
mAdapterHelper.preProcess();
if (!mLayoutRequestEaten) {
if (hasUpdatedView()) {
@@ -1585,6 +1598,7 @@
}
}
resumeRequestLayout(true);
+ onExitLayoutOrScroll();
TraceCompat.endSection();
} else if (mAdapterHelper.hasPendingUpdates()) {
TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
@@ -2234,6 +2248,10 @@
direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
final View found = ff.findNextFocus(this, focused, absDir);
needsFocusFailureLayout = found == null;
+ if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
+ // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
+ direction = absDir;
+ }
}
if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
@@ -2241,6 +2259,10 @@
? View.FOCUS_RIGHT : View.FOCUS_LEFT;
final View found = ff.findNextFocus(this, focused, absDir);
needsFocusFailureLayout = found == null;
+ if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
+ // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
+ direction = absDir;
+ }
}
if (needsFocusFailureLayout) {
consumePendingUpdateOperations();
@@ -2406,9 +2428,11 @@
// NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
Display display = ViewCompat.getDisplay(this);
float refreshRate = 60.0f;
- if (display != null
- && display.getRefreshRate() >= 30.0f) {
- refreshRate = display.getRefreshRate();
+ if (display != null) {
+ float displayRefreshRate = display.getRefreshRate();
+ if (displayRefreshRate >= 30.0f) {
+ refreshRate = displayRefreshRate;
+ }
}
mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
GapWorker.sGapWorker.set(mGapWorker);
@@ -4947,6 +4971,50 @@
}
}
+ /**
+ * Utility method for finding an internal RecyclerView, if present
+ */
+ @Nullable
+ static RecyclerView findNestedRecyclerView(@NonNull View view) {
+ if (!(view instanceof ViewGroup)) {
+ return null;
+ }
+ if (view instanceof RecyclerView) {
+ return (RecyclerView) view;
+ }
+ final ViewGroup parent = (ViewGroup) view;
+ final int count = parent.getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = parent.getChildAt(i);
+ final RecyclerView descendant = findNestedRecyclerView(child);
+ if (descendant != null) {
+ return descendant;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Utility method for clearing holder's internal RecyclerView, if present
+ */
+ static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
+ if (holder.mNestedRecyclerView != null) {
+ View item = holder.mNestedRecyclerView;
+ while (item != null) {
+ if (item == holder.itemView) {
+ return; // match found, don't need to clear
+ }
+
+ ViewParent parent = item.getParent();
+ if (parent instanceof View) {
+ item = (View) parent;
+ } else {
+ item = null;
+ }
+ }
+ holder.mNestedRecyclerView = null; // not nested
+ }
+ }
/**
* Time base for deadline-aware work scheduling. Overridable for testing.
@@ -5310,6 +5378,11 @@
return null;
}
holder = mAdapter.createViewHolder(RecyclerView.this, type);
+ if (ALLOW_THREAD_GAP_WORK) {
+ // only bother finding nested RV if prefetching
+ holder.mNestedRecyclerView = findNestedRecyclerView(holder.itemView);
+ }
+
long end = getNanoTime();
mRecyclerPool.factorInCreateTime(type, end - start);
if (DEBUG) {
@@ -5468,7 +5541,7 @@
if (DEBUG) {
Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
}
- addViewHolderToRecycledViewPool(viewHolder);
+ addViewHolderToRecycledViewPool(viewHolder, true);
mCachedViews.remove(cachedViewIndex);
}
@@ -5536,12 +5609,15 @@
cached = true;
}
if (!cached) {
- addViewHolderToRecycledViewPool(holder);
+ addViewHolderToRecycledViewPool(holder, true);
recycled = true;
}
- } else if (DEBUG) {
- Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
- + "re-visit here. We are still removing it from animation lists");
+ } else {
+ holder.mNestedRecyclerView = null;
+ if (DEBUG) {
+ Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
+ + "re-visit here. We are still removing it from animation lists");
+ }
}
// even if the holder is not removed, we still call this method so that it is removed
// from view holder lists.
@@ -5551,9 +5627,20 @@
}
}
- void addViewHolderToRecycledViewPool(ViewHolder holder) {
+ /**
+ * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
+ *
+ * Pass false to dispatchRecycled for views that have not been bound.
+ *
+ * @param holder Holder to be added to the pool.
+ * @param dispatchRecycled True to dispatch View recycled callbacks.
+ */
+ void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {
+ clearNestedRecyclerViewIfNotNested(holder);
ViewCompat.setAccessibilityDelegate(holder.itemView, null);
- dispatchViewRecycled(holder);
+ if (dispatchRecycled) {
+ dispatchViewRecycled(holder);
+ }
holder.mOwnerRecyclerView = null;
getRecycledViewPool().putRecycledView(holder);
}
@@ -6609,7 +6696,8 @@
/**
* Written by {@link GapWorker} when prefetches occur to track largest number of view ever
- * requested by a {@link #collectPrefetchPositions(int, int, State, PrefetchRegistry)} call.
+ * requested by a {@link #collectInitialPrefetchPositions(int, PrefetchRegistry)} or
+ * {@link #collectAdjacentPrefetchPositions(int, int, State, PrefetchRegistry)} call.
*/
int mPrefetchMaxCountObserved;
@@ -6935,15 +7023,16 @@
}
/**
- * Gather all positions from the LayoutManager to be prefetched.
+ * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
*
* <p>If item prefetch is enabled, this method is called in between traversals to gather
* which positions the LayoutManager will soon need, given upcoming movement in subsequent
* traversals.</p>
*
* <p>The LayoutManager should call {@link PrefetchRegistry#addPosition(int, int)} for each
- * item to be prepared, and these positions will have their ViewHolders created and bound
- * in advance of being needed by a scroll or layout.</p>
+ * item to be prepared, and these positions will have their ViewHolders created and bound,
+ * if there is sufficient time available, in advance of being needed by a
+ * scroll or layout.</p>
*
* @param dx X movement component.
* @param dy Y movement component.
@@ -6951,10 +7040,41 @@
* @param prefetchRegistry PrefetchRegistry to add prefetch entries into.
*
* @see #isItemPrefetchEnabled()
+ * @see #collectInitialPrefetchPositions(int, PrefetchRegistry)
*
* @hide
*/
- public void collectPrefetchPositions(int dx, int dy, State state,
+ public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
+ PrefetchRegistry prefetchRegistry) {}
+
+ /**
+ * Gather all positions from the LayoutManager to be prefetched in preperation for its
+ * Recyclerview to come on screen, due to the movement of another, containing RecyclerView.
+ *
+ * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
+ *
+ * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
+ * LayoutManager, this method is called in between draw traversals to gather
+ * which positions this LayoutManager will first need, once it appears on the screen.</p>
+ *
+ * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
+ * vertically scrolling LayoutManager, this method would be called when the horizontal list
+ * is about to come onscreen.</p>
+ *
+ * <p>The LayoutManager should call {@link PrefetchRegistry#addPosition(int, int)} for each
+ * item to be prepared, and these positions will have their ViewHolders created and bound,
+ * if there is sufficient time available, in advance of being needed by a
+ * scroll or layout.</p>
+ *
+ * @param adapterItemCount number of items in the associated adapter.
+ * @param prefetchRegistry PrefetchRegistry to add prefetch entries into.
+ *
+ * @see #isItemPrefetchEnabled()
+ * @see #collectAdjacentPrefetchPositions(int, int, State, PrefetchRegistry
+ *
+ * @hide
+ */
+ public void collectInitialPrefetchPositions(int adapterItemCount,
PrefetchRegistry prefetchRegistry) {}
void dispatchAttachedToWindow(RecyclerView view) {
@@ -9652,6 +9772,7 @@
*/
public static abstract class ViewHolder {
public final View itemView;
+ RecyclerView mNestedRecyclerView;
int mPosition = NO_POSITION;
int mOldPosition = NO_POSITION;
long mItemId = NO_ID;
@@ -10042,6 +10163,7 @@
clearPayload();
mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
+ clearNestedRecyclerViewIfNotNested(this);
}
/**
@@ -10946,23 +11068,15 @@
}
}
- @IntDef(flag = true, value = {
- STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface LayoutState {}
+ /** Owned by SmoothScroller */
private int mTargetPosition = RecyclerView.NO_POSITION;
- @LayoutState
- int mLayoutStep = STEP_START;
-
private SparseArray<Object> mData;
- /**
- * Number of items adapter has.
- */
- int mItemCount = 0;
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // Fields below are carried from one layout pass to the next
+ ////////////////////////////////////////////////////////////////////////////////////////////
/**
* Number of items adapter had in the previous layout.
@@ -10975,18 +11089,40 @@
*/
int mDeletedInvisibleItemCountSincePreviousLayout = 0;
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // Fields below must be updated or cleared before they are used (generally before a pass)
+ ////////////////////////////////////////////////////////////////////////////////////////////
+
+ @IntDef(flag = true, value = {
+ STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface LayoutState {}
+
+ @LayoutState
+ int mLayoutStep = STEP_START;
+
+ /**
+ * Number of items adapter has.
+ */
+ int mItemCount = 0;
+
boolean mStructureChanged = false;
boolean mInPreLayout = false;
- boolean mRunSimpleAnimations = false;
-
- boolean mRunPredictiveAnimations = false;
-
boolean mTrackOldChangeHolders = false;
boolean mIsMeasuring = false;
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // Fields below are always reset outside of the pass (or passes) that use them
+ ////////////////////////////////////////////////////////////////////////////////////////////
+
+ boolean mRunSimpleAnimations = false;
+
+ boolean mRunPredictiveAnimations = false;
+
/**
* This data is saved before a layout calculation happens. After the layout is finished,
* if the previously focused view has been replaced with another view for the same item, we
@@ -10998,6 +11134,8 @@
// that one instead
int mFocusedSubChildId;
+ ////////////////////////////////////////////////////////////////////////////////////////////
+
State reset() {
mTargetPosition = RecyclerView.NO_POSITION;
if (mData != null) {
@@ -11010,6 +11148,22 @@
}
/**
+ * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
+ * prior to any layout passes.
+ *
+ * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
+ * that Recycler#getViewForPosition() can function safely.</p>
+ */
+ void prepareForNestedPrefetch(Adapter adapter) {
+ mLayoutStep = STEP_START;
+ mItemCount = adapter.getItemCount();
+ mStructureChanged = false;
+ mInPreLayout = false;
+ mTrackOldChangeHolders = false;
+ mIsMeasuring = false;
+ }
+
+ /**
* Returns true if the RecyclerView is currently measuring the layout. This value is
* {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
* has non-exact measurement specs.
diff --git a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
index 7040bcc..62a7f09 100644
--- a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
@@ -213,8 +213,8 @@
private boolean mSmoothScrollbarEnabled = true;
/**
- * Temporary array used (solely in {@link #collectPrefetchPositions}) for stashing and sorting
- * distances to views being prefetched.
+ * Temporary array used (solely in {@link #collectAdjacentPrefetchPositions}) for stashing and
+ * sorting distances to views being prefetched.
*/
private int[] mPrefetchDistances;
@@ -2073,7 +2073,7 @@
/** @hide */
@Override
- public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+ public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
RecyclerView.PrefetchRegistry prefetchRegistry) {
/* This method uses the simplifying assumption that the next N items (where N = span count)
* will be assigned, one-to-one, to spans, where ordering is based on which span extends
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java b/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java
index 9137747..1bb4fc3 100644
--- a/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java
@@ -90,7 +90,8 @@
scrollAndExpectTiles(40, "scroll up a little, no new tiles loaded");
}
- @Test
+ // This test is disabled as it is flaky.
+ // @Test
public void tileCaching() throws Throwable {
scrollAndExpectTiles(25, "next screen", 30, 40);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java
index 0a28c2b..3ac53f4 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.support.test.filters.MediumTest;
+import android.support.test.filters.Suppress;
import android.support.test.runner.AndroidJUnit4;
import android.support.v7.util.AsyncListUtil;
import android.view.View;
@@ -60,6 +61,8 @@
public int mStartPrefetch = 0;
public int mEndPrefetch = 0;
+ // Test is disabled as it is flaky.
+ @Suppress
@Test
public void asyncListUtil() throws Throwable {
mRecyclerView = inflateWrappedRV();
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java
index 8d93553..4eee5de 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java
@@ -318,10 +318,10 @@
}
@Override
- public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+ public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
RecyclerView.PrefetchRegistry prefetchRegistry) {
if (prefetchLatch != null) prefetchLatch.countDown();
- super.collectPrefetchPositions(dx, dy, state, prefetchRegistry);
+ super.collectAdjacentPrefetchPositions(dx, dy, state, prefetchRegistry);
}
}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseLinearLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseLinearLayoutManagerTest.java
index 9bea02c..505b1c3 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseLinearLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseLinearLayoutManagerTest.java
@@ -635,10 +635,10 @@
}
@Override
- public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+ public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
RecyclerView.PrefetchRegistry prefetchRegistry) {
if (prefetchLatch != null) prefetchLatch.countDown();
- super.collectPrefetchPositions(dx, dy, state, prefetchRegistry);
+ super.collectAdjacentPrefetchPositions(dx, dy, state, prefetchRegistry);
}
}
}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseStaggeredGridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseStaggeredGridLayoutManagerTest.java
index ea88391..59175ac 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseStaggeredGridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseStaggeredGridLayoutManagerTest.java
@@ -805,10 +805,10 @@
}
@Override
- public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+ public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
RecyclerView.PrefetchRegistry prefetchRegistry) {
if (prefetchLatch != null) prefetchLatch.countDown();
- super.collectPrefetchPositions(dx, dy, state, prefetchRegistry);
+ super.collectAdjacentPrefetchPositions(dx, dy, state, prefetchRegistry);
}
}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/CacheUtils.java b/v7/recyclerview/tests/src/android/support/v7/widget/CacheUtils.java
index 0bd67f7..70a8644 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/CacheUtils.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/CacheUtils.java
@@ -28,7 +28,7 @@
static void verifyPositionsPrefetched(RecyclerView view, int dx, int dy,
Integer[]... positionData) {
RecyclerView.PrefetchRegistry prefetchRegistry = mock(RecyclerView.PrefetchRegistry.class);
- view.mLayout.collectPrefetchPositions(
+ view.mLayout.collectAdjacentPrefetchPositions(
dx, dy, view.mState, prefetchRegistry);
verify(prefetchRegistry, times(positionData.length)).addPosition(anyInt(), anyInt());
@@ -78,4 +78,15 @@
}
}
}
+
+ static RecyclerView.ViewHolder peekAtCachedViewForPosition(RecyclerView view, int position) {
+ for (int i = 0; i < view.mRecycler.mCachedViews.size(); i++) {
+ RecyclerView.ViewHolder holder = view.mRecycler.mCachedViews.get(i);
+ if (holder.mPosition == position) {
+ return holder;
+ }
+ }
+ fail("Unable to find view with position " + position);
+ return null;
+ }
}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/FocusSearchNavigationTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/FocusSearchNavigationTest.java
index 7bed232..55bed7e 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/FocusSearchNavigationTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/FocusSearchNavigationTest.java
@@ -28,6 +28,7 @@
import android.app.Activity;
import android.content.Context;
+import android.os.Build;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
@@ -60,8 +61,8 @@
@RunWith(Parameterized.class)
public class FocusSearchNavigationTest {
@Rule
- public ActivityTestRule<RecyclerViewTestActivity> mActivityRule
- = new ActivityTestRule<>(RecyclerViewTestActivity.class);
+ public ActivityTestRule<RecyclerViewTestActivity> mActivityRule =
+ new ActivityTestRule<>(RecyclerViewTestActivity.class);
private final int mOrientation;
private final int mLayoutDir;
@@ -73,11 +74,19 @@
@Parameterized.Parameters(name = "orientation:{0},layoutDir:{1}")
public static List<Object[]> params() {
- return Arrays.asList(
- new Object[]{VERTICAL, ViewCompat.LAYOUT_DIRECTION_LTR},
- new Object[]{HORIZONTAL, ViewCompat.LAYOUT_DIRECTION_LTR},
- new Object[]{HORIZONTAL, ViewCompat.LAYOUT_DIRECTION_RTL}
- );
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ return Arrays.asList(
+ new Object[]{VERTICAL, ViewCompat.LAYOUT_DIRECTION_LTR},
+ new Object[]{HORIZONTAL, ViewCompat.LAYOUT_DIRECTION_LTR},
+ new Object[]{HORIZONTAL, ViewCompat.LAYOUT_DIRECTION_RTL}
+ );
+ } else {
+ // Do not test RTL before API 17
+ return Arrays.asList(
+ new Object[]{VERTICAL, ViewCompat.LAYOUT_DIRECTION_LTR},
+ new Object[]{HORIZONTAL, ViewCompat.LAYOUT_DIRECTION_LTR}
+ );
+ }
}
private Activity mActivity;
@@ -91,11 +100,11 @@
@Override
public void run() {
mActivity.setContentView(R.layout.focus_search_activity);
- mActivity.getWindow().getDecorView().setLayoutDirection(mLayoutDir);
+ ViewCompat.setLayoutDirection(mActivity.getWindow().getDecorView(), mLayoutDir);
LinearLayout linearLayout = (LinearLayout) mActivity.findViewById(R.id.root);
linearLayout.setOrientation(mOrientation);
mRecyclerView = (RecyclerView) mActivity.findViewById(R.id.recycler_view);
- mRecyclerView.setLayoutDirection(mLayoutDir);
+ ViewCompat.setLayoutDirection(mRecyclerView, mLayoutDir);
LinearLayoutManager layout = new LinearLayoutManager(mActivity.getBaseContext());
layout.setOrientation(mOrientation);
mRecyclerView.setLayoutManager(layout);
@@ -115,7 +124,7 @@
waitForIdleSync();
assertThat("test sanity", mRecyclerView.getLayoutManager().getLayoutDirection(),
is(mLayoutDir));
- assertThat("test sanity", mRecyclerView.getLayoutDirection(), is(mLayoutDir));
+ assertThat("test sanity", ViewCompat.getLayoutDirection(mRecyclerView), is(mLayoutDir));
}
@Test
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GapWorkerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GapWorkerTest.java
index d25526f..5d08931 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GapWorkerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GapWorkerTest.java
@@ -22,7 +22,9 @@
import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.os.Build;
import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -129,4 +131,17 @@
assertEquals(200, list.get(2).distanceToItem);
assertEquals(900, list.get(3).distanceToItem);
}
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+ @Test
+ public void gapWorkerWithoutLayout() {
+ RecyclerView recyclerView = new RecyclerView(getContext());
+ try {
+ assertFalse(recyclerView.mIsAttached);
+ recyclerView.onAttachedToWindow();
+ recyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+ } finally {
+ recyclerView.onDetachedFromWindow();
+ }
+ }
}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java
index 1c436d7..22adefd 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java
@@ -19,7 +19,9 @@
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
+import android.os.Build;
import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
import android.support.test.runner.AndroidJUnit4;
import android.support.v4.view.ViewCompat;
import android.view.ViewGroup;
@@ -35,6 +37,7 @@
@MediumTest
@RunWith(AndroidJUnit4.class)
public class RecyclerViewAccessibilityLifecycleTest extends BaseRecyclerViewInstrumentationTest {
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
@Test
public void dontDispatchChangeDuringLayout() throws Throwable {
LayoutAllLayoutManager lm = new LayoutAllLayoutManager();
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
index 38b118e..5379354 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
@@ -432,6 +432,8 @@
});
}
+ // Disable this test on ICS because it causes testing devices to freeze.
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
@Test
public void dontReuseHiddenViewOnInvalidate() throws Throwable {
reuseHiddenViewTest(new ReuseTestCallback() {
@@ -1392,6 +1394,8 @@
mLayoutManager.waitForLayout(2);
}
+ // Run this test on Jelly Bean and newer because hasTransientState was introduced in API 16.
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
@Test
public void appCancelAnimationInDetach() throws Throwable {
final View[] addedView = new View[2];
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
index fff41f5..48abe68 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
@@ -40,6 +40,7 @@
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
+import android.widget.FrameLayout;
import android.widget.TextView;
import org.junit.Before;
@@ -368,7 +369,7 @@
mRecyclerView.setAdapter(new MockAdapter(20));
MockLayoutManager mlm = new MockLayoutManager() {
@Override
- public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+ public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
RecyclerView.PrefetchRegistry prefetchManager) {
prefetchManager.addPosition(0, 0);
prefetchManager.addPosition(1, 0);
@@ -387,7 +388,7 @@
mRecyclerView.layout(0, 0, 100, 100);
// prefetch gets 3 items, so expands cache by 3
- mRecyclerView.mPrefetchRegistry.collectPrefetchPositionsFromView(mRecyclerView);
+ mRecyclerView.mPrefetchRegistry.collectPrefetchPositionsFromView(mRecyclerView, false);
assertEquals(3, mRecyclerView.mPrefetchRegistry.mCount);
assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE + 3, recycler.mViewCacheMax);
@@ -414,6 +415,49 @@
}
}
+ @Test
+ public void findNestedRecyclerView() {
+ RecyclerView recyclerView = new RecyclerView(getContext());
+ assertEquals(recyclerView, RecyclerView.findNestedRecyclerView(recyclerView));
+
+ ViewGroup parent = new FrameLayout(getContext());
+ assertEquals(null, RecyclerView.findNestedRecyclerView(parent));
+ parent.addView(recyclerView);
+ assertEquals(recyclerView, RecyclerView.findNestedRecyclerView(parent));
+
+ ViewGroup grandParent = new FrameLayout(getContext());
+ assertEquals(null, RecyclerView.findNestedRecyclerView(grandParent));
+ grandParent.addView(parent);
+ assertEquals(recyclerView, RecyclerView.findNestedRecyclerView(grandParent));
+ }
+
+ @Test
+ public void clearNestedRecyclerViewIfNotNested() {
+ RecyclerView recyclerView = new RecyclerView(getContext());
+ ViewGroup parent = new FrameLayout(getContext());
+ parent.addView(recyclerView);
+ ViewGroup grandParent = new FrameLayout(getContext());
+ grandParent.addView(parent);
+
+ // verify trivial noop case
+ RecyclerView.ViewHolder holder = new RecyclerView.ViewHolder(recyclerView) {};
+ holder.mNestedRecyclerView = recyclerView;
+ RecyclerView.clearNestedRecyclerViewIfNotNested(holder);
+ assertEquals(recyclerView, holder.mNestedRecyclerView);
+
+ // verify clear case
+ holder = new RecyclerView.ViewHolder(new View(getContext())) {};
+ holder.mNestedRecyclerView = recyclerView;
+ RecyclerView.clearNestedRecyclerViewIfNotNested(holder);
+ assertNull(holder.mNestedRecyclerView);
+
+ // verify more deeply nested case
+ holder = new RecyclerView.ViewHolder(grandParent) {};
+ holder.mNestedRecyclerView = recyclerView;
+ RecyclerView.clearNestedRecyclerViewIfNotNested(holder);
+ assertEquals(recyclerView, holder.mNestedRecyclerView);
+ }
+
static class MockLayoutManager extends RecyclerView.LayoutManager {
int mLayoutCount = 0;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java
index 755173d..a9a67b5 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java
@@ -17,10 +17,12 @@
package android.support.v7.widget;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -29,6 +31,7 @@
import android.content.Context;
import android.os.Build;
+import android.os.Parcelable;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SdkSuppress;
import android.support.test.filters.SmallTest;
@@ -36,6 +39,8 @@
import android.view.View;
import android.view.ViewGroup;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -43,6 +48,7 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -109,7 +115,7 @@
}
@Override
- public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+ public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
RecyclerView.PrefetchRegistry prefetchManager) {
prefetchManager.addPosition(0, 0);
prefetchManager.addPosition(1, 0);
@@ -151,7 +157,7 @@
any(RecyclerView.ViewHolder.class), anyInt(), any(List.class));
assertTrue(mRecycler.mCachedViews.size() == 3);
- CacheUtils.verifyCacheContainsPositions(mRecyclerView, 0, 1, 2);
+ CacheUtils.verifyCacheContainsPrefetchedPositions(mRecyclerView, 0, 1, 2);
}
}
@@ -181,7 +187,7 @@
mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
assertEquals(5, mRecyclerView.mRecycler.mViewCacheMax);
- CacheUtils.verifyCacheContainsPositions(mRecyclerView, 3, 4, 5);
+ CacheUtils.verifyCacheContainsPrefetchedPositions(mRecyclerView, 3, 4, 5);
// further views recycled, as though from scrolling, shouldn't evict prefetched views:
mRecycler.recycleView(mRecycler.getViewForPosition(10));
@@ -308,6 +314,62 @@
}
@Test
+ public void partialPrefetchAvoidsViewRecycledCallback() {
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+
+ // 100x100 pixel views
+ RecyclerView.Adapter adapter = new RecyclerView.Adapter() {
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ mRecyclerView.registerTimePassingMs(5);
+ View view = new View(getContext());
+ view.setMinimumWidth(100);
+ view.setMinimumHeight(100);
+ return new RecyclerView.ViewHolder(view) {};
+ }
+
+ @Override
+ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+ mRecyclerView.registerTimePassingMs(5);
+ }
+
+ @Override
+ public int getItemCount() {
+ return 100;
+ }
+
+ @Override
+ public void onViewRecycled(RecyclerView.ViewHolder holder) {
+ // verify unbound view doesn't get
+ assertNotEquals(RecyclerView.NO_POSITION, holder.getAdapterPosition());
+ }
+ };
+ mRecyclerView.setAdapter(adapter);
+
+ layout(100, 300);
+
+ // offset scroll so that no prefetch-able views are directly adjacent to viewport
+ mRecyclerView.scrollBy(0, 50);
+
+ assertTrue(mRecycler.mCachedViews.size() == 0);
+ assertTrue(mRecyclerView.getRecycledViewPool().getRecycledViewCount(0) == 0);
+
+ // Should take 10 ms to inflate + bind, so just give it 9 so it doesn't have time to bind
+ final long deadlineNs = mRecyclerView.getNanoTime() + TimeUnit.MILLISECONDS.toNanos(9);
+
+ // Timed prefetch
+ mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1);
+ mRecyclerView.mGapWorker.prefetch(deadlineNs);
+
+ // will have enough time to inflate but not bind one view
+ assertTrue(mRecycler.mCachedViews.size() == 0);
+ assertTrue(mRecyclerView.getRecycledViewPool().getRecycledViewCount(0) == 1);
+ RecyclerView.ViewHolder pooledHolder = mRecyclerView.getRecycledViewPool()
+ .mScrap.get(0).mScrapHeap.get(0);
+ assertEquals(RecyclerView.NO_POSITION, pooledHolder.getAdapterPosition());
+ }
+
+ @Test
public void prefetchStaggeredItemsPriority() {
StaggeredGridLayoutManager sglm =
new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
@@ -392,8 +454,7 @@
final RecyclerView.Adapter adapter = new RecyclerView.Adapter() {
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- return new RecyclerView.ViewHolder(new View(parent.getContext())) {
- };
+ return new RecyclerView.ViewHolder(new View(parent.getContext())) {};
}
@Override
@@ -450,4 +511,204 @@
assertEquals(2, llm.getChildCount());
assertEquals(4, mRecyclerView.getChildCount());
}
+
+ @Test
+ public void viewHolderFindsNestedRecyclerViews() {
+ LinearLayoutManager llm = new LinearLayoutManager(getContext());
+ mRecyclerView.setLayoutManager(llm);
+
+ RecyclerView.Adapter mockAdapter = mock(RecyclerView.Adapter.class);
+ when(mockAdapter.onCreateViewHolder(any(ViewGroup.class), anyInt()))
+ .thenAnswer(new Answer<RecyclerView.ViewHolder>() {
+ @Override
+ public RecyclerView.ViewHolder answer(InvocationOnMock invocation)
+ throws Throwable {
+ View view = new RecyclerView(getContext());
+ view.setLayoutParams(new RecyclerView.LayoutParams(100, 100));
+ return new RecyclerView.ViewHolder(view) {};
+ }
+ });
+ when(mockAdapter.getItemCount()).thenReturn(100);
+ mRecyclerView.setAdapter(mockAdapter);
+
+ layout(100, 200);
+
+ verify(mockAdapter, times(2)).onCreateViewHolder(any(ViewGroup.class), anyInt());
+ verify(mockAdapter, times(2)).onBindViewHolder(
+ argThat(new BaseMatcher<RecyclerView.ViewHolder>() {
+ @Override
+ public boolean matches(Object item) {
+ RecyclerView.ViewHolder holder = (RecyclerView.ViewHolder) item;
+ return holder.itemView == holder.mNestedRecyclerView;
+ }
+
+ @Override
+ public void describeTo(Description description) { }
+ }),
+ anyInt(),
+ any(List.class));
+ }
+
+ static class InnerAdapter extends RecyclerView.Adapter<InnerAdapter.ViewHolder> {
+ private static final int INNER_ITEM_COUNT = 20;
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ ViewHolder(View itemView) {
+ super(itemView);
+ }
+ }
+
+ InnerAdapter() {}
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = new View(parent.getContext());
+ view.setLayoutParams(new RecyclerView.LayoutParams(100, 100));
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {}
+
+ @Override
+ public int getItemCount() {
+ return INNER_ITEM_COUNT;
+ }
+ }
+
+ static class OuterAdapter extends RecyclerView.Adapter<OuterAdapter.ViewHolder> {
+ private static final int OUTER_ITEM_COUNT = 10;
+
+ private boolean mReverseInner;
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ private final RecyclerView mRecyclerView;
+ ViewHolder(RecyclerView itemView) {
+ super(itemView);
+ mRecyclerView = itemView;
+ }
+ }
+
+ ArrayList<InnerAdapter> mAdapters = new ArrayList<>();
+ ArrayList<Parcelable> mSavedStates = new ArrayList<>();
+ RecyclerView.RecycledViewPool mSharedPool = new RecyclerView.RecycledViewPool();
+
+ OuterAdapter() {
+ this(false);
+ }
+
+ OuterAdapter(boolean reverseInner) {
+ mReverseInner = reverseInner;
+ for (int i = 0; i <= OUTER_ITEM_COUNT; i++) {
+ mAdapters.add(new InnerAdapter());
+ mSavedStates.add(null);
+ }
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ RecyclerView rv = new RecyclerView(parent.getContext());
+ rv.setLayoutManager(new LinearLayoutManager(parent.getContext(),
+ LinearLayoutManager.HORIZONTAL, mReverseInner));
+ rv.setRecycledViewPool(mSharedPool);
+ rv.setLayoutParams(new RecyclerView.LayoutParams(200, 100));
+ return new ViewHolder(rv);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ // Tests may rely on bound holders not being shared between inner adapters,
+ // since we force recycle here
+ holder.mRecyclerView.swapAdapter(mAdapters.get(position), true);
+
+ Parcelable savedState = mSavedStates.get(position);
+ if (savedState != null) {
+ holder.mRecyclerView.getLayoutManager().onRestoreInstanceState(savedState);
+ mSavedStates.set(position, null);
+ }
+ }
+
+ @Override
+ public void onViewRecycled(ViewHolder holder) {
+ mSavedStates.set(holder.getAdapterPosition(),
+ holder.mRecyclerView.getLayoutManager().onSaveInstanceState());
+ }
+
+ @Override
+ public int getItemCount() {
+ return OUTER_ITEM_COUNT;
+ }
+ }
+
+ @Test
+ public void nestedPrefetchSimple() {
+ LinearLayoutManager llm = new LinearLayoutManager(getContext());
+ assertEquals(2, llm.getInitialItemPrefetchCount());
+
+ mRecyclerView.setLayoutManager(llm);
+ mRecyclerView.setAdapter(new OuterAdapter());
+
+ layout(200, 200);
+ mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1);
+
+ // prefetch 2 (default)
+ mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+ RecyclerView.ViewHolder holder = CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 2);
+ assertNotNull(holder);
+ assertNotNull(holder.mNestedRecyclerView);
+ CacheUtils.verifyCacheContainsPrefetchedPositions(holder.mNestedRecyclerView, 0, 1);
+
+ // prefetch 4
+ ((LinearLayoutManager) holder.mNestedRecyclerView.getLayoutManager())
+ .setInitialPrefetchItemCount(4);
+ mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+ CacheUtils.verifyCacheContainsPrefetchedPositions(holder.mNestedRecyclerView, 0, 1, 2, 3);
+ }
+
+ @Test
+ public void nestedPrefetchReverseInner() {
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+ mRecyclerView.setAdapter(new OuterAdapter(/* reverseInner = */ true));
+
+ layout(200, 200);
+ mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1);
+
+ mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+ RecyclerView.ViewHolder holder = CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 2);
+
+ // anchor from right side, should see last two positions
+ CacheUtils.verifyCacheContainsPrefetchedPositions(holder.mNestedRecyclerView, 18, 19);
+ }
+
+ @Test
+ public void nestedPrefetchOffset() {
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+ mRecyclerView.setAdapter(new OuterAdapter());
+
+ layout(200, 200);
+
+ // Scroll top row by 5.5 items, verify positions 5, 6, 7 showing
+ RecyclerView inner = (RecyclerView) mRecyclerView.getChildAt(0);
+ inner.scrollBy(550, 0);
+ assertEquals(5, RecyclerView.getChildViewHolderInt(inner.getChildAt(0)).mPosition);
+ assertEquals(6, RecyclerView.getChildViewHolderInt(inner.getChildAt(1)).mPosition);
+ assertEquals(7, RecyclerView.getChildViewHolderInt(inner.getChildAt(2)).mPosition);
+
+ // scroll down 4 rows, up 3 so row 0 is adjacent but uncached
+ mRecyclerView.scrollBy(0, 400);
+ mRecyclerView.scrollBy(0, -300);
+
+ // top row no longer present
+ CacheUtils.verifyCacheDoesNotContainPositions(mRecyclerView, 0);
+
+ // prefetch upward, and validate that we've gotten the top row with correct offsets
+ mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, -1);
+ mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+ inner = (RecyclerView) CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 0).itemView;
+ CacheUtils.verifyCacheContainsPrefetchedPositions(inner, 5, 6);
+
+ // prefetch 4
+ ((LinearLayoutManager) inner.getLayoutManager()).setInitialPrefetchItemCount(4);
+ mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+ CacheUtils.verifyCacheContainsPrefetchedPositions(inner, 5, 6, 7, 8);
+ }
}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
index e861dbb..2c8fe28 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
@@ -97,6 +97,64 @@
}
@Test
+ public void triggerFocusSearchInOnRecycledCallback() throws Throwable {
+ final RecyclerView rv = new RecyclerView(getActivity()) {
+ @Override
+ void consumePendingUpdateOperations() {
+ try {
+ super.consumePendingUpdateOperations();
+ } catch (Throwable t) {
+ postExceptionToInstrumentation(t);
+ }
+ }
+ };
+ final AtomicBoolean receivedOnRecycled = new AtomicBoolean(false);
+ final TestAdapter adapter = new TestAdapter(20) {
+ @Override
+ public void onViewRecycled(TestViewHolder holder) {
+ super.onViewRecycled(holder);
+ if (receivedOnRecycled.getAndSet(true)) {
+ return;
+ }
+ rv.focusSearch(rv.getChildAt(0), View.FOCUS_FORWARD);
+ }
+ };
+ final AtomicInteger layoutCnt = new AtomicInteger(5);
+ TestLayoutManager tlm = new TestLayoutManager() {
+ @Override
+ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+ detachAndScrapAttachedViews(recycler);
+ layoutRange(recycler, 0, layoutCnt.get());
+ layoutLatch.countDown();
+ }
+ };
+ rv.setLayoutManager(tlm);
+ rv.setAdapter(adapter);
+ tlm.expectLayouts(1);
+ setRecyclerView(rv);
+ tlm.waitForLayout(2);
+
+ layoutCnt.set(4);
+ tlm.expectLayouts(1);
+ requestLayoutOnUIThread(rv);
+ tlm.waitForLayout(1);
+
+ assertThat("test sanity", rv.mRecycler.mCachedViews.size(), is(1));
+ tlm.expectLayouts(1);
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ adapter.notifyItemChanged(4);
+ rv.smoothScrollBy(0, 1);
+ }
+ });
+ checkForMainThreadException();
+ tlm.waitForLayout(2);
+ assertThat("test sanity", rv.mRecycler.mCachedViews.size(), is(0));
+ assertThat(receivedOnRecycled.get(), is(true));
+ }
+
+ @Test
public void detachAttachGetReadyWithoutChanges() throws Throwable {
detachAttachGetReady(false, false, false);
}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewPrefetchTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewPrefetchTest.java
index b3f44a8..3001240 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewPrefetchTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewPrefetchTest.java
@@ -57,7 +57,7 @@
}
@Override
- public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+ public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
RecyclerView.PrefetchRegistry prefetchRegistry) {
prefetchLatch.countDown();
prefetchRegistry.addPosition(6, 0);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSnappingTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSnappingTest.java
index 92245e3..828ffab 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSnappingTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSnappingTest.java
@@ -33,6 +33,7 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
+@MediumTest
@RunWith(Parameterized.class)
public class StaggeredGridLayoutManagerSnappingTest extends BaseStaggeredGridLayoutManagerTest {
@@ -56,7 +57,6 @@
return result;
}
- @MediumTest
@Test
public void snapOnScrollSameViewFixedSize() throws Throwable {
// This test is a special case for fixed sized children.
@@ -96,7 +96,6 @@
assertCenterAligned(viewAfterScroll);
}
- @MediumTest
@Test
public void snapOnScrollSameView() throws Throwable {
final Config config = (Config) mConfig.clone();
@@ -144,7 +143,6 @@
assertCenterAligned(viewAfterScroll);
}
- @MediumTest
@Test
public void snapOnFlingSameView() throws Throwable {
final Config config = (Config) mConfig.clone();
@@ -171,8 +169,6 @@
assertCenterAligned(viewAfterFling);
}
-
- @MediumTest
@Test
public void snapOnFlingNextView() throws Throwable {
final Config config = (Config) mConfig.clone();
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java
index abd9129..c251c5a 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java
@@ -29,6 +29,7 @@
import android.os.Build;
import android.support.test.filters.MediumTest;
import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.Suppress;
import android.support.test.runner.AndroidJUnit4;
import android.support.v7.util.PollingCheck;
import android.support.v7.util.TouchUtils;
@@ -119,6 +120,8 @@
basicSwipeTest(END, START | END, getActivity().getWindow().getDecorView().getWidth());
}
+ // Test is disabled as it is flaky.
+ @Suppress
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN_MR1)
@Test
public void swipeStartInRTL() throws Throwable {