Merge "Fix up small issues in support-transition."
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index b24c09a..3e9e37b 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -5,4 +5,4 @@
<mapping directory="$PROJECT_DIR$/../../external/doclava" vcs="Git" />
<mapping directory="$PROJECT_DIR$/../../external/jdiff" vcs="Git" />
</component>
-</project>
\ No newline at end of file
+</project>
diff --git a/api/26.0.0.txt b/api/26.0.0.txt
index b58bf3c..5b98882 100644
--- a/api/26.0.0.txt
+++ b/api/26.0.0.txt
@@ -1317,6 +1317,7 @@
method public int getTransportStreamId();
method public java.lang.String getType();
method public java.lang.String getVideoFormat();
+ method public boolean isBrowsable();
method public boolean isSearchable();
method public android.content.ContentValues toContentValues();
}
@@ -1351,6 +1352,13 @@
method public android.support.media.tv.Channel.Builder setVideoFormat(java.lang.String);
}
+ public class ChannelLogoUtils {
+ ctor public ChannelLogoUtils();
+ method public static android.graphics.Bitmap loadChannelLogo(android.content.Context, long);
+ method public static boolean storeChannelLogo(android.content.Context, long, android.net.Uri);
+ method public static boolean storeChannelLogo(android.content.Context, long, android.graphics.Bitmap);
+ }
+
public final class PreviewProgram extends android.support.media.tv.BasePreviewProgram {
method public static android.support.media.tv.PreviewProgram fromCursor(android.database.Cursor);
method public long getChannelId();
@@ -1879,6 +1887,24 @@
method public void setResizeClip(boolean);
}
+ public class ChangeClipBounds extends android.support.transition.Transition {
+ ctor public ChangeClipBounds();
+ ctor public ChangeClipBounds(android.content.Context, android.util.AttributeSet);
+ method public void captureEndValues(android.support.transition.TransitionValues);
+ method public void captureStartValues(android.support.transition.TransitionValues);
+ }
+
+ public class CircularPropagation extends android.support.transition.VisibilityPropagation {
+ ctor public CircularPropagation();
+ method public long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+ method public void setPropagationSpeed(float);
+ }
+
+ public class Explode extends android.support.transition.Visibility {
+ ctor public Explode();
+ ctor public Explode(android.content.Context, android.util.AttributeSet);
+ }
+
public class Fade extends android.support.transition.Visibility {
ctor public Fade(int);
ctor public Fade();
@@ -1931,9 +1957,12 @@
method public android.support.transition.Transition excludeTarget(java.lang.String, boolean);
method public android.support.transition.Transition excludeTarget(java.lang.Class, boolean);
method public long getDuration();
+ method public android.graphics.Rect getEpicenter();
+ method public android.support.transition.Transition.EpicenterCallback getEpicenterCallback();
method public android.animation.TimeInterpolator getInterpolator();
method public java.lang.String getName();
method public android.support.transition.PathMotion getPathMotion();
+ method public android.support.transition.TransitionPropagation getPropagation();
method public long getStartDelay();
method public java.util.List<java.lang.Integer> getTargetIds();
method public java.util.List<java.lang.String> getTargetNames();
@@ -1947,9 +1976,11 @@
method public android.support.transition.Transition removeTarget(java.lang.String);
method public android.support.transition.Transition removeTarget(java.lang.Class);
method public android.support.transition.Transition setDuration(long);
+ method public void setEpicenterCallback(android.support.transition.Transition.EpicenterCallback);
method public android.support.transition.Transition setInterpolator(android.animation.TimeInterpolator);
method public void setMatchOrder(int...);
method public void setPathMotion(android.support.transition.PathMotion);
+ method public void setPropagation(android.support.transition.TransitionPropagation);
method public android.support.transition.Transition setStartDelay(long);
field public static final int MATCH_ID = 3; // 0x3
field public static final int MATCH_INSTANCE = 1; // 0x1
@@ -1957,6 +1988,11 @@
field public static final int MATCH_NAME = 2; // 0x2
}
+ public static abstract class Transition.EpicenterCallback {
+ ctor public Transition.EpicenterCallback();
+ method public abstract android.graphics.Rect onGetEpicenter(android.support.transition.Transition);
+ }
+
public static abstract interface Transition.TransitionListener {
method public abstract void onTransitionCancel(android.support.transition.Transition);
method public abstract void onTransitionEnd(android.support.transition.Transition);
@@ -1982,6 +2018,13 @@
method public void transitionTo(android.support.transition.Scene);
}
+ public abstract class TransitionPropagation {
+ ctor public TransitionPropagation();
+ method public abstract void captureValues(android.support.transition.TransitionValues);
+ method public abstract java.lang.String[] getPropagationProperties();
+ method public abstract long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+ }
+
public class TransitionSet extends android.support.transition.Transition {
ctor public TransitionSet();
ctor public TransitionSet(android.content.Context, android.util.AttributeSet);
@@ -2019,6 +2062,15 @@
field public static final int MODE_OUT = 2; // 0x2
}
+ public abstract class VisibilityPropagation extends android.support.transition.TransitionPropagation {
+ ctor public VisibilityPropagation();
+ method public void captureValues(android.support.transition.TransitionValues);
+ method public java.lang.String[] getPropagationProperties();
+ method public int getViewVisibility(android.support.transition.TransitionValues);
+ method public int getViewX(android.support.transition.TransitionValues);
+ method public int getViewY(android.support.transition.TransitionValues);
+ }
+
}
package android.support.v13.app {
@@ -6927,7 +6979,6 @@
method public java.lang.Object getRemoteControlClient();
method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
method public boolean isActive();
- method public static deprecated android.support.v4.media.session.MediaSessionCompat obtain(android.content.Context, java.lang.Object);
method public void release();
method public void removeOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
method public void sendSessionEvent(java.lang.String, android.os.Bundle);
@@ -6998,7 +7049,6 @@
method public android.support.v4.media.MediaDescriptionCompat getDescription();
method public long getQueueId();
method public java.lang.Object getQueueItem();
- method public static deprecated android.support.v4.media.session.MediaSessionCompat.QueueItem obtain(java.lang.Object);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.QueueItem> CREATOR;
field public static final int UNKNOWN_ID = -1; // 0xffffffff
@@ -7503,7 +7553,7 @@
public class SimpleArrayMap<K, V> {
ctor public SimpleArrayMap();
ctor public SimpleArrayMap(int);
- ctor public SimpleArrayMap(android.support.v4.util.SimpleArrayMap);
+ ctor public SimpleArrayMap(android.support.v4.util.SimpleArrayMap<K, V>);
method public void clear();
method public boolean containsKey(java.lang.Object);
method public boolean containsValue(java.lang.Object);
diff --git a/api/current.txt b/api/current.txt
index e2873c4..8757fb5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1352,6 +1352,13 @@
method public android.support.media.tv.Channel.Builder setVideoFormat(java.lang.String);
}
+ public class ChannelLogoUtils {
+ ctor public ChannelLogoUtils();
+ method public static android.graphics.Bitmap loadChannelLogo(android.content.Context, long);
+ method public static boolean storeChannelLogo(android.content.Context, long, android.net.Uri);
+ method public static boolean storeChannelLogo(android.content.Context, long, android.graphics.Bitmap);
+ }
+
public final class PreviewProgram extends android.support.media.tv.BasePreviewProgram {
method public static android.support.media.tv.PreviewProgram fromCursor(android.database.Cursor);
method public long getChannelId();
@@ -1671,13 +1678,13 @@
package android.support.percent {
- public class PercentFrameLayout extends android.widget.FrameLayout {
+ public deprecated class PercentFrameLayout extends android.widget.FrameLayout {
ctor public PercentFrameLayout(android.content.Context);
ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet);
ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet, int);
}
- public static class PercentFrameLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+ public static deprecated class PercentFrameLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
ctor public PercentFrameLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
ctor public PercentFrameLayout.LayoutParams(int, int);
ctor public PercentFrameLayout.LayoutParams(int, int, int);
@@ -1688,7 +1695,7 @@
method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
}
- public class PercentLayoutHelper {
+ public deprecated class PercentLayoutHelper {
ctor public PercentLayoutHelper(android.view.ViewGroup);
method public void adjustChildren(int, int);
method public static void fetchWidthAndHeight(android.view.ViewGroup.LayoutParams, android.content.res.TypedArray, int, int);
@@ -1697,7 +1704,7 @@
method public void restoreOriginalParams();
}
- public static class PercentLayoutHelper.PercentLayoutInfo {
+ public static deprecated class PercentLayoutHelper.PercentLayoutInfo {
ctor public PercentLayoutHelper.PercentLayoutInfo();
method public void fillLayoutParams(android.view.ViewGroup.LayoutParams, int, int);
method public deprecated void fillMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams, int, int);
@@ -1715,17 +1722,17 @@
field public float widthPercent;
}
- public static abstract interface PercentLayoutHelper.PercentLayoutParams {
+ public static abstract deprecated interface PercentLayoutHelper.PercentLayoutParams {
method public abstract android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
}
- public class PercentRelativeLayout extends android.widget.RelativeLayout {
+ public deprecated class PercentRelativeLayout extends android.widget.RelativeLayout {
ctor public PercentRelativeLayout(android.content.Context);
ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet);
ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet, int);
}
- public static class PercentRelativeLayout.LayoutParams extends android.widget.RelativeLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+ public static deprecated class PercentRelativeLayout.LayoutParams extends android.widget.RelativeLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
ctor public PercentRelativeLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
ctor public PercentRelativeLayout.LayoutParams(int, int);
ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
@@ -1880,6 +1887,24 @@
method public void setResizeClip(boolean);
}
+ public class ChangeClipBounds extends android.support.transition.Transition {
+ ctor public ChangeClipBounds();
+ ctor public ChangeClipBounds(android.content.Context, android.util.AttributeSet);
+ method public void captureEndValues(android.support.transition.TransitionValues);
+ method public void captureStartValues(android.support.transition.TransitionValues);
+ }
+
+ public class CircularPropagation extends android.support.transition.VisibilityPropagation {
+ ctor public CircularPropagation();
+ method public long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+ method public void setPropagationSpeed(float);
+ }
+
+ public class Explode extends android.support.transition.Visibility {
+ ctor public Explode();
+ ctor public Explode(android.content.Context, android.util.AttributeSet);
+ }
+
public class Fade extends android.support.transition.Visibility {
ctor public Fade(int);
ctor public Fade();
@@ -1932,9 +1957,12 @@
method public android.support.transition.Transition excludeTarget(java.lang.String, boolean);
method public android.support.transition.Transition excludeTarget(java.lang.Class, boolean);
method public long getDuration();
+ method public android.graphics.Rect getEpicenter();
+ method public android.support.transition.Transition.EpicenterCallback getEpicenterCallback();
method public android.animation.TimeInterpolator getInterpolator();
method public java.lang.String getName();
method public android.support.transition.PathMotion getPathMotion();
+ method public android.support.transition.TransitionPropagation getPropagation();
method public long getStartDelay();
method public java.util.List<java.lang.Integer> getTargetIds();
method public java.util.List<java.lang.String> getTargetNames();
@@ -1948,9 +1976,11 @@
method public android.support.transition.Transition removeTarget(java.lang.String);
method public android.support.transition.Transition removeTarget(java.lang.Class);
method public android.support.transition.Transition setDuration(long);
+ method public void setEpicenterCallback(android.support.transition.Transition.EpicenterCallback);
method public android.support.transition.Transition setInterpolator(android.animation.TimeInterpolator);
method public void setMatchOrder(int...);
method public void setPathMotion(android.support.transition.PathMotion);
+ method public void setPropagation(android.support.transition.TransitionPropagation);
method public android.support.transition.Transition setStartDelay(long);
field public static final int MATCH_ID = 3; // 0x3
field public static final int MATCH_INSTANCE = 1; // 0x1
@@ -1958,6 +1988,11 @@
field public static final int MATCH_NAME = 2; // 0x2
}
+ public static abstract class Transition.EpicenterCallback {
+ ctor public Transition.EpicenterCallback();
+ method public abstract android.graphics.Rect onGetEpicenter(android.support.transition.Transition);
+ }
+
public static abstract interface Transition.TransitionListener {
method public abstract void onTransitionCancel(android.support.transition.Transition);
method public abstract void onTransitionEnd(android.support.transition.Transition);
@@ -1983,6 +2018,13 @@
method public void transitionTo(android.support.transition.Scene);
}
+ public abstract class TransitionPropagation {
+ ctor public TransitionPropagation();
+ method public abstract void captureValues(android.support.transition.TransitionValues);
+ method public abstract java.lang.String[] getPropagationProperties();
+ method public abstract long getStartDelay(android.view.ViewGroup, android.support.transition.Transition, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+ }
+
public class TransitionSet extends android.support.transition.Transition {
ctor public TransitionSet();
ctor public TransitionSet(android.content.Context, android.util.AttributeSet);
@@ -2020,6 +2062,15 @@
field public static final int MODE_OUT = 2; // 0x2
}
+ public abstract class VisibilityPropagation extends android.support.transition.TransitionPropagation {
+ ctor public VisibilityPropagation();
+ method public void captureValues(android.support.transition.TransitionValues);
+ method public java.lang.String[] getPropagationProperties();
+ method public int getViewVisibility(android.support.transition.TransitionValues);
+ method public int getViewX(android.support.transition.TransitionValues);
+ method public int getViewY(android.support.transition.TransitionValues);
+ }
+
}
package android.support.v13.app {
@@ -2814,6 +2865,9 @@
method protected abstract int getPageCount();
method protected abstract java.lang.CharSequence getPageDescription(int);
method protected abstract java.lang.CharSequence getPageTitle(int);
+ method protected final boolean isLogoAnimationFinished();
+ method protected void moveToNextPage();
+ method protected void moveToPreviousPage();
method protected abstract android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup);
method protected abstract android.view.View onCreateContentView(android.view.LayoutInflater, android.view.ViewGroup);
method protected android.animation.Animator onCreateDescriptionAnimator();
@@ -2822,6 +2876,7 @@
method protected android.animation.Animator onCreateLogoAnimation();
method protected android.animation.Animator onCreateTitleAnimator();
method protected void onFinishFragment();
+ method protected void onLogoAnimationFinished();
method protected void onPageChanged(int, int);
method public int onProvideTheme();
method public final void setIconResouceId(int);
@@ -2836,6 +2891,9 @@
method protected abstract int getPageCount();
method protected abstract java.lang.CharSequence getPageDescription(int);
method protected abstract java.lang.CharSequence getPageTitle(int);
+ method protected final boolean isLogoAnimationFinished();
+ method protected void moveToNextPage();
+ method protected void moveToPreviousPage();
method protected abstract android.view.View onCreateBackgroundView(android.view.LayoutInflater, android.view.ViewGroup);
method protected abstract android.view.View onCreateContentView(android.view.LayoutInflater, android.view.ViewGroup);
method protected android.animation.Animator onCreateDescriptionAnimator();
@@ -2844,6 +2902,7 @@
method protected android.animation.Animator onCreateLogoAnimation();
method protected android.animation.Animator onCreateTitleAnimator();
method protected void onFinishFragment();
+ method protected void onLogoAnimationFinished();
method protected void onPageChanged(int, int);
method public int onProvideTheme();
method public final void setIconResouceId(int);
@@ -5883,6 +5942,7 @@
ctor public NotificationCompat.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
method public java.lang.String getDataMimeType();
method public android.net.Uri getDataUri();
+ method public android.os.Bundle getExtras();
method public java.lang.CharSequence getSender();
method public java.lang.CharSequence getText();
method public long getTimestamp();
@@ -6928,7 +6988,6 @@
method public java.lang.Object getRemoteControlClient();
method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
method public boolean isActive();
- method public static deprecated android.support.v4.media.session.MediaSessionCompat obtain(android.content.Context, java.lang.Object);
method public void release();
method public void removeOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener);
method public void sendSessionEvent(java.lang.String, android.os.Bundle);
@@ -6999,7 +7058,6 @@
method public android.support.v4.media.MediaDescriptionCompat getDescription();
method public long getQueueId();
method public java.lang.Object getQueueItem();
- method public static deprecated android.support.v4.media.session.MediaSessionCompat.QueueItem obtain(java.lang.Object);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.QueueItem> CREATOR;
field public static final int UNKNOWN_ID = -1; // 0xffffffff
@@ -7504,7 +7562,7 @@
public class SimpleArrayMap<K, V> {
ctor public SimpleArrayMap();
ctor public SimpleArrayMap(int);
- ctor public SimpleArrayMap(android.support.v4.util.SimpleArrayMap);
+ ctor public SimpleArrayMap(android.support.v4.util.SimpleArrayMap<K, V>);
method public void clear();
method public boolean containsKey(java.lang.Object);
method public boolean containsValue(java.lang.Object);
diff --git a/buildSrc/diff_and_docs.gradle b/buildSrc/diff_and_docs.gradle
index 9ad419d..2034c15 100644
--- a/buildSrc/diff_and_docs.gradle
+++ b/buildSrc/diff_and_docs.gradle
@@ -25,10 +25,51 @@
import groovy.io.FileType
+// Set up platform API files for federation.
+if (project.androidApiTxt != null) {
+ task generateSdkApi(type: Copy) {
+ description = 'Copies the API files for the current SDK.'
+
+ // Export the API files so this looks like a DoclavaTask.
+ ext.apiFile = new File(project.docsDir, 'release/sdk_current.txt')
+ ext.removedApiFile = new File(project.docsDir, 'release/sdk_removed.txt')
+
+ from project.androidApiTxt.absolutePath
+ into apiFile.parent
+ rename { apiFile.name }
+
+ // Register the fake removed file as an output.
+ outputs.file removedApiFile
+
+ doLast {
+ removedApiFile.createNewFile()
+ }
+ }
+} else {
+ task generateSdkApi(type: DoclavaTask, dependsOn: [configurations.doclava]) {
+ description = 'Generates API files for the current SDK.'
+
+ docletpath = configurations.doclava.resolve()
+ destinationDir = project.docsDir
+
+ classpath = project.androidJar
+ source zipTree(project.androidSrcJar)
+
+ apiFile = new File(project.docsDir, 'release/sdk_current.txt')
+ removedApiFile = new File(project.docsDir, 'release/sdk_removed.txt')
+ generateDocs = false
+
+ options {
+ addStringOption "stubpackages", "android.*"
+ }
+ }
+}
+
// Generates online docs.
-task generateDocs(type: DoclavaTask, dependsOn: configurations.doclava) {
+task generateDocs(type: DoclavaTask, dependsOn: [configurations.doclava, generateSdkApi]) {
group = JavaBasePlugin.DOCUMENTATION_GROUP
- description = 'Generates d.android.com style documentation.'
+ description = 'Generates d.android.com-style documentation.'
+
docletpath = configurations.doclava.resolve()
destinationDir = new File(project.docsDir, "online")
@@ -40,6 +81,12 @@
['android.whichdoc', 'online'],
['android.hasSamples', 'true'])
+ def federateOption = new DoclavaMultilineJavadocOptionFileOption('federate')
+ federateOption.add(['Android', 'https://developer.android.com'])
+
+ def federationapiOption = new DoclavaMultilineJavadocOptionFileOption('federationapi')
+ federationapiOption.add(['Android', generateSdkApi.apiFile.absolutePath])
+
// Track API change history.
def apiFilePattern = /(\d+\.\d+\.\d).txt/
def sinceOption = new DoclavaMultilineJavadocOptionFileOption('since')
@@ -60,9 +107,10 @@
options {
addStringOption "templatedir",
"${supportRootFolder}/../../external/doclava/res/assets/templates-sdk"
- addStringOption "federate Android", "http://developer.android.com"
addStringOption "stubpackages", "android.support.*"
addStringOption "samplesdir", "${supportRootFolder}/samples"
+ addOption federateOption
+ addOption federationapiOption
addOption hdfOption
addOption sinceOption
}
@@ -70,21 +118,18 @@
exclude '**/BuildConfig.java'
}
-JDiffTask createApiDiffsTask(String taskName, File oldApiXml, File newApiXml, File outDir,
- Configuration jdiff, Task... dependencies) {
- return tasks.create(name: taskName, type: JDiffTask.class) {
- dependsOn jdiff
- dependsOn dependencies
+// Generates a distribution artifact for online docs.
+task distDocs(type: Zip, dependsOn: generateDocs) {
+ group = JavaBasePlugin.DOCUMENTATION_GROUP
+ description = 'Generates distribution artifact for d.android.com-style documentation.'
- docletpath = jdiff.resolve()
+ from generateDocs.destinationDir
+ destinationDir project.distDir
+ baseName = "android-support-docs"
+ version = project.buildNumber
- oldApiXmlFile oldApiXml
- newApiXmlFile newApiXml
- destinationDir = outDir
-
- // This prefix is based on the assumption that the output diffs will
- // ultimately land in frameworks/base/docs/html/sdk/support_api_diff/.
- newJavadocPrefix = "../reference/"
+ doLast {
+ logger.lifecycle('Wrote API reference to ${archivePath}')
}
}
@@ -103,7 +148,6 @@
options {
addStringOption "templatedir",
"${supportRootFolder}/../../external/doclava/res/assets/templates-sdk"
- addStringOption "federate Android", "http://developer.android.com"
addStringOption "stubpackages", "android.support.*"
}
exclude '**/BuildConfig.java'
diff --git a/buildSrc/init.gradle b/buildSrc/init.gradle
index 25281df..7337c53 100644
--- a/buildSrc/init.gradle
+++ b/buildSrc/init.gradle
@@ -80,8 +80,11 @@
final String fullSdkPath = "${init.prebuiltsRoot}/fullsdk-${platform}"
if (file(fullSdkPath).exists()) {
gradle.ext.currentSdk = 26
- project.ext.androidJar = files("${fullSdkPath}/platforms/android-${gradle.ext.currentSdk}" +
- "/android.jar")
+ project.ext.androidJar =
+ files("${fullSdkPath}/platforms/android-${gradle.currentSdk}/android.jar")
+ project.ext.androidSrcJar =
+ file("${fullSdkPath}/platforms/android-${gradle.currentSdk}/android-stubs-src.jar")
+ project.ext.androidApiTxt = null
System.setProperty('android.home', "${init.prebuiltsRoot}/fullsdk-${platform}")
File props = file("local.properties")
props.write "sdk.dir=${fullSdkPath}"
@@ -89,6 +92,8 @@
} else {
gradle.ext.currentSdk = 'current'
project.ext.androidJar = files("${init.prebuiltsRoot}/sdk/current/android.jar")
+ project.ext.androidSrcJar = null
+ project.ext.androidApiTxt = file("${init.prebuiltsRoot}/sdk/api/26.txt")
File props = file("local.properties")
props.write "android.dir=../../"
ext.usingFullSdk = false
diff --git a/buildSrc/src/main/groovy/android/support/SupportLibraryExtension.groovy b/buildSrc/src/main/groovy/android/support/SupportLibraryExtension.groovy
index b3b5783..f9a2581 100644
--- a/buildSrc/src/main/groovy/android/support/SupportLibraryExtension.groovy
+++ b/buildSrc/src/main/groovy/android/support/SupportLibraryExtension.groovy
@@ -16,11 +16,38 @@
package android.support;
+import org.gradle.api.Project
+
/**
* Extension for {@link SupportLibraryPlugin}.
*/
class SupportLibraryExtension {
+ Project project
String name;
String description;
String inceptionYear;
-}
+ Collection<License> licenses = [];
+
+ SupportLibraryExtension(Project project) {
+ this.project = project
+ }
+
+ License license(Closure closure) {
+ def license = project.configure(new License(), closure)
+ licenses.add(license)
+ return license
+ }
+
+ class License {
+ String name;
+ String url;
+
+ void url(String p) {
+ url = p
+ }
+
+ void name(String p) {
+ name = p
+ }
+ }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy b/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy
index 4c9f797..87a5abf 100644
--- a/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy
+++ b/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy
@@ -42,7 +42,7 @@
@Override
public void apply(Project project) {
SupportLibraryExtension supportLibraryExtension =
- project.getExtensions().create("supportLibrary", SupportLibraryExtension);
+ project.getExtensions().create("supportLibrary", SupportLibraryExtension, project);
project.apply(ImmutableMap.of("plugin", "com.android.library"));
project.apply(ImmutableMap.of("plugin", ErrorProneBasePlugin.class));
@@ -142,6 +142,15 @@
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
distribution 'repo'
}
+
+ supportLibraryExtension.getLicenses().each {
+ SupportLibraryExtension.License supportLicense ->
+ license {
+ name supportLicense.name
+ url supportLicense.url
+ distribution 'repo'
+ }
+ }
}
scm {
diff --git a/buildSrc/src/main/groovy/android/support/doclava/DoclavaJavadocOptionFileOption.java b/buildSrc/src/main/groovy/android/support/doclava/DoclavaJavadocOptionFileOption.java
index 4bd67a3..db3f318 100644
--- a/buildSrc/src/main/groovy/android/support/doclava/DoclavaJavadocOptionFileOption.java
+++ b/buildSrc/src/main/groovy/android/support/doclava/DoclavaJavadocOptionFileOption.java
@@ -61,9 +61,14 @@
*/
public DoclavaJavadocOptionFileOption duplicate() {
final Iterable<String> value = getValue();
- final ArrayList<String> valueCopy = new ArrayList<>();
- for (String item : value) {
- valueCopy.add(item);
+ final ArrayList<String> valueCopy;
+ if (value != null) {
+ valueCopy = new ArrayList<>();
+ for (String item : value) {
+ valueCopy.add(item);
+ }
+ } else {
+ valueCopy = null;
}
return new DoclavaJavadocOptionFileOption(getOption(), valueCopy);
}
diff --git a/buildSrc/versions.gradle b/buildSrc/versions.gradle
index 33cd0a0..8152a02 100644
--- a/buildSrc/versions.gradle
+++ b/buildSrc/versions.gradle
@@ -17,10 +17,10 @@
ext['android.builder.sdkDownload'] = false
// Version code of the support library components.
-ext.supportVersion = "26.0.0-SNAPSHOT"
+ext.supportVersion = "26.0.0-alpha1"
// This number gets incremented for each public release.
-ext.extraVersion = 44
+ext.extraVersion = 46
// Enforce the use of prebuilt dependencies in all sub-projects. This is
// required for the doclava dependency.
diff --git a/compat/AndroidManifest.xml b/compat/AndroidManifest.xml
index 6a9f6a6..09383ee 100644
--- a/compat/AndroidManifest.xml
+++ b/compat/AndroidManifest.xml
@@ -17,6 +17,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="android.support.compat">
<uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.compat"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/compat/ics/android/support/v4/app/ShareCompatICS.java b/compat/ics/android/support/v4/app/ShareCompatICS.java
deleted file mode 100644
index f635e73..0000000
--- a/compat/ics/android/support/v4/app/ShareCompatICS.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.app;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.support.annotation.RequiresApi;
-import android.view.ActionProvider;
-import android.view.MenuItem;
-import android.widget.ShareActionProvider;
-
-@RequiresApi(14)
-class ShareCompatICS {
- private static final String HISTORY_FILENAME_PREFIX = ".sharecompat_";
-
- public static void configureMenuItem(MenuItem item, Activity callingActivity, Intent intent) {
- ActionProvider itemProvider = item.getActionProvider();
- ShareActionProvider provider = null;
- if (!(itemProvider instanceof ShareActionProvider)) {
- provider = new ShareActionProvider(callingActivity);
- } else {
- provider = (ShareActionProvider) itemProvider;
- }
- provider.setShareHistoryFileName(HISTORY_FILENAME_PREFIX +
- callingActivity.getClass().getName());
- provider.setShareIntent(intent);
- item.setActionProvider(provider);
- }
-}
diff --git a/compat/java/android/support/v4/app/ActivityCompat.java b/compat/java/android/support/v4/app/ActivityCompat.java
index 303c4c3..e5c057f 100644
--- a/compat/java/android/support/v4/app/ActivityCompat.java
+++ b/compat/java/android/support/v4/app/ActivityCompat.java
@@ -74,7 +74,7 @@
/**
* This class should not be instantiated, but the constructor must be
- * visible for the class to be extended (ex. in support-v13).
+ * visible for the class to be extended (as in support-v13).
*/
protected ActivityCompat() {
// Not publicly instantiable, but may be extended.
diff --git a/compat/java/android/support/v4/app/NotificationCompat.java b/compat/java/android/support/v4/app/NotificationCompat.java
index 5d43871..f8edf58 100644
--- a/compat/java/android/support/v4/app/NotificationCompat.java
+++ b/compat/java/android/support/v4/app/NotificationCompat.java
@@ -20,7 +20,6 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
-import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
@@ -2298,11 +2297,13 @@
static final String KEY_SENDER = "sender";
static final String KEY_DATA_MIME_TYPE = "type";
static final String KEY_DATA_URI= "uri";
+ static final String KEY_EXTRAS_BUNDLE = "extras";
private final CharSequence mText;
private final long mTimestamp;
private final CharSequence mSender;
+ private Bundle mExtras = new Bundle();
private String mDataMimeType;
private Uri mDataUri;
@@ -2371,6 +2372,13 @@
}
/**
+ * Get the extras Bundle for this message.
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
* Get the text used to display the contact's name in the messaging experience
*/
public CharSequence getSender() {
@@ -2407,6 +2415,9 @@
if (mDataUri != null) {
bundle.putParcelable(KEY_DATA_URI, mDataUri);
}
+ if (mExtras != null) {
+ bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
+ }
return bundle;
}
@@ -2441,10 +2452,12 @@
bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
bundle.containsKey(KEY_DATA_URI)) {
-
message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
(Uri) bundle.getParcelable(KEY_DATA_URI));
}
+ if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
+ message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
+ }
return message;
}
} catch (ClassCastException e) {
diff --git a/compat/java/android/support/v4/app/ShareCompat.java b/compat/java/android/support/v4/app/ShareCompat.java
index 1f1c776..b0c6e62 100644
--- a/compat/java/android/support/v4/app/ShareCompat.java
+++ b/compat/java/android/support/v4/app/ShareCompat.java
@@ -16,7 +16,8 @@
package android.support.v4.app;
-import android.support.annotation.RequiresApi;
+import static android.os.Build.VERSION.SDK_INT;
+
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
@@ -24,14 +25,15 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.Build;
import android.support.annotation.StringRes;
import android.support.v4.content.IntentCompat;
import android.text.Html;
import android.text.Spanned;
import android.util.Log;
+import android.view.ActionProvider;
import android.view.Menu;
import android.view.MenuItem;
+import android.widget.ShareActionProvider;
import java.util.ArrayList;
@@ -75,94 +77,7 @@
public static final String EXTRA_CALLING_ACTIVITY =
"android.support.v4.app.EXTRA_CALLING_ACTIVITY";
- /**
- * Compatibility shims for sharing operations
- */
- interface ShareCompatImpl {
- void configureMenuItem(MenuItem item, IntentBuilder shareIntent);
- String escapeHtml(CharSequence text);
- }
-
- static class ShareCompatImplBase implements ShareCompatImpl {
- @Override
- public void configureMenuItem(MenuItem item, IntentBuilder shareIntent) {
- item.setIntent(shareIntent.createChooserIntent());
- }
-
- @Override
- public String escapeHtml(CharSequence text) {
- StringBuilder out = new StringBuilder();
- withinStyle(out, text, 0, text.length());
- return out.toString();
- }
-
- private static void withinStyle(StringBuilder out, CharSequence text,
- int start, int end) {
- for (int i = start; i < end; i++) {
- char c = text.charAt(i);
-
- if (c == '<') {
- out.append("<");
- } else if (c == '>') {
- out.append(">");
- } else if (c == '&') {
- out.append("&");
- } else if (c > 0x7E || c < ' ') {
- out.append("&#" + ((int) c) + ";");
- } else if (c == ' ') {
- while (i + 1 < end && text.charAt(i + 1) == ' ') {
- out.append(" ");
- i++;
- }
-
- out.append(' ');
- } else {
- out.append(c);
- }
- }
- }
- }
-
- @RequiresApi(14)
- static class ShareCompatImplICS extends ShareCompatImplBase {
- @Override
- public void configureMenuItem(MenuItem item, IntentBuilder shareIntent) {
- ShareCompatICS.configureMenuItem(item, shareIntent.getActivity(),
- shareIntent.getIntent());
- if (shouldAddChooserIntent(item)) {
- item.setIntent(shareIntent.createChooserIntent());
- }
- }
-
- boolean shouldAddChooserIntent(MenuItem item) {
- return !item.hasSubMenu();
- }
- }
-
- @RequiresApi(16)
- static class ShareCompatImplJB extends ShareCompatImplICS {
- @Override
- public String escapeHtml(CharSequence html) {
- return ShareCompatJB.escapeHtml(html);
- }
-
- @Override
- boolean shouldAddChooserIntent(MenuItem item) {
- return false;
- }
- }
-
- static ShareCompatImpl IMPL;
-
- static {
- if (Build.VERSION.SDK_INT >= 16) {
- IMPL = new ShareCompatImplJB();
- } else if (Build.VERSION.SDK_INT >= 14) {
- IMPL = new ShareCompatImplICS();
- } else {
- IMPL = new ShareCompatImplBase();
- }
- }
+ private static final String HISTORY_FILENAME_PREFIX = ".sharecompat_";
private ShareCompat() {}
@@ -209,22 +124,18 @@
/**
* Configure a {@link MenuItem} to act as a sharing action.
*
- * <p>If the app is running on API level 14 or higher (Android 4.0/Ice Cream Sandwich)
- * this method will configure a ShareActionProvider to provide a more robust UI
+ * <p>This method will configure a ShareActionProvider to provide a more robust UI
* for selecting the target of the share. History will be tracked for each calling
* activity in a file named with the prefix ".sharecompat_" in the application's
* private data directory. If the application wishes to set this MenuItem to show
* as an action in the Action Bar it should use {@link MenuItem#setShowAsAction(int)} to request
* that behavior in addition to calling this method.</p>
*
- * <p>If the app is running on an older platform version this method will configure
- * a standard activity chooser dialog for the menu item.</p>
- *
* <p>During the calling activity's lifecycle, if data within the share intent must
* change the app should change that state in one of several ways:</p>
* <ul>
- * <li>Call {@link ActivityCompat#invalidateOptionsMenu(Activity)}. If the app is running
- * on API level 11 or above and uses the Action Bar its menu will be recreated and rebuilt.
+ * <li>Call {@link ActivityCompat#invalidateOptionsMenu(Activity)}. If the app uses the
+ * Action Bar its menu will be recreated and rebuilt.
* If not, the activity will receive a call to {@link Activity#onPrepareOptionsMenu(Menu)}
* the next time the user presses the menu key to open the options menu panel. The activity
* can then call configureMenuItem again with a new or altered IntentBuilder to reconfigure
@@ -237,7 +148,23 @@
* @param shareIntent IntentBuilder with data about the content to share
*/
public static void configureMenuItem(MenuItem item, IntentBuilder shareIntent) {
- IMPL.configureMenuItem(item, shareIntent);
+ ActionProvider itemProvider = item.getActionProvider();
+ ShareActionProvider provider;
+ if (!(itemProvider instanceof ShareActionProvider)) {
+ provider = new ShareActionProvider(shareIntent.getActivity());
+ } else {
+ provider = (ShareActionProvider) itemProvider;
+ }
+ provider.setShareHistoryFileName(HISTORY_FILENAME_PREFIX
+ + shareIntent.getActivity().getClass().getName());
+ provider.setShareIntent(shareIntent.getIntent());
+ item.setActionProvider(provider);
+
+ if (SDK_INT < 16) {
+ if (!item.hasSubMenu()) {
+ item.setIntent(shareIntent.createChooserIntent());
+ }
+ }
}
/**
@@ -251,8 +178,8 @@
public static void configureMenuItem(Menu menu, int menuItemId, IntentBuilder shareIntent) {
MenuItem item = menu.findItem(menuItemId);
if (item == null) {
- throw new IllegalArgumentException("Could not find menu item with id " + menuItemId +
- " in the supplied menu");
+ throw new IllegalArgumentException("Could not find menu item with id " + menuItemId
+ + " in the supplied menu");
}
configureMenuItem(item, shareIntent);
}
@@ -760,12 +687,44 @@
if (text instanceof Spanned) {
result = Html.toHtml((Spanned) text);
} else if (text != null) {
- result = IMPL.escapeHtml(text);
+ if (SDK_INT >= 16) {
+ result = Html.escapeHtml(text);
+ } else {
+ StringBuilder out = new StringBuilder();
+ withinStyle(out, text, 0, text.length());
+ result = out.toString();
+ }
}
}
return result;
}
+ private static void withinStyle(StringBuilder out, CharSequence text,
+ int start, int end) {
+ for (int i = start; i < end; i++) {
+ char c = text.charAt(i);
+
+ if (c == '<') {
+ out.append("<");
+ } else if (c == '>') {
+ out.append(">");
+ } else if (c == '&') {
+ out.append("&");
+ } else if (c > 0x7E || c < ' ') {
+ out.append("&#" + ((int) c) + ";");
+ } else if (c == ' ') {
+ while (i + 1 < end && text.charAt(i + 1) == ' ') {
+ out.append(" ");
+ i++;
+ }
+
+ out.append(' ');
+ } else {
+ out.append(c);
+ }
+ }
+ }
+
/**
* Get a URI referring to a data stream shared with the target activity.
*
@@ -800,8 +759,8 @@
if (index == 0) {
return mIntent.getParcelableExtra(Intent.EXTRA_STREAM);
}
- throw new IndexOutOfBoundsException("Stream items available: " + getStreamCount() +
- " index requested: " + index);
+ throw new IndexOutOfBoundsException("Stream items available: " + getStreamCount()
+ + " index requested: " + index);
}
/**
diff --git a/compat/java/android/support/v4/content/ModernAsyncTask.java b/compat/java/android/support/v4/content/ModernAsyncTask.java
index db07ee5..8e2991a 100644
--- a/compat/java/android/support/v4/content/ModernAsyncTask.java
+++ b/compat/java/android/support/v4/content/ModernAsyncTask.java
@@ -178,7 +178,7 @@
Result postResult(Result result) {
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
- new AsyncTaskResult<Result>(this, result));
+ new AsyncTaskResult<>(this, result));
message.sendToTarget();
return result;
}
diff --git a/compat/java/android/support/v4/util/SimpleArrayMap.java b/compat/java/android/support/v4/util/SimpleArrayMap.java
index c7d4b5d..1e520e5 100644
--- a/compat/java/android/support/v4/util/SimpleArrayMap.java
+++ b/compat/java/android/support/v4/util/SimpleArrayMap.java
@@ -227,7 +227,7 @@
/**
* Create a new ArrayMap with the mappings from the given ArrayMap.
*/
- public SimpleArrayMap(SimpleArrayMap map) {
+ public SimpleArrayMap(SimpleArrayMap<K, V> map) {
this();
if (map != null) {
putAll(map);
diff --git a/compat/java/android/support/v4/view/ViewCompat.java b/compat/java/android/support/v4/view/ViewCompat.java
index 54c2730..60f8ba7 100644
--- a/compat/java/android/support/v4/view/ViewCompat.java
+++ b/compat/java/android/support/v4/view/ViewCompat.java
@@ -19,7 +19,6 @@
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.animation.ValueAnimator;
-import android.support.annotation.RequiresApi;
import android.content.ClipData;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -35,6 +34,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.v4.os.BuildCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
@@ -468,11 +468,11 @@
}
public void postInvalidateOnAnimation(View view) {
- view.invalidate();
+ view.postInvalidate();
}
public void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom) {
- view.invalidate(left, top, right, bottom);
+ view.postInvalidate(left, top, right, bottom);
}
public void postOnAnimation(View view, Runnable action) {
diff --git a/compat/tests/java/android/support/v4/app/NotificationCompatTest.java b/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
index 3b953ff..4c7bb0e 100644
--- a/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
+++ b/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
@@ -25,6 +25,7 @@
import android.annotation.TargetApi;
import android.app.Notification;
import android.content.Context;
+import android.os.Bundle;
import android.support.test.filters.SdkSuppress;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -35,6 +36,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -170,6 +173,25 @@
verifyRemoteInputArrayHasSingleResult(a.getDataOnlyRemoteInputs(), DATA_RESULT_KEY);
}
+ @Test
+ public void testMessage_setAndGetExtras() throws Throwable {
+ String extraKey = "extra_key";
+ CharSequence extraValue = "extra_value";
+ NotificationCompat.MessagingStyle.Message m =
+ new NotificationCompat.MessagingStyle.Message("text", 0 /*timestamp */, "sender");
+ m.getExtras().putCharSequence(extraKey, extraValue);
+ assertEquals(extraValue, m.getExtras().getCharSequence(extraKey));
+
+ ArrayList<NotificationCompat.MessagingStyle.Message> messages = new ArrayList<>(1);
+ messages.add(m);
+ Bundle[] bundleArray =
+ NotificationCompat.MessagingStyle.Message.getBundleArrayForMessages(messages);
+ assertEquals(1, bundleArray.length);
+ NotificationCompat.MessagingStyle.Message fromBundle =
+ NotificationCompat.MessagingStyle.Message.getMessageFromBundle(bundleArray[0]);
+ assertEquals(extraValue, fromBundle.getExtras().getCharSequence(extraKey));
+ }
+
private static RemoteInput newDataOnlyRemoteInput() {
return new RemoteInput.Builder(DATA_RESULT_KEY)
.setAllowFreeFormInput(false)
diff --git a/compat/tests/res/values-hdpi/dimens.xml b/compat/tests/res/values-hdpi/dimens.xml
index eb1ff54..3126a6e 100755
--- a/compat/tests/res/values-hdpi/dimens.xml
+++ b/compat/tests/res/values-hdpi/dimens.xml
@@ -13,6 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<dimen name="density_aware_size">14dip</dimen>
</resources>
\ No newline at end of file
diff --git a/compat/tests/res/values-mdpi/dimens.xml b/compat/tests/res/values-mdpi/dimens.xml
index 5766379..ada2cae 100755
--- a/compat/tests/res/values-mdpi/dimens.xml
+++ b/compat/tests/res/values-mdpi/dimens.xml
@@ -13,6 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<dimen name="density_aware_size">12dip</dimen>
</resources>
\ No newline at end of file
diff --git a/compat/tests/res/values-xhdpi/dimens.xml b/compat/tests/res/values-xhdpi/dimens.xml
index a25d23d..21125b8 100755
--- a/compat/tests/res/values-xhdpi/dimens.xml
+++ b/compat/tests/res/values-xhdpi/dimens.xml
@@ -13,6 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<dimen name="density_aware_size">16dip</dimen>
</resources>
\ No newline at end of file
diff --git a/compat/tests/res/values-xxhdpi/dimens.xml b/compat/tests/res/values-xxhdpi/dimens.xml
index 399cde1..aaee862 100755
--- a/compat/tests/res/values-xxhdpi/dimens.xml
+++ b/compat/tests/res/values-xxhdpi/dimens.xml
@@ -13,6 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<dimen name="density_aware_size">18dip</dimen>
</resources>
\ No newline at end of file
diff --git a/compat/tests/res/values/attrs.xml b/compat/tests/res/values/attrs.xml
index 36d1e9e..eb82fb3 100644
--- a/compat/tests/res/values/attrs.xml
+++ b/compat/tests/res/values/attrs.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<attr name="theme_color_default" format="reference" />
<attr name="theme_color_focused" format="reference" />
<attr name="theme_color_pressed" format="reference" />
diff --git a/compat/tests/res/values/colors.xml b/compat/tests/res/values/colors.xml
index 15158cf..0902707 100644
--- a/compat/tests/res/values/colors.xml
+++ b/compat/tests/res/values/colors.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<color name="text_color">#FF8090</color>
<color name="selector_color_default">#70A0C0</color>
diff --git a/compat/tests/res/values/dimens.xml b/compat/tests/res/values/dimens.xml
index 5c82462..5c47040 100644
--- a/compat/tests/res/values/dimens.xml
+++ b/compat/tests/res/values/dimens.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<dimen name="text_medium_size">20sp</dimen>
<dimen name="drawable_small_size">12dip</dimen>
diff --git a/compat/tests/res/values/strings.xml b/compat/tests/res/values/strings.xml
index 4aebfae..d43d30e 100644
--- a/compat/tests/res/values/strings.xml
+++ b/compat/tests/res/values/strings.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<!-- Short text for testing. -->
<string name="test_text_short">Lorem ipsum</string>
<!-- Medium text for testing. -->
diff --git a/compat/tests/res/values/styles.xml b/compat/tests/res/values/styles.xml
index 447d5ec..b74bacc 100644
--- a/compat/tests/res/values/styles.xml
+++ b/compat/tests/res/values/styles.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<style name="TestActivityTheme">
<item name="android:windowAnimationStyle">@null</item>
</style>
diff --git a/core-ui/AndroidManifest.xml b/core-ui/AndroidManifest.xml
index 61eba66..b952b5e 100644
--- a/core-ui/AndroidManifest.xml
+++ b/core-ui/AndroidManifest.xml
@@ -17,6 +17,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="android.support.coreui">
<uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.coreui"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/core-ui/tests/res/values/colors.xml b/core-ui/tests/res/values/colors.xml
index d0d5309..3c7bf7c 100644
--- a/core-ui/tests/res/values/colors.xml
+++ b/core-ui/tests/res/values/colors.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<color name="text_color">#FF8090</color>
<color name="test_red">#FF6030</color>
diff --git a/core-ui/tests/res/values/dimens.xml b/core-ui/tests/res/values/dimens.xml
index c3617a9..d473645 100644
--- a/core-ui/tests/res/values/dimens.xml
+++ b/core-ui/tests/res/values/dimens.xml
@@ -13,6 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<dimen name="text_medium_size">20sp</dimen>
</resources>
\ No newline at end of file
diff --git a/core-ui/tests/res/values/strings.xml b/core-ui/tests/res/values/strings.xml
index 2fa1430..e605c2c 100644
--- a/core-ui/tests/res/values/strings.xml
+++ b/core-ui/tests/res/values/strings.xml
@@ -13,6 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<string name="hello">Hello World</string>
</resources>
\ No newline at end of file
diff --git a/core-ui/tests/res/values/styles.xml b/core-ui/tests/res/values/styles.xml
index e530e60..047e2d0 100644
--- a/core-ui/tests/res/values/styles.xml
+++ b/core-ui/tests/res/values/styles.xml
@@ -17,9 +17,4 @@
<style name="TestActivityTheme" parent="android:Theme.Holo">
<item name="android:windowAnimationStyle">@null</item>
</style>
- <style name="TextMediumStyle" parent="@android:style/TextAppearance.Medium">
- <item name="android:textSize">@dimen/text_medium_size</item>
- <item name="android:textColor">@color/text_color</item>
- <item name="android:textStyle">italic</item>
- </style>
</resources>
\ No newline at end of file
diff --git a/core-utils/AndroidManifest.xml b/core-utils/AndroidManifest.xml
index b3b404b..5c10a5b 100644
--- a/core-utils/AndroidManifest.xml
+++ b/core-utils/AndroidManifest.xml
@@ -17,6 +17,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="android.support.coreutils">
<uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.coreutils"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/customtabs/AndroidManifest.xml b/customtabs/AndroidManifest.xml
index 19a1d54..4069dae 100644
--- a/customtabs/AndroidManifest.xml
+++ b/customtabs/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.customtabs">
<uses-sdk android:minSdkVersion="15"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/design/AndroidManifest.xml b/design/AndroidManifest.xml
index e4449de..afae16e 100644
--- a/design/AndroidManifest.xml
+++ b/design/AndroidManifest.xml
@@ -18,6 +18,7 @@
package="android.support.design">
<uses-sdk android:minSdkVersion="14"
tools:overrideLibrary="android.support.transition"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/design/src/.readme b/design/src/.readme
deleted file mode 100644
index 4bcebad..0000000
--- a/design/src/.readme
+++ /dev/null
@@ -1,2 +0,0 @@
-This hidden file is there to ensure there is an src folder.
-Once we support binary library this will go away.
\ No newline at end of file
diff --git a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
index af88e9d..a9fa46a 100644
--- a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
+++ b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
@@ -21,9 +21,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
-import android.os.SystemClock;
import android.support.design.test.R;
import android.support.test.filters.LargeTest;
+import android.support.testutils.PollingCheck;
import android.widget.ImageView;
import org.junit.Test;
@@ -387,8 +387,13 @@
// this test needs to be tied to the internal implementation details of running animation
// that scales the FAB to 0/0 scales and interpolates its alpha to 0. Since that animation
// starts running partway through our swipe gesture and may complete a bit later then
- // the swipe gesture, sleep for a bit to catch the "final" state of the FAB.
- SystemClock.sleep(200);
+ // the swipe gesture, poll to catch the "final" state of the FAB.
+ PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+ @Override
+ public boolean canProceed() {
+ return fab.getScaleX() == 0.0f;
+ }
+ });
// At this point the FAB should be scaled to 0/0 and set at alpha 0. Since the relevant
// getter methods are only available on v11+, wrap the asserts with build version check.
@@ -403,9 +408,13 @@
originalAppbarBottom,
longSwipeAmount);
- // Same as for swipe-up gesture - sleep for a bit to catch the "final" visible state of
- // the FAB.
- SystemClock.sleep(200);
+ // Same as for swipe-up gesture.
+ PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+ @Override
+ public boolean canProceed() {
+ return fab.getScaleX() == 1.0f;
+ }
+ });
// At this point the FAB should be scaled back to its original size and be at full opacity.
assertEquals(1.0f, fab.getScaleX(), 0.0f);
diff --git a/design/tests/src/android/support/design/widget/AppBarWithToolbarAndTabsTest.java b/design/tests/src/android/support/design/widget/AppBarWithToolbarAndTabsTest.java
index c745f17..23ef7c5 100644
--- a/design/tests/src/android/support/design/widget/AppBarWithToolbarAndTabsTest.java
+++ b/design/tests/src/android/support/design/widget/AppBarWithToolbarAndTabsTest.java
@@ -27,7 +27,9 @@
import android.support.annotation.StringRes;
import android.support.design.test.R;
import android.support.design.testutils.Cheeses;
+import android.support.test.filters.FlakyTest;
import android.support.test.filters.LargeTest;
+import android.support.test.filters.Suppress;
import org.junit.Test;
@@ -232,6 +234,8 @@
assertAppBarElevation(mDefaultElevationValue);
}
+ @Suppress
+ @FlakyTest(bugId = 30701044)
@LargeTest
@Test
public void testSnappingToolbarAndSnappingTabs() throws Throwable {
diff --git a/dynamic-animation/AndroidManifest.xml b/dynamic-animation/AndroidManifest.xml
index 9e83183..f818402 100644
--- a/dynamic-animation/AndroidManifest.xml
+++ b/dynamic-animation/AndroidManifest.xml
@@ -16,5 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.dynamicanimation">
<uses-sdk android:minSdkVersion="16"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/dynamic-animation/src/android/support/animation/DynamicAnimation.java b/dynamic-animation/src/android/support/animation/DynamicAnimation.java
index 7285943..cb21683 100644
--- a/dynamic-animation/src/android/support/animation/DynamicAnimation.java
+++ b/dynamic-animation/src/android/support/animation/DynamicAnimation.java
@@ -332,6 +332,12 @@
* to pixel/second based on the density of the screen to achieve a consistent look across
* different screens.
*
+ * <p>To convert from dp/second to pixel/second:
+ * <pre class="prettyprint">
+ * float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond,
+ * getResources().getDisplayMetrics());
+ * </pre>
+ *
* @param startVelocity start velocity of the animation in pixel/second
* @return the Animation whose start velocity is being set
*/
diff --git a/dynamic-animation/src/android/support/animation/SpringAnimation.java b/dynamic-animation/src/android/support/animation/SpringAnimation.java
index 70f939b..47096df 100644
--- a/dynamic-animation/src/android/support/animation/SpringAnimation.java
+++ b/dynamic-animation/src/android/support/animation/SpringAnimation.java
@@ -27,12 +27,39 @@
* The animation will continue to run until the spring force reaches equilibrium. If the spring used
* in the animation is undamped, the animation will never reach equilibrium. Instead, it will
* oscillate forever.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * </div>
+ *
+ * <p>To create a simple {@link SpringAnimation} that uses the default {@link SpringForce}:</p>
+ * <pre class="prettyprint">
+ * // Create an animation to animate view's X property, set the rest position of the
+ * // default spring to 0, and start the animation with a starting velocity of 5000 (pixel/s).
+ * final SpringAnimation anim = new SpringAnimation(view, DynamicAnimation.X, 0)
+ * .setSpring(spring).setStartVelocity(5000).start();
+ * </pre>
+ *
+ * <p>Alternatively, a {@link SpringAnimation} can take a pre-configured {@link SpringForce}, and
+ * use that to drive the animation. </p>
+ * <pre class="prettyprint">
+ * // Create a low stiffness, low bounce spring at position 0.
+ * SpringForce spring = new SpringForce(0)
+ * .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+ * .setStiffness(SpringForce.STIFFNESS_LOW);
+ * // Create an animation to animate view's scaleY property, and start the animation using
+ * // the spring above and a starting value of 0.5. Additionally, constrain the range of value for
+ * // the animation to be non-negative, effectively preventing any spring overshoot.
+ * final SpringAnimation anim = new SpringAnimation(view, DynamicAnimation.SCALE_Y)
+ * .setMinValue(0).setSpring(spring).setStartValue(1).start();
+ * </pre>
*/
public final class SpringAnimation extends DynamicAnimation<SpringAnimation> {
private SpringForce mSpring = null;
private float mPendingPosition = UNSET;
private static final float UNSET = Float.MAX_VALUE;
+ private boolean mEndRequested = false;
/**
* This creates a SpringAnimation that animates the property of the given view.
@@ -132,13 +159,7 @@
throw new AndroidRuntimeException("Animations may only be started on the main thread");
}
if (mRunning) {
- if (mPendingPosition != UNSET) {
- mSpring.setFinalPosition(mPendingPosition);
- mPendingPosition = UNSET;
- }
- mValue = mSpring.getFinalPosition();
- mVelocity = 0;
- cancel();
+ mEndRequested = true;
}
}
@@ -183,6 +204,19 @@
@Override
boolean updateValueAndVelocity(long deltaT) {
+ // If user had requested end, then update the value and velocity to end state and consider
+ // animation done.
+ if (mEndRequested) {
+ if (mPendingPosition != UNSET) {
+ mSpring.setFinalPosition(mPendingPosition);
+ mPendingPosition = UNSET;
+ }
+ mValue = mSpring.getFinalPosition();
+ mVelocity = 0;
+ mEndRequested = false;
+ return true;
+ }
+
if (mPendingPosition != UNSET) {
double lastPosition = mSpring.getFinalPosition();
// Approximate by considering half of the time spring position stayed at the old
diff --git a/dynamic-animation/tests/src/android/support/dynamicanimation/tests/SpringTests.java b/dynamic-animation/tests/src/android/support/dynamicanimation/tests/SpringTests.java
index c87bf50..d716fb8 100644
--- a/dynamic-animation/tests/src/android/support/dynamicanimation/tests/SpringTests.java
+++ b/dynamic-animation/tests/src/android/support/dynamicanimation/tests/SpringTests.java
@@ -437,22 +437,48 @@
*/
@Test
public void testSkipToEnd() {
- final SpringAnimation anim = new SpringAnimation(mView1, DynamicAnimation.SCROLL_X, 0.0f);
+ final float finalPosition = 10f;
+ final SpringAnimation anim = new SpringAnimation(mView1, DynamicAnimation.SCROLL_X,
+ finalPosition);
+ final DynamicAnimation.OnAnimationEndListener mockListener =
+ mock(DynamicAnimation.OnAnimationEndListener.class);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
- anim.setStartValue(200).start();
+ anim.addEndListener(mockListener).setStartValue(200).start();
}
});
assertTrue(anim.isRunning());
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
+ int scrollX = mView1.getScrollX();
anim.skipToEnd();
+ // Expect no change in the animation values until next frame.
+ assertEquals(scrollX, mView1.getScrollX());
+ assertTrue(anim.isRunning());
}
});
- assertFalse(anim.isRunning());
- assertEquals(0, mView1.getScrollX());
+ verify(mockListener, timeout(100).times(1)).onAnimationEnd(anim, false, finalPosition, 0);
+
+ // Also make sure the skipToEnd() call doesn't affect next animation run.
+ final DynamicAnimation.OnAnimationEndListener mockListener2 =
+ mock(DynamicAnimation.OnAnimationEndListener.class);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ anim.addEndListener(mockListener2);
+ anim.animateToFinalPosition(finalPosition + 1000f);
+ }
+ });
+ // Verify that the animation doesn't finish right away
+ verify(mockListener2, timeout(300).times(0)).onAnimationEnd(any(DynamicAnimation.class),
+ any(boolean.class), any(float.class), any(float.class));
+
+ // But the animation should eventually finish.
+ verify(mockListener, timeout(1000).times(1)).onAnimationEnd(anim, false,
+ finalPosition + 1000f, 0);
+
}
/**
diff --git a/emoji/Android.mk b/emoji/Android.mk
new file mode 100644
index 0000000..337f5b9
--- /dev/null
+++ b/emoji/Android.mk
@@ -0,0 +1,16 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/emoji/appcompat/Android.mk b/emoji/appcompat/Android.mk
new file mode 100644
index 0000000..54f6b14
--- /dev/null
+++ b/emoji/appcompat/Android.mk
@@ -0,0 +1,41 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+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-emoji-appcompat \
+# android-support-v7-appcompat \
+# android-support-emoji \
+# 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-emoji-appcompat
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-annotations \
+ android-support-v7-appcompat \
+ android-support-emoji \
+ android-support-compat
+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/emoji/appcompat/AndroidManifest.xml b/emoji/appcompat/AndroidManifest.xml
index 81bc0f8..56d8ab5 100644
--- a/emoji/appcompat/AndroidManifest.xml
+++ b/emoji/appcompat/AndroidManifest.xml
@@ -17,6 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.text.emoji.appcompat">
<uses-sdk android:minSdkVersion="19"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/emoji/bundled-typeface/Android.mk b/emoji/bundled-typeface/Android.mk
new file mode 100644
index 0000000..324801e
--- /dev/null
+++ b/emoji/bundled-typeface/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+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-emoji-typeface \
+# android-support-emoji \
+# 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-emoji-typeface
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-annotations \
+ android-support-emoji \
+ android-support-compat
+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/emoji/bundled-typeface/AndroidManifest.xml b/emoji/bundled-typeface/AndroidManifest.xml
index 2c5bc46..5bb7578 100644
--- a/emoji/bundled-typeface/AndroidManifest.xml
+++ b/emoji/bundled-typeface/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.text.emoji.typeface">
<uses-sdk android:minSdkVersion="19"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}"/>
- <application/>
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
\ No newline at end of file
diff --git a/emoji/bundled-typeface/build.gradle b/emoji/bundled-typeface/build.gradle
index 5014649..ab45916 100644
--- a/emoji/bundled-typeface/build.gradle
+++ b/emoji/bundled-typeface/build.gradle
@@ -22,4 +22,14 @@
name 'Android Emoji Compat'
inceptionYear '2017'
description 'Library bundled with assets to enable emoji compatibility in Kitkat and newer devices to avoid the empty emoji characters.'
+
+ license {
+ name 'SIL Open Font License, Version 1.1'
+ url 'http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web'
+ }
+
+ license {
+ name 'Unicode, Inc. License'
+ url 'http://www.unicode.org/copyright.html#License'
+ }
}
\ No newline at end of file
diff --git a/emoji/core/Android.mk b/emoji/core/Android.mk
new file mode 100644
index 0000000..35fb551
--- /dev/null
+++ b/emoji/core/Android.mk
@@ -0,0 +1,54 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+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-emoji \
+# 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-emoji
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-emoji-flatbuffers-jarjar
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-annotations \
+ android-support-compat
+LOCAL_JAR_EXCLUDE_FILES := none
+LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+#############################################################
+# Pre-built dependency jars
+#############################################################
+
+include $(CLEAR_VARS)
+LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
+ android-support-emoji-flatbuffers:third_party/flatbuffers/flatbuffers-java-1.6.0.jar
+include $(BUILD_MULTI_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-emoji-flatbuffers
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
+LOCAL_MODULE := android-support-emoji-flatbuffers-jarjar
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/emoji/core/AndroidManifest.xml b/emoji/core/AndroidManifest.xml
index b288921..815858d 100644
--- a/emoji/core/AndroidManifest.xml
+++ b/emoji/core/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.text.emoji">
<uses-sdk android:minSdkVersion="19"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/emoji/core/build.gradle b/emoji/core/build.gradle
index cc389d1..0e94d4e 100644
--- a/emoji/core/build.gradle
+++ b/emoji/core/build.gradle
@@ -8,7 +8,7 @@
compile jarjar.repackage {
from files('third_party/flatbuffers/flatbuffers-java-1.6.0.jar')
destinationName 'flatbuffers-java-1.6.0-repacked.jar'
- classRename 'com.google.flatbuffers.**', 'com.google.flatbuffers.emojicompat.@1'
+ classRename 'com.google.flatbuffers.**', 'android.support.text.emoji.flatbuffer.@1'
}
androidTestCompile (libs.test_runner) {
@@ -20,6 +20,7 @@
androidTestCompile libs.mockito_core
androidTestCompile libs.dexmaker
androidTestCompile libs.dexmaker_mockito
+ androidTestCompile project(path: ':support-testutils')
}
android {
@@ -38,4 +39,14 @@
name 'Android Emoji Compat'
inceptionYear '2017'
description 'Core library to enable emoji compatibility in Kitkat and newer devices to avoid the empty emoji characters.'
-}
\ No newline at end of file
+
+ license {
+ name 'SIL Open Font License, Version 1.1'
+ url 'http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web'
+ }
+
+ license {
+ name 'Unicode, Inc. License'
+ url 'http://www.unicode.org/copyright.html#License'
+ }
+}
diff --git a/emoji/core/jarjar-rules.txt b/emoji/core/jarjar-rules.txt
new file mode 100644
index 0000000..e6b4b3e
--- /dev/null
+++ b/emoji/core/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.flatbuffers.** android.support.text.emoji.flatbuffer.@1
\ No newline at end of file
diff --git a/emoji/core/src/android/support/text/emoji/EmojiProcessor.java b/emoji/core/src/android/support/text/emoji/EmojiProcessor.java
index 9712b80..9275a6e 100644
--- a/emoji/core/src/android/support/text/emoji/EmojiProcessor.java
+++ b/emoji/core/src/android/support/text/emoji/EmojiProcessor.java
@@ -457,9 +457,10 @@
*/
private boolean hasGlyph(final CharSequence charSequence, int start, final int end,
final EmojiMetadata metadata) {
- // if the emoji was added to OS at a later version we know that the system cannot render it.
- // without this check pre M devices result in more false positives.
- if (metadata.getSdkAdded() > Build.VERSION.SDK_INT) {
+ // For pre M devices, heuristic in PaintCompat can result in false positives. we are
+ // adding another heuristic using the sdkAdded field. if the emoji was added to OS
+ // at a later version we assume that the system probably cannot render it.
+ if (Build.VERSION.SDK_INT < 23 && metadata.getSdkAdded() > Build.VERSION.SDK_INT) {
return false;
}
diff --git a/emoji/core/src/android/support/text/emoji/flatbuffer/MetadataItem.java b/emoji/core/src/android/support/text/emoji/flatbuffer/MetadataItem.java
index f0466ea..ea21821 100644
--- a/emoji/core/src/android/support/text/emoji/flatbuffer/MetadataItem.java
+++ b/emoji/core/src/android/support/text/emoji/flatbuffer/MetadataItem.java
@@ -21,9 +21,6 @@
import android.support.annotation.RestrictTo;
-import com.google.flatbuffers.emojicompat.FlatBufferBuilder;
-import com.google.flatbuffers.emojicompat.Table;
-
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
diff --git a/emoji/core/src/android/support/text/emoji/flatbuffer/MetadataList.java b/emoji/core/src/android/support/text/emoji/flatbuffer/MetadataList.java
index 4de07d7..257b243 100644
--- a/emoji/core/src/android/support/text/emoji/flatbuffer/MetadataList.java
+++ b/emoji/core/src/android/support/text/emoji/flatbuffer/MetadataList.java
@@ -21,9 +21,6 @@
import android.support.annotation.RestrictTo;
-import com.google.flatbuffers.emojicompat.FlatBufferBuilder;
-import com.google.flatbuffers.emojicompat.Table;
-
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
diff --git a/emoji/core/tests/java/android/support/text/emoji/EmojiInstrumentationTest.java b/emoji/core/tests/java/android/support/text/emoji/EmojiInstrumentationTest.java
index eb0ba3a..22ee475 100644
--- a/emoji/core/tests/java/android/support/text/emoji/EmojiInstrumentationTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/EmojiInstrumentationTest.java
@@ -34,16 +34,15 @@
import android.support.test.filters.LargeTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.support.testutils.PollingCheck;
import android.support.text.emoji.test.R;
import android.support.text.emoji.util.KeyboardUtil;
import android.support.text.emoji.util.TestString;
import android.text.Editable;
-import android.text.Selection;
import android.text.Spannable;
import android.text.Spanned;
import android.text.style.RelativeSizeSpan;
import android.util.TypedValue;
-import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.EditText;
import android.widget.TextView;
@@ -102,7 +101,8 @@
// cover the charsequence with RelativeSizeSpan which will triple the size of the
// characters.
- final RelativeSizeSpan sizeSpan = new RelativeSizeSpan(3.0f);
+ final float multiplier = 3.0f;
+ final RelativeSizeSpan sizeSpan = new RelativeSizeSpan(multiplier);
spanned.setSpan(sizeSpan, 0, charSequence.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
// set the new text
mInstrumentation.runOnMainSync(new Runnable() {
@@ -128,18 +128,13 @@
final EditText editText = (EditText) activity.findViewById(R.id.editText);
final TestString string = new TestString(EMOJI_WITH_ZWJ).withPrefix()
.withSuffix();
- final InputConnection inputConnection = editText.onCreateInputConnection(new EditorInfo());
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- KeyboardUtil.setComposingTextInBatch(inputConnection, string.toString());
- }
- });
- mInstrumentation.waitForIdleSync();
+ final InputConnection inputConnection = KeyboardUtil.initTextViewForSimulatedIme(
+ mInstrumentation, editText);
+ KeyboardUtil.setComposingTextInBatch(mInstrumentation, inputConnection,
+ string.toString());
Editable editable = editText.getEditableText();
- // 0xf0950 is the remapped codepoint for WOMEN_WITH_BALL
assertThat(editable, hasEmojiAt(EMOJI_WITH_ZWJ, string.emojiStartIndex(),
string.emojiEndIndex()));
}
@@ -150,25 +145,19 @@
final EditText editText = (EditText) activity.findViewById(R.id.editText);
final TestString string = new TestString(EMOJI_WITH_ZWJ).withPrefix()
.withSuffix();
- final InputConnection inputConnection = editText.onCreateInputConnection(new EditorInfo());
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- KeyboardUtil.setComposingTextInBatch(inputConnection, string.toString());
- Selection.setSelection(editText.getEditableText(), string.emojiEndIndex());
- }
- });
- mInstrumentation.waitForIdleSync();
+ final InputConnection inputConnection = KeyboardUtil.initTextViewForSimulatedIme(
+ mInstrumentation, editText);
+ KeyboardUtil.setComposingTextInBatch(mInstrumentation, inputConnection, string.toString());
+
+ // assert that emoji is there
final Editable editable = editText.getEditableText();
assertThat(editable, hasEmoji());
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- KeyboardUtil.deleteSurrondingText(inputConnection, 1, 0);
- }
- });
- mInstrumentation.waitForIdleSync();
+ // put selection at the end of emoji and back delete
+ KeyboardUtil.setSelection(mInstrumentation, editText.getEditableText(),
+ string.emojiEndIndex());
+ KeyboardUtil.deleteSurroundingText(mInstrumentation, inputConnection, 1, 0);
+
assertThat(editable, not(hasEmoji()));
}
@@ -178,25 +167,20 @@
final EditText editText = (EditText) activity.findViewById(R.id.editText);
final TestString string = new TestString(EMOJI_WITH_ZWJ).withPrefix()
.withSuffix();
- final InputConnection inputConnection = editText.onCreateInputConnection(new EditorInfo());
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- KeyboardUtil.setComposingTextInBatch(inputConnection, string.toString());
- Selection.setSelection(editText.getEditableText(), string.emojiStartIndex());
- }
- });
- mInstrumentation.waitForIdleSync();
+ final InputConnection inputConnection = KeyboardUtil.initTextViewForSimulatedIme(
+ mInstrumentation, editText);
+ KeyboardUtil.setComposingTextInBatch(mInstrumentation, inputConnection, string.toString());
+
+ // assert that emoji is there
final Editable editable = editText.getEditableText();
assertThat(editable, hasEmoji());
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- KeyboardUtil.deleteSurrondingText(inputConnection, 0, 1);
- }
- });
- mInstrumentation.waitForIdleSync();
+ // put selection at the begining of emoji and forward delete
+ KeyboardUtil.setSelection(mInstrumentation, editText.getEditableText(),
+ string.emojiStartIndex());
+ KeyboardUtil.deleteSurroundingText(mInstrumentation, inputConnection, 0, 1);
+
+
assertThat(editable, not(hasEmoji()));
}
@@ -206,21 +190,26 @@
final EditText editText = (EditText) activity.findViewById(R.id.editText);
final TestString string = new TestString(EMOJI_WITH_ZWJ).withPrefix()
.withSuffix();
- final InputConnection inputConnection = editText.onCreateInputConnection(new EditorInfo());
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- KeyboardUtil.setComposingTextInBatch(inputConnection, string.toString());
- Selection.setSelection(editText.getEditableText(), string.emojiEndIndex());
- }
- });
- mInstrumentation.waitForIdleSync();
+ final InputConnection inputConnection = KeyboardUtil.initTextViewForSimulatedIme(
+ mInstrumentation, editText);
+ KeyboardUtil.setComposingTextInBatch(mInstrumentation, inputConnection, string.toString());
+
+ // assert that emoji is there
final Editable editable = editText.getEditableText();
assertThat(editable, hasEmoji());
-
+ // put selection at the end of emoji and back delete
+ KeyboardUtil.setSelection(mInstrumentation, editText.getEditableText(),
+ string.emojiEndIndex());
mInstrumentation.sendKeySync(del());
mInstrumentation.waitForIdleSync();
+
+ PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+ @Override
+ public boolean canProceed() {
+ return not(hasEmoji()).matches(true);
+ }
+ });
assertThat(editable, not(hasEmoji()));
}
@@ -230,20 +219,26 @@
final EditText editText = (EditText) activity.findViewById(R.id.editText);
final TestString string = new TestString(EMOJI_WITH_ZWJ).withPrefix()
.withSuffix();
- final InputConnection inputConnection = editText.onCreateInputConnection(new EditorInfo());
- mInstrumentation.runOnMainSync(new Runnable() {
- @Override
- public void run() {
- KeyboardUtil.setComposingTextInBatch(inputConnection, string.toString());
- Selection.setSelection(editText.getEditableText(), string.emojiStartIndex());
- }
- });
- mInstrumentation.waitForIdleSync();
+ final InputConnection inputConnection = KeyboardUtil.initTextViewForSimulatedIme(
+ mInstrumentation, editText);
+ KeyboardUtil.setComposingTextInBatch(mInstrumentation, inputConnection, string.toString());
+
+ // assert that emoji is there
final Editable editable = editText.getEditableText();
assertThat(editable, hasEmoji());
+ // put selection at the begining of emoji and forward delete
+ KeyboardUtil.setSelection(mInstrumentation, editText.getEditableText(),
+ string.emojiStartIndex());
mInstrumentation.sendKeySync(forwardDel());
mInstrumentation.waitForIdleSync();
+
+ PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
+ @Override
+ public boolean canProceed() {
+ return not(hasEmoji()).matches(true);
+ }
+ });
assertThat(editable, not(hasEmoji()));
}
}
diff --git a/emoji/core/tests/java/android/support/text/emoji/util/KeyboardUtil.java b/emoji/core/tests/java/android/support/text/emoji/util/KeyboardUtil.java
index 4764455..0c89b2d 100644
--- a/emoji/core/tests/java/android/support/text/emoji/util/KeyboardUtil.java
+++ b/emoji/core/tests/java/android/support/text/emoji/util/KeyboardUtil.java
@@ -15,8 +15,17 @@
*/
package android.support.text.emoji.util;
+import android.app.Instrumentation;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.method.QwertyKeyListener;
+import android.text.method.TextKeyListener;
import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
+import android.widget.TextView;
+
+import java.util.concurrent.CountDownLatch;
/**
* Utility class for KeyEvents
@@ -65,15 +74,74 @@
return new KeyEvent(currentTime, currentTime, KeyEvent.ACTION_DOWN, keycode, 0);
}
- public static void setComposingTextInBatch(InputConnection input, CharSequence text) {
- input.beginBatchEdit();
- input.setComposingText(text, 1);
- input.endBatchEdit();
+ public static void setComposingTextInBatch(final Instrumentation instrumentation,
+ final InputConnection inputConnection, final CharSequence text)
+ throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ inputConnection.beginBatchEdit();
+ inputConnection.setComposingText(text, 1);
+ inputConnection.endBatchEdit();
+ latch.countDown();
+ }
+ });
+
+ latch.await();
+ instrumentation.waitForIdleSync();
}
- public static void deleteSurrondingText(InputConnection input, int before, int after) {
- input.beginBatchEdit();
- input.deleteSurroundingText(before, after);
- input.endBatchEdit();
+ public static void deleteSurroundingText(final Instrumentation instrumentation,
+ final InputConnection inputConnection, final int before, final int after)
+ throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ inputConnection.beginBatchEdit();
+ inputConnection.deleteSurroundingText(before, after);
+ inputConnection.endBatchEdit();
+ latch.countDown();
+ }
+ });
+ latch.await();
+ instrumentation.waitForIdleSync();
+ }
+
+ public static void setSelection(Instrumentation instrumentation, final Spannable spannable,
+ final int start) throws InterruptedException {
+ setSelection(instrumentation, spannable, start, start);
+ }
+
+ public static void setSelection(Instrumentation instrumentation, final Spannable spannable,
+ final int start, final int end) throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ Selection.setSelection(spannable, start, end);
+ latch.countDown();
+ }
+ });
+ latch.await();
+ instrumentation.waitForIdleSync();
+ }
+
+ public static InputConnection initTextViewForSimulatedIme(Instrumentation instrumentation,
+ final TextView textView) throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ instrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ textView.setKeyListener(
+ QwertyKeyListener.getInstance(false, TextKeyListener.Capitalize.NONE));
+ textView.setText("", TextView.BufferType.EDITABLE);
+ latch.countDown();
+ }
+ });
+ latch.await();
+ instrumentation.waitForIdleSync();
+ return textView.onCreateInputConnection(new EditorInfo());
}
}
diff --git a/emoji/core/tests/res/layout/activity_default.xml b/emoji/core/tests/res/layout/activity_default.xml
index b9a09ca..c8341b8 100644
--- a/emoji/core/tests/res/layout/activity_default.xml
+++ b/emoji/core/tests/res/layout/activity_default.xml
@@ -9,7 +9,8 @@
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content"
+ android:textSize="8sp"/>
<android.support.text.emoji.widget.EmojiEditText
android:id="@+id/editText"
diff --git a/emoji/core/third_party/flatbuffers/FLATBUFFERS_LICENSE.txt b/emoji/core/third_party/flatbuffers/LICENSE
similarity index 100%
rename from emoji/core/third_party/flatbuffers/FLATBUFFERS_LICENSE.txt
rename to emoji/core/third_party/flatbuffers/LICENSE
diff --git a/emoji/core/third_party/flatbuffers/README.android b/emoji/core/third_party/flatbuffers/README.android
new file mode 100644
index 0000000..4317ba1
--- /dev/null
+++ b/emoji/core/third_party/flatbuffers/README.android
@@ -0,0 +1,10 @@
+URL: https://github.com/google/flatbuffers/archive/v1.6.0.zip
+Version: 1.6.0
+License: Apache License, Version 2.0
+License File: LICENSE
+
+Description:
+Cross platform serialization library. Used to read the metadata in the font.
+
+Local Modifications:
+No modifications.
\ No newline at end of file
diff --git a/emoji/core/third_party/notocoloremoji/LICENSE b/emoji/core/third_party/notocoloremoji/LICENSE
new file mode 100644
index 0000000..d952d62
--- /dev/null
+++ b/emoji/core/third_party/notocoloremoji/LICENSE
@@ -0,0 +1,92 @@
+This Font Software is licensed under the SIL Open Font License,
+Version 1.1.
+
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font
+creation efforts of academic and linguistic communities, and to
+provide a free and open framework in which fonts may be shared and
+improved in partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply to
+any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software
+components as distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to,
+deleting, or substituting -- in part or in whole -- any of the
+components of the Original Version, by changing formats or by porting
+the Font Software to a new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed,
+modify, redistribute, and sell modified and unmodified copies of the
+Font Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components, in
+Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the
+corresponding Copyright Holder. This restriction only applies to the
+primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created using
+the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/emoji/core/third_party/unicode/LICENSE b/emoji/core/third_party/unicode/LICENSE
new file mode 100644
index 0000000..3a6d4f2
--- /dev/null
+++ b/emoji/core/third_party/unicode/LICENSE
@@ -0,0 +1,37 @@
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright © 1991-2016 Unicode, Inc. All rights reserved.
+Distributed under the Terms of Use in
+http://www.unicode.org/copyright.html.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation
+(the "Data Files") or Unicode software and any associated documentation
+(the "Software") to deal in the Data Files or Software
+without restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, and/or sell copies of
+the Data Files or Software, and to permit persons to whom the Data Files
+or Software are furnished to do so, provided that
+(a) this copyright and permission notice appear with all copies
+of the Data Files or Software,
+(b) this copyright and permission notice appear in associated
+documentation, and
+(c) there is clear notice in each modified Data File or in the Software
+as well as in the documentation associated with the Data File(s) or
+Software that the data or software has been modified.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in these Data Files or Software without prior
+written authorization of the copyright holder.
\ No newline at end of file
diff --git a/exifinterface/AndroidManifest.xml b/exifinterface/AndroidManifest.xml
index 94b3300..344b99b 100644
--- a/exifinterface/AndroidManifest.xml
+++ b/exifinterface/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.exifinterface">
<uses-sdk android:minSdkVersion="14"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/fragment/AndroidManifest.xml b/fragment/AndroidManifest.xml
index c6fdcc4..725fe43 100644
--- a/fragment/AndroidManifest.xml
+++ b/fragment/AndroidManifest.xml
@@ -17,6 +17,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="android.support.fragment">
<uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.fragment"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/fragment/tests/java/android/support/v4/app/LoaderTest.java b/fragment/tests/java/android/support/v4/app/LoaderTest.java
index fde9d63..9ae0723 100644
--- a/fragment/tests/java/android/support/v4/app/LoaderTest.java
+++ b/fragment/tests/java/android/support/v4/app/LoaderTest.java
@@ -59,7 +59,19 @@
new ActivityTestRule(LoaderActivity.class);
@After
- public void clearActivity() {
+ public void resetActivity() {
+ final LoaderActivity activity = LoaderActivity.sActivity;
+ final int unspecifiedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ if (activity != null && activity.getRequestedOrientation() != unspecifiedOrientation) {
+ LoaderActivity.sResumed = new CountDownLatch(1);
+ activity.setRequestedOrientation(unspecifiedOrientation);
+ // Wait for the orientation change to settle, if there was a change
+ try {
+ LoaderActivity.sResumed.await(1, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // I guess there wasn't a change in orientation after all
+ }
+ }
LoaderActivity.clearState();
}
diff --git a/fragment/tests/res/values/colors.xml b/fragment/tests/res/values/colors.xml
index d0d5309..3c7bf7c 100644
--- a/fragment/tests/res/values/colors.xml
+++ b/fragment/tests/res/values/colors.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<color name="text_color">#FF8090</color>
<color name="test_red">#FF6030</color>
diff --git a/fragment/tests/res/values/dimens.xml b/fragment/tests/res/values/dimens.xml
index c3617a9..d473645 100644
--- a/fragment/tests/res/values/dimens.xml
+++ b/fragment/tests/res/values/dimens.xml
@@ -13,6 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<dimen name="text_medium_size">20sp</dimen>
</resources>
\ No newline at end of file
diff --git a/fragment/tests/res/values/strings.xml b/fragment/tests/res/values/strings.xml
index 2fa1430..e605c2c 100644
--- a/fragment/tests/res/values/strings.xml
+++ b/fragment/tests/res/values/strings.xml
@@ -13,6 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<string name="hello">Hello World</string>
</resources>
\ No newline at end of file
diff --git a/fragment/tests/res/values/styles.xml b/fragment/tests/res/values/styles.xml
index ae6325b..3e8e01c 100644
--- a/fragment/tests/res/values/styles.xml
+++ b/fragment/tests/res/values/styles.xml
@@ -13,13 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<style name="TestActivityTheme">
<item name="android:windowAnimationStyle">@null</item>
</style>
- <style name="TextMediumStyle" parent="@android:style/TextAppearance.Medium">
- <item name="android:textSize">@dimen/text_medium_size</item>
- <item name="android:textColor">@color/text_color</item>
- <item name="android:textStyle">italic</item>
- </style>
</resources>
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 84939b4..5bc6b35 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=../../../../tools/external/gradle/gradle-3.3-bin.zip
+distributionUrl=../../../../tools/external/gradle/gradle-3.4-bin.zip
diff --git a/graphics/drawable/animated/AndroidManifest.xml b/graphics/drawable/animated/AndroidManifest.xml
index 58017dc..b59fa47 100644
--- a/graphics/drawable/animated/AndroidManifest.xml
+++ b/graphics/drawable/animated/AndroidManifest.xml
@@ -16,6 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.graphics.drawable.animated">
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application/>
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/graphics/drawable/static/AndroidManifest.xml b/graphics/drawable/static/AndroidManifest.xml
index 0383e4c..14821a8 100644
--- a/graphics/drawable/static/AndroidManifest.xml
+++ b/graphics/drawable/static/AndroidManifest.xml
@@ -16,6 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.graphics.drawable">
- <application/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/instantvideo/AndroidManifest.xml b/instantvideo/AndroidManifest.xml
index 08ebcbe..26b46c0 100644
--- a/instantvideo/AndroidManifest.xml
+++ b/instantvideo/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.media.instantvideo">
<uses-sdk android:minSdkVersion="14"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/media-compat/AndroidManifest.xml b/media-compat/AndroidManifest.xml
index 4e73a70..f5e74a2 100644
--- a/media-compat/AndroidManifest.xml
+++ b/media-compat/AndroidManifest.xml
@@ -17,6 +17,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="android.support.mediacompat">
<uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.mediacompat"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
index fdea9cf..56a0b1b 100644
--- a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -703,22 +703,6 @@
* @param context The context to use to create the session.
* @param mediaSession A {@link android.media.session.MediaSession} object.
* @return An equivalent {@link MediaSessionCompat} object, or null if none.
- * @deprecated Use {@link #fromMediaSession(Context, Object)} instead.
- */
- @Deprecated
- public static MediaSessionCompat obtain(Context context, Object mediaSession) {
- return fromMediaSession(context, mediaSession);
- }
-
- /**
- * Creates an instance from a framework {@link android.media.session.MediaSession} object.
- * <p>
- * This method is only supported on API 21+. On API 20 and below, it returns null.
- * </p>
- *
- * @param context The context to use to create the session.
- * @param mediaSession A {@link android.media.session.MediaSession} object.
- * @return An equivalent {@link MediaSessionCompat} object, or null if none.
*/
public static MediaSessionCompat fromMediaSession(Context context, Object mediaSession) {
if (context != null && mediaSession != null && Build.VERSION.SDK_INT >= 21) {
@@ -1446,22 +1430,6 @@
*
* @param queueItem A {@link android.media.session.MediaSession.QueueItem} object.
* @return An equivalent {@link QueueItem} object, or null if none.
- * @deprecated Use {@link #fromQueueItem(Object)} instead.
- */
- @Deprecated
- public static QueueItem obtain(Object queueItem) {
- return fromQueueItem(queueItem);
- }
-
- /**
- * Creates an instance from a framework {@link android.media.session.MediaSession.QueueItem}
- * object.
- * <p>
- * This method is only supported on API 21+. On API 20 and below, it returns null.
- * </p>
- *
- * @param queueItem A {@link android.media.session.MediaSession.QueueItem} object.
- * @return An equivalent {@link QueueItem} object, or null if none.
*/
public static QueueItem fromQueueItem(Object queueItem) {
if (queueItem == null || Build.VERSION.SDK_INT < 21) {
diff --git a/pathmap.mk b/pathmap.mk
index fd29ee1..1d8a47c 100644
--- a/pathmap.mk
+++ b/pathmap.mk
@@ -20,26 +20,29 @@
FRAMEWORKS_SUPPORT_SUBDIRS := \
annotations \
compat \
- media-compat \
- fragment \
core-ui \
core-utils \
- v7/gridlayout \
- v7/cardview \
- v7/mediarouter \
- v7/palette \
- v13 \
- v17/leanback \
+ customtabs \
design \
+ dynamic-animation \
+ exifinterface \
+ fragment \
+ instantvideo \
+ media-compat \
percent \
recommendation \
transition \
+ tv-provider \
+ v7/cardview \
+ v7/gridlayout \
+ v7/mediarouter \
+ v7/palette \
v7/preference \
+ v13 \
v14/preference \
+ v17/leanback \
v17/preference-leanback \
- customtabs \
- exifinterface \
- dynamic-animation
+ wearable
#
# A version of FRAMEWORKS_SUPPORT_SUBDIRS that is expanded to full paths from
@@ -50,15 +53,20 @@
frameworks/support/graphics/drawable/animated \
frameworks/support/graphics/drawable/static \
frameworks/support/v7/appcompat/src \
- frameworks/support/v7/recyclerview/src
+ frameworks/support/v7/recyclerview/src \
+ frameworks/support/emoji/core/src \
+ frameworks/support/emoji/appcompat/src \
+ frameworks/support/emoji/bundled-typeface/src
#
# A list of support library modules.
#
FRAMEWORKS_SUPPORT_JAVA_LIBRARIES := \
$(foreach dir,$(FRAMEWORKS_SUPPORT_SUBDIRS),android-support-$(subst /,-,$(dir))) \
- android-support-v4 \
android-support-vectordrawable \
android-support-animatedvectordrawable \
android-support-v7-appcompat \
- android-support-v7-recyclerview
+ android-support-v7-recyclerview \
+ android-support-emoji \
+ android-support-emoji-appcompat \
+ android-support-emoji-typeface
diff --git a/percent/AndroidManifest.xml b/percent/AndroidManifest.xml
index fd770ab..58eebfe 100644
--- a/percent/AndroidManifest.xml
+++ b/percent/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.percent">
<uses-sdk android:minSdkVersion="14"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/percent/build.gradle b/percent/build.gradle
index 1dc9490..90c2962 100644
--- a/percent/build.gradle
+++ b/percent/build.gradle
@@ -3,7 +3,6 @@
dependencies {
compile project(':support-compat')
-
androidTestCompile (libs.test_runner) {
exclude module: 'support-annotations'
}
diff --git a/percent/src/.readme b/percent/src/.readme
deleted file mode 100644
index 4bcebad..0000000
--- a/percent/src/.readme
+++ /dev/null
@@ -1,2 +0,0 @@
-This hidden file is there to ensure there is an src folder.
-Once we support binary library this will go away.
\ No newline at end of file
diff --git a/percent/src/android/support/percent/PercentFrameLayout.java b/percent/src/android/support/percent/PercentFrameLayout.java
index 9dce2bb..b9abd39 100644
--- a/percent/src/android/support/percent/PercentFrameLayout.java
+++ b/percent/src/android/support/percent/PercentFrameLayout.java
@@ -74,7 +74,60 @@
* </pre>
* This will make the aspect ratio 16:9 (1.78:1) with the width fixed at 300dp and height adjusted
* accordingly.
+ *
+ * @deprecated consider using ConstraintLayout and associated layouts instead. The following shows
+ * how to replicate the functionality of percentage layouts with a ConstraintLayout. The Guidelines
+ * are used to define each percentage break point, and then a Button view is stretched to fill
+ * the gap:
+ *
+ * <pre class="prettyprint">
+ * <android.support.constraint.ConstraintLayout
+ * xmlns:android="http://schemas.android.com/apk/res/android"
+ * xmlns:app="http://schemas.android.com/apk/res-auto"
+ * android:layout_width="match_parent"
+ * android:layout_height="match_parent">
+ *
+ * <android.support.constraint.Guideline
+ * android:layout_width="wrap_content"
+ * android:layout_height="wrap_content"
+ * android:id="@+id/left_guideline"
+ * app:layout_constraintGuide_percent=".15"
+ * android:orientation="vertical"/>
+ *
+ * <android.support.constraint.Guideline
+ * android:layout_width="wrap_content"
+ * android:layout_height="wrap_content"
+ * android:id="@+id/right_guideline"
+ * app:layout_constraintGuide_percent=".85"
+ * android:orientation="vertical"/>
+ *
+ * <android.support.constraint.Guideline
+ * android:layout_width="wrap_content"
+ * android:layout_height="wrap_content"
+ * android:id="@+id/top_guideline"
+ * app:layout_constraintGuide_percent=".15"
+ * android:orientation="horizontal"/>
+ *
+ * <android.support.constraint.Guideline
+ * android:layout_width="wrap_content"
+ * android:layout_height="wrap_content"
+ * android:id="@+id/bottom_guideline"
+ * app:layout_constraintGuide_percent=".85"
+ * android:orientation="horizontal"/>
+ *
+ * <Button
+ * android:text="Button"
+ * android:layout_width="0dp"
+ * android:layout_height="0dp"
+ * android:id="@+id/button"
+ * app:layout_constraintLeft_toLeftOf="@+id/left_guideline"
+ * app:layout_constraintRight_toRightOf="@+id/right_guideline"
+ * app:layout_constraintTop_toTopOf="@+id/top_guideline"
+ * app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" />
+ *
+ * </android.support.constraint.ConstraintLayout>
*/
+@Deprecated
public class PercentFrameLayout extends FrameLayout {
private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);
@@ -115,6 +168,10 @@
mHelper.restoreOriginalParams();
}
+ /**
+ * @deprecated this class is deprecated along with its parent class.
+ */
+ @Deprecated
public static class LayoutParams extends FrameLayout.LayoutParams
implements PercentLayoutHelper.PercentLayoutParams {
private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;
diff --git a/percent/src/android/support/percent/PercentLayoutHelper.java b/percent/src/android/support/percent/PercentLayoutHelper.java
index d681244..44a76c7 100644
--- a/percent/src/android/support/percent/PercentLayoutHelper.java
+++ b/percent/src/android/support/percent/PercentLayoutHelper.java
@@ -67,7 +67,59 @@
* }
* </pre>
* </ol>
+ * @deprecated consider using ConstraintLayout and associated layouts instead. The following shows
+ * how to replicate the functionality of percentage layouts with a ConstraintLayout. The Guidelines
+ * are used to define each percentage break point, and then a Button view is stretched to fill
+ * the gap:
+ *
+ * <pre class="prettyprint">
+ * <android.support.constraint.ConstraintLayout
+ * xmlns:android="http://schemas.android.com/apk/res/android"
+ * xmlns:app="http://schemas.android.com/apk/res-auto"
+ * android:layout_width="match_parent"
+ * android:layout_height="match_parent">
+ *
+ * <android.support.constraint.Guideline
+ * android:layout_width="wrap_content"
+ * android:layout_height="wrap_content"
+ * android:id="@+id/left_guideline"
+ * app:layout_constraintGuide_percent=".15"
+ * android:orientation="vertical"/>
+ *
+ * <android.support.constraint.Guideline
+ * android:layout_width="wrap_content"
+ * android:layout_height="wrap_content"
+ * android:id="@+id/right_guideline"
+ * app:layout_constraintGuide_percent=".85"
+ * android:orientation="vertical"/>
+ *
+ * <android.support.constraint.Guideline
+ * android:layout_width="wrap_content"
+ * android:layout_height="wrap_content"
+ * android:id="@+id/top_guideline"
+ * app:layout_constraintGuide_percent=".15"
+ * android:orientation="horizontal"/>
+ *
+ * <android.support.constraint.Guideline
+ * android:layout_width="wrap_content"
+ * android:layout_height="wrap_content"
+ * android:id="@+id/bottom_guideline"
+ * app:layout_constraintGuide_percent=".85"
+ * android:orientation="horizontal"/>
+ *
+ * <Button
+ * android:text="Button"
+ * android:layout_width="0dp"
+ * android:layout_height="0dp"
+ * android:id="@+id/button"
+ * app:layout_constraintLeft_toLeftOf="@+id/left_guideline"
+ * app:layout_constraintRight_toRightOf="@+id/right_guideline"
+ * app:layout_constraintTop_toTopOf="@+id/top_guideline"
+ * app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" />
+ *
+ * </android.support.constraint.ConstraintLayout>
*/
+@Deprecated
public class PercentLayoutHelper {
private static final String TAG = "PercentLayout";
@@ -344,7 +396,10 @@
/**
* Container for information about percentage dimensions and margins. It acts as an extension
* for {@code LayoutParams}.
+ *
+ * @deprecated use ConstraintLayout and Guidelines for layout support.
*/
+ @Deprecated
public static class PercentLayoutInfo {
/** The decimal value of the percentage-based width. */
public float widthPercent;
@@ -555,7 +610,10 @@
*
* Your {@code LayoutParams} subclass should contain an instance of {@code PercentLayoutInfo}
* and the implementation of this interface should be a simple accessor.
+ *
+ * @deprecated this class is deprecated along with its parent class.
*/
+ @Deprecated
public interface PercentLayoutParams {
PercentLayoutInfo getPercentLayoutInfo();
}
diff --git a/percent/src/android/support/percent/PercentRelativeLayout.java b/percent/src/android/support/percent/PercentRelativeLayout.java
index f068d6b..5b10349 100644
--- a/percent/src/android/support/percent/PercentRelativeLayout.java
+++ b/percent/src/android/support/percent/PercentRelativeLayout.java
@@ -73,7 +73,60 @@
* </pre>
* This will make the aspect ratio 16:9 (1.78:1) with the width fixed at 300dp and height adjusted
* accordingly.
+ *
+ * @deprecated consider using ConstraintLayout and associated layouts instead. The following shows
+ * how to replicate the functionality of percentage layouts with a ConstraintLayout. The Guidelines
+ * are used to define each percentage break point, and then a Button view is stretched to fill
+ * the gap:
+ *
+ * <pre class="prettyprint">
+ * <android.support.constraint.ConstraintLayout
+ * xmlns:android="http://schemas.android.com/apk/res/android"
+ * xmlns:app="http://schemas.android.com/apk/res-auto"
+ * android:layout_width="match_parent"
+ * android:layout_height="match_parent">
+ *
+ * <android.support.constraint.Guideline
+ * android:layout_width="wrap_content"
+ * android:layout_height="wrap_content"
+ * android:id="@+id/left_guideline"
+ * app:layout_constraintGuide_percent=".15"
+ * android:orientation="vertical"/>
+ *
+ * <android.support.constraint.Guideline
+ * android:layout_width="wrap_content"
+ * android:layout_height="wrap_content"
+ * android:id="@+id/right_guideline"
+ * app:layout_constraintGuide_percent=".85"
+ * android:orientation="vertical"/>
+ *
+ * <android.support.constraint.Guideline
+ * android:layout_width="wrap_content"
+ * android:layout_height="wrap_content"
+ * android:id="@+id/top_guideline"
+ * app:layout_constraintGuide_percent=".15"
+ * android:orientation="horizontal"/>
+ *
+ * <android.support.constraint.Guideline
+ * android:layout_width="wrap_content"
+ * android:layout_height="wrap_content"
+ * android:id="@+id/bottom_guideline"
+ * app:layout_constraintGuide_percent=".85"
+ * android:orientation="horizontal"/>
+ *
+ * <Button
+ * android:text="Button"
+ * android:layout_width="0dp"
+ * android:layout_height="0dp"
+ * android:id="@+id/button"
+ * app:layout_constraintLeft_toLeftOf="@+id/left_guideline"
+ * app:layout_constraintRight_toRightOf="@+id/right_guideline"
+ * app:layout_constraintTop_toTopOf="@+id/top_guideline"
+ * app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" />
+ *
+ * </android.support.constraint.ConstraintLayout>
*/
+@Deprecated
public class PercentRelativeLayout extends RelativeLayout {
private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);
@@ -114,6 +167,10 @@
mHelper.restoreOriginalParams();
}
+ /**
+ * @deprecated this class is deprecated along with its parent class.
+ */
+ @Deprecated
public static class LayoutParams extends RelativeLayout.LayoutParams
implements PercentLayoutHelper.PercentLayoutParams {
private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;
diff --git a/recommendation/Android.mk b/recommendation/Android.mk
index 2a68de6..4c27532 100644
--- a/recommendation/Android.mk
+++ b/recommendation/Android.mk
@@ -33,38 +33,3 @@
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# ===========================================================
-# Common Droiddoc vars
-recommendation.docs.src_files := \
- $(call all-java-files-under, src) \
- $(call all-html-files-under, src)
-recommendation.docs.java_libraries := \
- android-support-v4 \
- android-support-recommendation
-
-# Documentation
-# ===========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := android-support-recommendation
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(recommendation.docs.src_files)
-
-LOCAL_SDK_VERSION := 21
-LOCAL_IS_HOST_MODULE := false
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := external/doclava/res/assets/templates-sdk
-
-LOCAL_SHARED_ANDROID_LIBRARIES := $(recommendation.docs.java_libraries)
-
-LOCAL_DROIDDOC_OPTIONS := \
- -offlinemode \
- -hdf android.whichdoc offline \
- -federate Android http://developer.android.com \
- -federationapi Android prebuilts/sdk/api/21.txt \
- -hide 113
-
-include $(BUILD_DROIDDOC)
-
diff --git a/recommendation/AndroidManifest.xml b/recommendation/AndroidManifest.xml
index e36c822..09017e0 100644
--- a/recommendation/AndroidManifest.xml
+++ b/recommendation/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.recommendation">
<uses-sdk android:minSdkVersion="21"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/samples/Support13Demos/res/values/colors.xml b/samples/Support13Demos/res/values/colors.xml
index a1daf63..e50c7a0 100644
--- a/samples/Support13Demos/res/values/colors.xml
+++ b/samples/Support13Demos/res/values/colors.xml
@@ -18,5 +18,5 @@
<drawable name="red">#7f00</drawable>
<drawable name="blue">#770000ff</drawable>
<drawable name="green">#7700ff00</drawable>
- <drawable name="yellow">#77ffff00</drawable>
+ <drawable name="yellow">#77ffff00</drawable>
</resources>
diff --git a/samples/Support13Demos/res/values/strings.xml b/samples/Support13Demos/res/values/strings.xml
index 30b6a18..b0950b2 100644
--- a/samples/Support13Demos/res/values/strings.xml
+++ b/samples/Support13Demos/res/values/strings.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<string name="activity_sample_code">Support v13 Demos</string>
<string name="hello_world"><b>Hello, <i>World!</i></b></string>
diff --git a/samples/Support4Demos/res/values/strings.xml b/samples/Support4Demos/res/values/strings.xml
index 24d88d6..053dd3a 100644
--- a/samples/Support4Demos/res/values/strings.xml
+++ b/samples/Support4Demos/res/values/strings.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<string name="activity_sample_code">Support v4 Demos</string>
<string name="hello_world"><b>Hello, <i>World!</i></b></string>
@@ -24,9 +24,9 @@
</string>
<string name="alert_dialog_ok">OK</string>
<string name="alert_dialog_cancel">Cancel</string>
-
+
<string name="initial_text">Initial text.</string>
-
+
<string name="pick_result">Pick a result to send, or BACK to cancel.</string>
<string name="corky">Corky</string>
<string name="violet">Violet</string>
@@ -86,7 +86,7 @@
<string name="home">Go home</string>
<string name="new_fragment">Add new</string>
<string name="delete_fragment">Pop top</string>
-
+
<string name="fragment_tabs">Fragment/Tabs</string>
<string name="fragment_tabs_pager">Fragment/Tabs and Pager</string>
diff --git a/samples/Support7Demos/AndroidManifest.xml b/samples/Support7Demos/AndroidManifest.xml
index 0ba2e6a..0635231 100644
--- a/samples/Support7Demos/AndroidManifest.xml
+++ b/samples/Support7Demos/AndroidManifest.xml
@@ -254,6 +254,15 @@
</intent-filter>
</activity>
+ <activity android:name=".app.AppCompatWidgetsTextViews"
+ android:label="@string/appcompat_widgets_text_views"
+ android:theme="@style/Theme.Custom.TextLink">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.example.android.supportv7.SAMPLE_CODE" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".app.ToolbarUsage"
android:label="@string/toolbar_usage"
android:theme="@style/Theme.Custom.NoActionBar">
@@ -537,6 +546,16 @@
<category android:name="com.example.android.supportv7.SAMPLE_CODE"/>
</intent-filter>
</activity>
+
+ <!-- ListView styling activity -->
+ <activity android:name=".widget.ListViewActivity"
+ android:label="@string/list_view_activity"
+ android:theme="@style/Theme.AppCompat.Light">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.example.android.supportv7.SAMPLE_CODE"/>
+ </intent-filter>
+ </activity>
</application>
diff --git a/samples/Support7Demos/res/color/link_color.xml b/samples/Support7Demos/res/color/link_color.xml
new file mode 100644
index 0000000..6d71ed9
--- /dev/null
+++ b/samples/Support7Demos/res/color/link_color.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:alpha="?android:disabledAlpha"
+ android:color="?colorPrimary"
+ android:state_enabled="false"/>
+ <item android:color="?colorPrimary"/>
+</selector>
\ No newline at end of file
diff --git a/samples/Support7Demos/res/layout/appcompat_widgets_text_views.xml b/samples/Support7Demos/res/layout/appcompat_widgets_text_views.xml
new file mode 100644
index 0000000..4aafaa1
--- /dev/null
+++ b/samples/Support7Demos/res/layout/appcompat_widgets_text_views.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="16dp">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/text_plain_enabled"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ android:text="@string/text_plain_disabled"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/text_link_enabled"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ android:text="@string/text_link_disabled"/>
+
+ </LinearLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/samples/Support7Demos/res/layout/list_view_activity.xml b/samples/Support7Demos/res/layout/list_view_activity.xml
new file mode 100644
index 0000000..aed3ea6
--- /dev/null
+++ b/samples/Support7Demos/res/layout/list_view_activity.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<ListView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
diff --git a/samples/Support7Demos/res/layout/list_view_item.xml b/samples/Support7Demos/res/layout/list_view_item.xml
new file mode 100644
index 0000000..4a97d34
--- /dev/null
+++ b/samples/Support7Demos/res/layout/list_view_item.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="12dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/textAppearanceListItem" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/textAppearanceListItemSecondary" />
+
+</LinearLayout>
diff --git a/samples/Support7Demos/res/layout/popup_menu_activity.xml b/samples/Support7Demos/res/layout/popup_menu_activity.xml
index c3ed4f5..552a996 100644
--- a/samples/Support7Demos/res/layout/popup_menu_activity.xml
+++ b/samples/Support7Demos/res/layout/popup_menu_activity.xml
@@ -27,6 +27,16 @@
android:layout_centerHorizontal="true"
android:text="@string/popup_menu_button" />
+ <android.support.v7.widget.SwitchCompat
+ android:id="@+id/elevation_toggle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/test_button"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:checked="true"
+ android:text="@string/popup_default_elevation" />
+
<TextView
android:id="@+id/log"
android:layout_width="match_parent"
diff --git a/samples/Support7Demos/res/values-v21/styles.xml b/samples/Support7Demos/res/values-v21/styles.xml
index d5b1764..f75030d 100644
--- a/samples/Support7Demos/res/values-v21/styles.xml
+++ b/samples/Support7Demos/res/values-v21/styles.xml
@@ -24,4 +24,8 @@
<item name="windowActionModeOverlay">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
-</resources>
\ No newline at end of file
+
+ <style name="CustomPopupNoElevation" parent="@style/Widget.AppCompat.Light.PopupMenu">
+ <item name="android:popupElevation">2dp</item>
+ </style>
+</resources>
diff --git a/samples/Support7Demos/res/values/strings.xml b/samples/Support7Demos/res/values/strings.xml
index fe325a1..c5ffe46 100644
--- a/samples/Support7Demos/res/values/strings.xml
+++ b/samples/Support7Demos/res/values/strings.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
<string name="activity_sample_code">Support v7 Demos</string>
<!-- MediaRouter -->
@@ -68,6 +68,7 @@
<string name="appcompat_widgets_buttons">AppCompat/Widgets/Buttons</string>
<string name="appcompat_widgets_spinners">AppCompat/Widgets/Spinners</string>
<string name="appcompat_widgets_text_input">AppCompat/Widgets/Text Input</string>
+ <string name="appcompat_widgets_text_views">AppCompat/Widgets/Text Views</string>
<string name="action_bar_search">Search</string>
<string name="action_bar_add">Add</string>
@@ -217,6 +218,7 @@
<string name="popup_menu_activity">AppCompat/Popup menu</string>
<string name="popup_menu_summary">This activity illustrates the use of popup menus. The popup menu is shown by clicking the button above. The text area below logs various events.</string>
<string name="popup_menu_button">Show popup!</string>
+ <string name="popup_default_elevation">Use default elevation on popup</string>
<string name="popup_menu_highlight">Highlight</string>
<string name="popup_menu_highlight_description">Highlight description</string>
<string name="popup_menu_highlight_tooltip">Highlight tooltip</string>
@@ -230,9 +232,16 @@
<string name="popup_menu_share_circles">To my circles</string>
<string name="popup_menu_print">Print</string>
+ <string name="list_view_activity">AppCompat/ListView styling</string>
+
<string name="appcompat_vector_disabled">AnimatedVectorDrawableCompat does not work on devices running API v10 or below</string>
<string name="appcompat_vector_title">AppCompat/Integrations/AnimatedVectorDrawable</string>
<string name="night_mode">DAY</string>
+
+ <string name="text_plain_enabled">Plain enabled</string>
+ <string name="text_plain_disabled">Plain disabled</string>
+ <string name="text_link_enabled">With <a href="http://www.google.com">link</a> enabled</string>
+ <string name="text_link_disabled">With <a href="http://www.google.com">link</a> disabled</string>
</resources>
diff --git a/samples/Support7Demos/res/values/styles.xml b/samples/Support7Demos/res/values/styles.xml
index 3395c69..9c022d3 100644
--- a/samples/Support7Demos/res/values/styles.xml
+++ b/samples/Support7Demos/res/values/styles.xml
@@ -29,6 +29,10 @@
<item name="colorAccent">#ffff00</item>
</style>
+ <style name="Theme.Custom.TextLink">
+ <item name="android:textColorLink">@color/link_color</item>
+ </style>
+
<style name="Theme.SampleMediaRouter" parent="Theme.AppCompat">
<item name="colorPrimary">#fff44336</item>
<item name="colorPrimaryDark">#d32f2f</item>
@@ -52,4 +56,6 @@
<item name="windowActionModeOverlay">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
+
+ <style name="CustomPopupNoElevation" parent="@style/Widget.AppCompat.Light.PopupMenu" />
</resources>
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatWidgetsTextViews.java b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatWidgetsTextViews.java
new file mode 100644
index 0000000..1480509
--- /dev/null
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatWidgetsTextViews.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.supportv7.app;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+import com.example.android.supportv7.R;
+
+/**
+ * This demonstrates the styled {@link android.widget.TextView} widgets in AppCompat.
+ */
+public class AppCompatWidgetsTextViews extends AppCompatActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.appcompat_widgets_text_views);
+ }
+
+}
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/ListViewActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/ListViewActivity.java
new file mode 100644
index 0000000..cb0c2e2
--- /dev/null
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/ListViewActivity.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.supportv7.widget;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.example.android.supportv7.R;
+
+/**
+ * Sample activity for the demo of list item styles.
+ */
+public class ListViewActivity extends AppCompatActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.list_view_activity);
+ ListView listView = (ListView) findViewById(R.id.list_view);
+ listView.setAdapter(new BaseAdapter() {
+ @Override
+ public int getCount() {
+ return 100;
+ }
+
+ @Override
+ public Object getItem(int i) {
+ return null;
+ }
+
+ @Override
+ public long getItemId(int i) {
+ return i;
+ }
+
+ @Override
+ public View getView(int i, View view, ViewGroup viewGroup) {
+ // this is just a demo, so not using view holders
+ if (view == null) {
+ view = LayoutInflater.from(viewGroup.getContext()).inflate(
+ R.layout.list_view_item, viewGroup, false);
+ }
+ TextView title = (TextView) view.findViewById(R.id.title);
+ TextView subtitle = (TextView) view.findViewById(R.id.subtitle);
+ title.setText("Title for #" + i);
+ subtitle.setText("Subtitle for #" + i);
+ return view;
+ }
+ });
+ }
+}
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/PopupMenuActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/PopupMenuActivity.java
index bb7cd86..3adf840 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/PopupMenuActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/PopupMenuActivity.java
@@ -20,26 +20,23 @@
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.PopupMenu;
+import android.support.v7.widget.SwitchCompat;
+import android.view.Gravity;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
+
import com.example.android.supportv7.R;
import java.text.SimpleDateFormat;
import java.util.Date;
public class PopupMenuActivity extends AppCompatActivity {
- private ViewGroup mContainer;
-
private TextView mLog;
- private Button mButton;
-
- private PopupMenu mPopupMenu;
-
private SimpleDateFormat mDateFormat;
@Override
@@ -50,17 +47,29 @@
mDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
- mContainer = (ViewGroup) findViewById(R.id.container);
- mLog = (TextView) mContainer.findViewById(R.id.log);
- mButton = (Button) mContainer.findViewById(R.id.test_button);
+ final ViewGroup container = (ViewGroup) findViewById(R.id.container);
+ mLog = (TextView) container.findViewById(R.id.log);
- mButton.setOnClickListener(new View.OnClickListener() {
+ final SwitchCompat elevationToggle = (SwitchCompat) container.findViewById(
+ R.id.elevation_toggle);
+ final Button button = (Button) container.findViewById(R.id.test_button);
+ button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- mPopupMenu = new PopupMenu(mContainer.getContext(), mButton);
- final MenuInflater menuInflater = mPopupMenu.getMenuInflater();
- menuInflater.inflate(R.menu.popup_menu, mPopupMenu.getMenu());
- final MenuItem editItem = mPopupMenu.getMenu().findItem(R.id.action_edit);
+ // Do we need to use a custom style that removes elevation?
+ boolean useDefaultElevation = elevationToggle.isChecked();
+
+ PopupMenu popupMenu = null;
+ if (useDefaultElevation) {
+ popupMenu = new PopupMenu(container.getContext(), button);
+ } else {
+ popupMenu = new PopupMenu(container.getContext(), button, Gravity.NO_GRAVITY,
+ 0, R.style.CustomPopupNoElevation);
+ }
+
+ final MenuInflater menuInflater = popupMenu.getMenuInflater();
+ menuInflater.inflate(R.menu.popup_menu, popupMenu.getMenu());
+ final MenuItem editItem = popupMenu.getMenu().findItem(R.id.action_edit);
MenuItemCompat.setContentDescription(editItem,
getString(R.string.popup_menu_edit_description));
MenuItemCompat.setTooltipText(editItem,
@@ -68,7 +77,7 @@
// Register a listener to be notified when a menu item in our popup menu has
// been clicked.
- mPopupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
addToLog("Item '"+ item.getTitle() + "' clicked");
@@ -77,7 +86,7 @@
});
// Register a listener to be notified when our popup menu is dismissed.
- mPopupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {
+ popupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {
@Override
public void onDismiss(PopupMenu menu) {
addToLog("Popup menu dismissed");
@@ -85,7 +94,7 @@
});
// Show the popup menu
- mPopupMenu.show();
+ popupMenu.show();
}
});
}
diff --git a/samples/SupportTransitionDemos/AndroidManifest.xml b/samples/SupportTransitionDemos/AndroidManifest.xml
index 083f898..6a08d2c 100644
--- a/samples/SupportTransitionDemos/AndroidManifest.xml
+++ b/samples/SupportTransitionDemos/AndroidManifest.xml
@@ -72,5 +72,23 @@
<category android:name="com.example.android.support.transition.SAMPLE_CODE" />
</intent-filter>
</activity>
+
+ <activity android:name=".widget.ExplodeUsage"
+ android:label="@string/explode"
+ android:theme="@style/Theme.Transition">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.example.android.support.transition.SAMPLE_CODE" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".widget.ChangeClipBoundsUsage"
+ android:label="@string/clipBounds"
+ android:theme="@style/Theme.Transition">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.example.android.support.transition.SAMPLE_CODE" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/samples/SupportTransitionDemos/res/layout/clip_bounds.xml b/samples/SupportTransitionDemos/res/layout/clip_bounds.xml
new file mode 100644
index 0000000..2e98f32
--- /dev/null
+++ b/samples/SupportTransitionDemos/res/layout/clip_bounds.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/root"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <Button
+ android:id="@+id/toggle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/toggle"/>
+
+ <ImageView
+ android:id="@+id/photo"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="Photo"
+ android:src="@drawable/photo"/>
+
+</LinearLayout>
diff --git a/samples/SupportTransitionDemos/res/layout/explode.xml b/samples/SupportTransitionDemos/res/layout/explode.xml
new file mode 100644
index 0000000..a612910
--- /dev/null
+++ b/samples/SupportTransitionDemos/res/layout/explode.xml
@@ -0,0 +1,63 @@
+<?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:id="@+id/root"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <View
+ android:id="@+id/view_1"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_gravity="start|top"
+ android:layout_margin="64dp"
+ android:background="#E91E63"/>
+
+ <View
+ android:id="@+id/view_2"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_gravity="end|top"
+ android:layout_margin="64dp"
+ android:background="#673AB7"/>
+
+ <View
+ android:id="@+id/view_3"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_gravity="start|bottom"
+ android:layout_margin="64dp"
+ android:background="#009688"/>
+
+ <View
+ android:id="@+id/view_4"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_gravity="end|bottom"
+ android:layout_margin="64dp"
+ android:background="#FF5722"/>
+
+ <Button
+ android:id="@+id/toggle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_margin="64dp"
+ android:text="@string/toggle"/>
+
+</FrameLayout>
diff --git a/samples/SupportTransitionDemos/res/values/strings.xml b/samples/SupportTransitionDemos/res/values/strings.xml
index de69ee1..73ed883 100644
--- a/samples/SupportTransitionDemos/res/values/strings.xml
+++ b/samples/SupportTransitionDemos/res/values/strings.xml
@@ -20,6 +20,8 @@
<string name="custom">Custom Transition</string>
<string name="beginDelayed">Begin Delayed Transition</string>
<string name="arcMotion">Arc Motion</string>
+ <string name="explode">Explode</string>
+ <string name="clipBounds">Change Clip Bounds</string>
<string name="toggle">Toggle</string>
<string name="begin">Begin</string>
<string name="hello_world">Hello, world!</string>
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeClipBoundsUsage.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeClipBoundsUsage.java
new file mode 100644
index 0000000..8455404
--- /dev/null
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ChangeClipBoundsUsage.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.transition.widget;
+
+
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.support.transition.ChangeClipBounds;
+import android.support.transition.TransitionManager;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.example.android.support.transition.R;
+
+/**
+ * This demonstrates usage of {@link ChangeClipBounds}.
+ */
+public class ChangeClipBoundsUsage extends TransitionUsageBase {
+
+ private static final Rect BOUNDS = new Rect(20, 20, 100, 100);
+
+ private final ChangeClipBounds mChangeClipBounds = new ChangeClipBounds();
+ private ViewGroup mRoot;
+ private ImageView mPhoto;
+
+ @Override
+ int getLayoutResId() {
+ return R.layout.clip_bounds;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mRoot = (ViewGroup) findViewById(R.id.root);
+ mPhoto = (ImageView) findViewById(R.id.photo);
+ findViewById(R.id.toggle).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ toggle();
+ }
+ });
+ }
+
+ void toggle() {
+ TransitionManager.beginDelayedTransition(mRoot, mChangeClipBounds);
+ if (BOUNDS.equals(ViewCompat.getClipBounds(mPhoto))) {
+ ViewCompat.setClipBounds(mPhoto, null);
+ } else {
+ ViewCompat.setClipBounds(mPhoto, BOUNDS);
+ }
+ }
+
+}
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ExplodeUsage.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ExplodeUsage.java
new file mode 100644
index 0000000..c7a4fd1
--- /dev/null
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/ExplodeUsage.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.transition.widget;
+
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.support.transition.Explode;
+import android.support.transition.Transition;
+import android.support.transition.TransitionManager;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.example.android.support.transition.R;
+
+import java.util.ArrayList;
+
+/**
+ * This demonstrates usage of {@link Explode} Transition type.
+ */
+public class ExplodeUsage extends TransitionUsageBase {
+
+ private FrameLayout mRoot;
+ private final ArrayList<View> mViews = new ArrayList<>();
+ private final Explode mExplode = new Explode();
+
+ final Rect mRect = new Rect();
+
+ @Override
+ int getLayoutResId() {
+ return R.layout.explode;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mExplode.setEpicenterCallback(new Transition.EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ return mRect;
+ }
+ });
+ mRoot = (FrameLayout) findViewById(R.id.root);
+ if (mViews.isEmpty()) {
+ mViews.add(findViewById(R.id.view_1));
+ mViews.add(findViewById(R.id.view_2));
+ mViews.add(findViewById(R.id.view_3));
+ mViews.add(findViewById(R.id.view_4));
+ }
+ findViewById(R.id.toggle).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ v.getGlobalVisibleRect(mRect);
+ TransitionManager.beginDelayedTransition(mRoot, mExplode);
+ int vis = mViews.get(0).getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE;
+ for (View view : mViews) {
+ view.setVisibility(vis);
+ }
+ }
+ });
+ }
+
+}
diff --git a/transition/AndroidManifest.xml b/transition/AndroidManifest.xml
index 1059f63..309b695 100644
--- a/transition/AndroidManifest.xml
+++ b/transition/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.transition">
<uses-sdk android:minSdkVersion="14"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/transition/res/values/ids.xml b/transition/res/values/ids.xml
index 071cf2a..5fc44dc 100644
--- a/transition/res/values/ids.xml
+++ b/transition/res/values/ids.xml
@@ -18,4 +18,5 @@
<item name="transition_scene_layoutid_cache" type="id"/>
<item name="transition_current_scene" type="id"/>
<item name="transition_layout_save" type="id"/>
+ <item name="transition_position" type="id"/>
</resources>
diff --git a/transition/src/android/support/transition/ChangeClipBounds.java b/transition/src/android/support/transition/ChangeClipBounds.java
new file mode 100644
index 0000000..7fd1023
--- /dev/null
+++ b/transition/src/android/support/transition/ChangeClipBounds.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * ChangeClipBounds captures the {@link android.view.View#getClipBounds()} before and after the
+ * scene change and animates those changes during the transition.
+ *
+ * <p>Prior to API 18 this does nothing.</p>
+ */
+public class ChangeClipBounds extends Transition {
+
+ private static final String PROPNAME_CLIP = "android:clipBounds:clip";
+ private static final String PROPNAME_BOUNDS = "android:clipBounds:bounds";
+
+ private static final String[] sTransitionProperties = {
+ PROPNAME_CLIP,
+ };
+
+ @Override
+ public String[] getTransitionProperties() {
+ return sTransitionProperties;
+ }
+
+ public ChangeClipBounds() {
+ }
+
+ public ChangeClipBounds(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ private void captureValues(TransitionValues values) {
+ View view = values.view;
+ if (view.getVisibility() == View.GONE) {
+ return;
+ }
+
+ Rect clip = ViewCompat.getClipBounds(view);
+ values.values.put(PROPNAME_CLIP, clip);
+ if (clip == null) {
+ Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+ values.values.put(PROPNAME_BOUNDS, bounds);
+ }
+ }
+
+ @Override
+ public void captureStartValues(@NonNull TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(@NonNull TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public Animator createAnimator(@NonNull final ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null
+ || !startValues.values.containsKey(PROPNAME_CLIP)
+ || !endValues.values.containsKey(PROPNAME_CLIP)) {
+ return null;
+ }
+ Rect start = (Rect) startValues.values.get(PROPNAME_CLIP);
+ Rect end = (Rect) endValues.values.get(PROPNAME_CLIP);
+ if (start == null && end == null) {
+ return null; // No animation required since there is no clip.
+ }
+
+ if (start == null) {
+ start = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+ } else if (end == null) {
+ end = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ }
+ if (start.equals(end)) {
+ return null;
+ }
+
+ ViewCompat.setClipBounds(endValues.view, start);
+ RectEvaluator evaluator = new RectEvaluator(new Rect());
+ return ObjectAnimator.ofObject(endValues.view, ViewUtils.CLIP_BOUNDS, evaluator,
+ start, end);
+ }
+}
diff --git a/transition/src/android/support/transition/CircularPropagation.java b/transition/src/android/support/transition/CircularPropagation.java
new file mode 100644
index 0000000..ced4223
--- /dev/null
+++ b/transition/src/android/support/transition/CircularPropagation.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.transition;
+
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A propagation that varies with the distance to the epicenter of the Transition
+ * or center of the scene if no epicenter exists. When a View is visible in the
+ * start of the transition, Views farther from the epicenter will transition
+ * sooner than Views closer to the epicenter. When a View is not in the start
+ * of the transition or is not visible at the start of the transition, it will
+ * transition sooner when closer to the epicenter and later when farther from
+ * the epicenter. This is the default TransitionPropagation used with
+ * {@link Explode}.
+ */
+public class CircularPropagation extends VisibilityPropagation {
+
+ private float mPropagationSpeed = 3.0f;
+
+ /**
+ * Sets the speed at which transition propagation happens, relative to the duration of the
+ * Transition. A <code>propagationSpeed</code> of 1 means that a View centered farthest from
+ * the epicenter and View centered at the epicenter will have a difference
+ * in start delay of approximately the duration of the Transition. A speed of 2 means the
+ * start delay difference will be approximately half of the duration of the transition. A
+ * value of 0 is illegal, but negative values will invert the propagation.
+ *
+ * @param propagationSpeed The speed at which propagation occurs, relative to the duration
+ * of the transition. A speed of 4 means it works 4 times as fast
+ * as the duration of the transition. May not be 0.
+ */
+ public void setPropagationSpeed(float propagationSpeed) {
+ if (propagationSpeed == 0) {
+ throw new IllegalArgumentException("propagationSpeed may not be 0");
+ }
+ mPropagationSpeed = propagationSpeed;
+ }
+
+ @Override
+ public long getStartDelay(ViewGroup sceneRoot, Transition transition,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (startValues == null && endValues == null) {
+ return 0;
+ }
+ int directionMultiplier = 1;
+ TransitionValues positionValues;
+ if (endValues == null || getViewVisibility(startValues) == View.VISIBLE) {
+ positionValues = startValues;
+ directionMultiplier = -1;
+ } else {
+ positionValues = endValues;
+ }
+
+ int viewCenterX = getViewX(positionValues);
+ int viewCenterY = getViewY(positionValues);
+
+ Rect epicenter = transition.getEpicenter();
+ int epicenterX;
+ int epicenterY;
+ if (epicenter != null) {
+ epicenterX = epicenter.centerX();
+ epicenterY = epicenter.centerY();
+ } else {
+ int[] loc = new int[2];
+ sceneRoot.getLocationOnScreen(loc);
+ epicenterX = Math.round(loc[0] + (sceneRoot.getWidth() / 2)
+ + sceneRoot.getTranslationX());
+ epicenterY = Math.round(loc[1] + (sceneRoot.getHeight() / 2)
+ + sceneRoot.getTranslationY());
+ }
+ float distance = distance(viewCenterX, viewCenterY, epicenterX, epicenterY);
+ float maxDistance = distance(0, 0, sceneRoot.getWidth(), sceneRoot.getHeight());
+ float distanceFraction = distance / maxDistance;
+
+ long duration = transition.getDuration();
+ if (duration < 0) {
+ duration = 300;
+ }
+
+ return Math.round(duration * directionMultiplier / mPropagationSpeed * distanceFraction);
+ }
+
+ private static float distance(float x1, float y1, float x2, float y2) {
+ float x = x2 - x1;
+ float y = y2 - y1;
+ return (float) Math.sqrt((x * x) + (y * y));
+ }
+
+}
diff --git a/transition/src/android/support/transition/Explode.java b/transition/src/android/support/transition/Explode.java
new file mode 100644
index 0000000..38d0fb7
--- /dev/null
+++ b/transition/src/android/support/transition/Explode.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+
+/**
+ * This transition tracks changes to the visibility of target views in the
+ * start and end scenes and moves views in or out from the edges of the
+ * scene. Visibility is determined by both the
+ * {@link View#setVisibility(int)} state of the view as well as whether it
+ * is parented in the current view hierarchy. Disappearing Views are
+ * limited as described in {@link Visibility#onDisappear(android.view.ViewGroup,
+ * TransitionValues, int, TransitionValues, int)}.
+ * <p>Views move away from the focal View or the center of the Scene if
+ * no epicenter was provided.</p>
+ */
+public class Explode extends Visibility {
+
+ private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
+ private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
+ private static final String PROPNAME_SCREEN_BOUNDS = "android:explode:screenBounds";
+
+ private int[] mTempLoc = new int[2];
+
+ public Explode() {
+ setPropagation(new CircularPropagation());
+ }
+
+ public Explode(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setPropagation(new CircularPropagation());
+ }
+
+ private void captureValues(TransitionValues transitionValues) {
+ View view = transitionValues.view;
+ view.getLocationOnScreen(mTempLoc);
+ int left = mTempLoc[0];
+ int top = mTempLoc[1];
+ int right = left + view.getWidth();
+ int bottom = top + view.getHeight();
+ transitionValues.values.put(PROPNAME_SCREEN_BOUNDS, new Rect(left, top, right, bottom));
+ }
+
+ @Override
+ public void captureStartValues(@NonNull TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(@NonNull TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public Animator onAppear(ViewGroup sceneRoot, View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (endValues == null) {
+ return null;
+ }
+ Rect bounds = (Rect) endValues.values.get(PROPNAME_SCREEN_BOUNDS);
+ float endX = view.getTranslationX();
+ float endY = view.getTranslationY();
+ calculateOut(sceneRoot, bounds, mTempLoc);
+ float startX = endX + mTempLoc[0];
+ float startY = endY + mTempLoc[1];
+
+ return TranslationAnimationCreator.createAnimation(view, endValues, bounds.left, bounds.top,
+ startX, startY, endX, endY, sDecelerate);
+ }
+
+ @Override
+ public Animator onDisappear(ViewGroup sceneRoot, View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (startValues == null) {
+ return null;
+ }
+ Rect bounds = (Rect) startValues.values.get(PROPNAME_SCREEN_BOUNDS);
+ int viewPosX = bounds.left;
+ int viewPosY = bounds.top;
+ float startX = view.getTranslationX();
+ float startY = view.getTranslationY();
+ float endX = startX;
+ float endY = startY;
+ int[] interruptedPosition = (int[]) startValues.view.getTag(R.id.transition_position);
+ if (interruptedPosition != null) {
+ // We want to have the end position relative to the interrupted position, not
+ // the position it was supposed to start at.
+ endX += interruptedPosition[0] - bounds.left;
+ endY += interruptedPosition[1] - bounds.top;
+ bounds.offsetTo(interruptedPosition[0], interruptedPosition[1]);
+ }
+ calculateOut(sceneRoot, bounds, mTempLoc);
+ endX += mTempLoc[0];
+ endY += mTempLoc[1];
+
+ return TranslationAnimationCreator.createAnimation(view, startValues,
+ viewPosX, viewPosY, startX, startY, endX, endY, sAccelerate);
+ }
+
+ private void calculateOut(View sceneRoot, Rect bounds, int[] outVector) {
+ sceneRoot.getLocationOnScreen(mTempLoc);
+ int sceneRootX = mTempLoc[0];
+ int sceneRootY = mTempLoc[1];
+ int focalX;
+ int focalY;
+
+ Rect epicenter = getEpicenter();
+ if (epicenter == null) {
+ focalX = sceneRootX + (sceneRoot.getWidth() / 2)
+ + Math.round(sceneRoot.getTranslationX());
+ focalY = sceneRootY + (sceneRoot.getHeight() / 2)
+ + Math.round(sceneRoot.getTranslationY());
+ } else {
+ focalX = epicenter.centerX();
+ focalY = epicenter.centerY();
+ }
+
+ int centerX = bounds.centerX();
+ int centerY = bounds.centerY();
+ float xVector = centerX - focalX;
+ float yVector = centerY - focalY;
+
+ if (xVector == 0 && yVector == 0) {
+ // Random direction when View is centered on focal View.
+ xVector = (float) (Math.random() * 2) - 1;
+ yVector = (float) (Math.random() * 2) - 1;
+ }
+ float vectorSize = calculateDistance(xVector, yVector);
+ xVector /= vectorSize;
+ yVector /= vectorSize;
+
+ float maxDistance =
+ calculateMaxDistance(sceneRoot, focalX - sceneRootX, focalY - sceneRootY);
+
+ outVector[0] = Math.round(maxDistance * xVector);
+ outVector[1] = Math.round(maxDistance * yVector);
+ }
+
+ private static float calculateMaxDistance(View sceneRoot, int focalX, int focalY) {
+ int maxX = Math.max(focalX, sceneRoot.getWidth() - focalX);
+ int maxY = Math.max(focalY, sceneRoot.getHeight() - focalY);
+ return calculateDistance(maxX, maxY);
+ }
+
+ private static float calculateDistance(float x, float y) {
+ return (float) Math.sqrt((x * x) + (y * y));
+ }
+
+}
diff --git a/transition/src/android/support/transition/Transition.java b/transition/src/android/support/transition/Transition.java
index 17b23ab..807e353 100644
--- a/transition/src/android/support/transition/Transition.java
+++ b/transition/src/android/support/transition/Transition.java
@@ -25,6 +25,7 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Path;
+import android.graphics.Rect;
import android.support.annotation.IdRes;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
@@ -37,6 +38,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.view.InflateException;
import android.view.SurfaceView;
import android.view.TextureView;
@@ -234,6 +236,13 @@
// to be run in runAnimators()
private ArrayList<Animator> mAnimators = new ArrayList<>();
+ // The function for calculating the Animation start delay.
+ TransitionPropagation mPropagation;
+
+ // The rectangular region for Transitions like Explode and TransitionPropagations
+ // like CircularPropagation
+ private EpicenterCallback mEpicenterCallback;
+
// For Fragment shared element transitions, linking views explicitly by mismatching
// transitionNames.
private ArrayMap<String, String> mNameOverrides;
@@ -694,6 +703,8 @@
Log.d(LOG_TAG, "createAnimators() for " + this);
}
ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
+ long minStartDelay = Long.MAX_VALUE;
+ SparseIntArray startDelays = new SparseIntArray();
int startValuesListCount = startValuesList.size();
for (int i = 0; i < startValuesListCount; ++i) {
TransitionValues start = startValuesList.get(i);
@@ -764,6 +775,11 @@
view = start.view;
}
if (animator != null) {
+ if (mPropagation != null) {
+ long delay = mPropagation.getStartDelay(sceneRoot, this, start, end);
+ startDelays.put(mAnimators.size(), (int) delay);
+ minStartDelay = Math.min(delay, minStartDelay);
+ }
AnimationInfo info = new AnimationInfo(view, getName(), this,
ViewUtils.getWindowId(sceneRoot), infoValues);
runningAnimators.put(animator, info);
@@ -772,6 +788,14 @@
}
}
}
+ if (minStartDelay != 0) {
+ for (int i = 0; i < startDelays.size(); i++) {
+ int index = startDelays.keyAt(i);
+ Animator animator = mAnimators.get(index);
+ long delay = startDelays.valueAt(i) - minStartDelay + animator.getStartDelay();
+ animator.setStartDelay(delay);
+ }
+ }
}
/**
@@ -1455,6 +1479,7 @@
captureEndValues(values);
}
values.mTargetedTransitions.add(this);
+ capturePropagationValues(values);
if (start) {
addViewValues(mStartValues, view, values);
} else {
@@ -1472,6 +1497,7 @@
captureEndValues(values);
}
values.mTargetedTransitions.add(this);
+ capturePropagationValues(values);
if (start) {
addViewValues(mStartValues, view, values);
} else {
@@ -1594,6 +1620,7 @@
captureEndValues(values);
}
values.mTargetedTransitions.add(this);
+ capturePropagationValues(values);
if (start) {
addViewValues(mStartValues, view, values);
} else {
@@ -1993,6 +2020,7 @@
}
/**
+ * <<<<<<< HEAD
* Sets the algorithm used to calculate two-dimensional interpolation.
* <p>
* Transitions such as {@link android.transition.ChangeBounds} move Views, typically
@@ -2029,6 +2057,106 @@
return mPathMotion;
}
+ /**
+ * Sets the callback to use to find the epicenter of a Transition. A null value indicates
+ * that there is no epicenter in the Transition and onGetEpicenter() will return null.
+ * Transitions like {@link android.transition.Explode} use a point or Rect to orient
+ * the direction of travel. This is called the epicenter of the Transition and is
+ * typically centered on a touched View. The
+ * {@link android.transition.Transition.EpicenterCallback} allows a Transition to
+ * dynamically retrieve the epicenter during a Transition.
+ *
+ * @param epicenterCallback The callback to use to find the epicenter of the Transition.
+ */
+ public void setEpicenterCallback(@Nullable EpicenterCallback epicenterCallback) {
+ mEpicenterCallback = epicenterCallback;
+ }
+
+ /**
+ * Returns the callback used to find the epicenter of the Transition.
+ * Transitions like {@link android.transition.Explode} use a point or Rect to orient
+ * the direction of travel. This is called the epicenter of the Transition and is
+ * typically centered on a touched View. The
+ * {@link android.transition.Transition.EpicenterCallback} allows a Transition to
+ * dynamically retrieve the epicenter during a Transition.
+ *
+ * @return the callback used to find the epicenter of the Transition.
+ */
+ @Nullable
+ public EpicenterCallback getEpicenterCallback() {
+ return mEpicenterCallback;
+ }
+
+ /**
+ * Returns the epicenter as specified by the
+ * {@link android.transition.Transition.EpicenterCallback} or null if no callback exists.
+ *
+ * @return the epicenter as specified by the
+ * {@link android.transition.Transition.EpicenterCallback} or null if no callback exists.
+ * @see #setEpicenterCallback(EpicenterCallback)
+ */
+ @Nullable
+ public Rect getEpicenter() {
+ if (mEpicenterCallback == null) {
+ return null;
+ }
+ return mEpicenterCallback.onGetEpicenter(this);
+ }
+
+ /**
+ * Sets the method for determining Animator start delays.
+ * When a Transition affects several Views like {@link android.transition.Explode} or
+ * {@link android.transition.Slide}, there may be a desire to have a "wave-front" effect
+ * such that the Animator start delay depends on position of the View. The
+ * TransitionPropagation specifies how the start delays are calculated.
+ *
+ * @param transitionPropagation The class used to determine the start delay of
+ * Animators created by this Transition. A null value
+ * indicates that no delay should be used.
+ */
+ public void setPropagation(TransitionPropagation transitionPropagation) {
+ mPropagation = transitionPropagation;
+ }
+
+ /**
+ * Returns the {@link android.transition.TransitionPropagation} used to calculate Animator
+ * start
+ * delays.
+ * When a Transition affects several Views like {@link android.transition.Explode} or
+ * {@link android.transition.Slide}, there may be a desire to have a "wave-front" effect
+ * such that the Animator start delay depends on position of the View. The
+ * TransitionPropagation specifies how the start delays are calculated.
+ *
+ * @return the {@link android.transition.TransitionPropagation} used to calculate Animator start
+ * delays. This is null by default.
+ */
+ public TransitionPropagation getPropagation() {
+ return mPropagation;
+ }
+
+ /**
+ * Captures TransitionPropagation values for the given view and the
+ * hierarchy underneath it.
+ */
+ void capturePropagationValues(TransitionValues transitionValues) {
+ if (mPropagation != null && !transitionValues.values.isEmpty()) {
+ String[] propertyNames = mPropagation.getPropagationProperties();
+ if (propertyNames == null) {
+ return;
+ }
+ boolean containsAll = true;
+ for (int i = 0; i < propertyNames.length; i++) {
+ if (!transitionValues.values.containsKey(propertyNames[i])) {
+ containsAll = false;
+ break;
+ }
+ }
+ if (!containsAll) {
+ mPropagation.captureValues(transitionValues);
+ }
+ }
+ }
+
Transition setSceneRoot(ViewGroup sceneRoot) {
mSceneRoot = sceneRoot;
return this;
@@ -2273,4 +2401,29 @@
}
}
+ /**
+ * Class to get the epicenter of Transition. Use
+ * {@link #setEpicenterCallback(EpicenterCallback)} to set the callback used to calculate the
+ * epicenter of the Transition. Override {@link #getEpicenter()} to return the rectangular
+ * region in screen coordinates of the epicenter of the transition.
+ *
+ * @see #setEpicenterCallback(EpicenterCallback)
+ */
+ public abstract static class EpicenterCallback {
+
+ /**
+ * Implementers must override to return the epicenter of the Transition in screen
+ * coordinates. Transitions like {@link android.transition.Explode} depend upon
+ * an epicenter for the Transition. In Explode, Views move toward or away from the
+ * center of the epicenter Rect along the vector between the epicenter and the center
+ * of the View appearing and disappearing. Some Transitions, such as
+ * {@link android.transition.Fade} pay no attention to the epicenter.
+ *
+ * @param transition The transition for which the epicenter applies.
+ * @return The Rect region of the epicenter of <code>transition</code> or null if
+ * there is no epicenter.
+ */
+ public abstract Rect onGetEpicenter(Transition transition);
+ }
+
}
diff --git a/transition/src/android/support/transition/TransitionInflater.java b/transition/src/android/support/transition/TransitionInflater.java
index 9bdfdf7..e1da808 100644
--- a/transition/src/android/support/transition/TransitionInflater.java
+++ b/transition/src/android/support/transition/TransitionInflater.java
@@ -134,6 +134,10 @@
transition = new Fade(mContext, attrs);
} else if ("changeBounds".equals(name)) {
transition = new ChangeBounds(mContext, attrs);
+ } else if ("explode".equals(name)) {
+ transition = new Explode(mContext, attrs);
+ } else if ("changeClipBounds".equals(name)) {
+ transition = new ChangeClipBounds(mContext, attrs);
} else if ("autoTransition".equals(name)) {
transition = new AutoTransition(mContext, attrs);
} else if ("transitionSet".equals(name)) {
diff --git a/transition/src/android/support/transition/TransitionPropagation.java b/transition/src/android/support/transition/TransitionPropagation.java
new file mode 100644
index 0000000..76ce9b2
--- /dev/null
+++ b/transition/src/android/support/transition/TransitionPropagation.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.transition;
+
+import android.view.ViewGroup;
+
+/**
+ * Extend <code>TransitionPropagation</code> to customize start delays for Animators created
+ * in {@link Transition#createAnimator(ViewGroup, TransitionValues, TransitionValues)}.
+ * A Transition such as {@link Explode} defaults to using {@link CircularPropagation} and Views
+ * closer to the epicenter will move out of the scene later and into the scene sooner than Views
+ * farther from the epicenter, giving the appearance of inertia. With no TransitionPropagation, all
+ * Views will react simultaneously to the start of the transition.
+ *
+ * @see Transition#setPropagation(TransitionPropagation)
+ * @see Transition#getEpicenter()
+ */
+public abstract class TransitionPropagation {
+
+ /**
+ * Called by Transition to alter the Animator start delay. All start delays will be adjusted
+ * such that the minimum becomes zero.
+ *
+ * @param sceneRoot The root of the View hierarchy running the transition.
+ * @param transition The transition that created the Animator
+ * @param startValues The values for a specific target in the start scene.
+ * @param endValues The values for the target in the end scene.
+ * @return A start delay to use with the Animator created by <code>transition</code>. The
+ * delay will be offset by the minimum delay of all <code>TransitionPropagation</code>s
+ * used in the Transition so that the smallest delay will be 0. Returned values may be
+ * negative.
+ */
+ public abstract long getStartDelay(ViewGroup sceneRoot, Transition transition,
+ TransitionValues startValues, TransitionValues endValues);
+
+ /**
+ * Captures the values in the start or end scene for the properties that this
+ * transition propagation monitors. These values are then passed as the startValues
+ * or endValues structure in a later call to
+ * {@link #getStartDelay(ViewGroup, Transition, TransitionValues, TransitionValues)}.
+ * The main concern for an implementation is what the
+ * properties are that the transition cares about and what the values are
+ * for all of those properties. The start and end values will be compared
+ * later during the
+ * {@link #getStartDelay(ViewGroup, Transition, TransitionValues, TransitionValues)}.
+ * method to determine the start delay.
+ *
+ * <p>Subclasses must implement this method. The method should only be called by the
+ * transition system; it is not intended to be called from external classes.</p>
+ *
+ * @param transitionValues The holder for any values that the Transition
+ * wishes to store. Values are stored in the <code>values</code> field
+ * of this TransitionValues object and are keyed from
+ * a String value. For example, to store a view's rotation value,
+ * a transition might call
+ * <code>transitionValues.values.put("appname:transitionname:rotation",
+ * view.getRotation())</code>. The target view will already be stored
+ * in
+ * the transitionValues structure when this method is called.
+ */
+ public abstract void captureValues(TransitionValues transitionValues);
+
+ /**
+ * Returns the set of property names stored in the {@link TransitionValues}
+ * object passed into {@link #captureValues(TransitionValues)} that
+ * this transition propagation cares about for the purposes of preventing
+ * duplicate capturing of property values.
+ *
+ * <p>A <code>TransitionPropagation</code> must override this method to prevent
+ * duplicate capturing of values and must contain at least one </p>
+ *
+ * @return An array of property names as described in the class documentation for
+ * {@link TransitionValues}.
+ */
+ public abstract String[] getPropagationProperties();
+
+}
diff --git a/transition/src/android/support/transition/TransitionSet.java b/transition/src/android/support/transition/TransitionSet.java
index f1d83e0..df63414 100644
--- a/transition/src/android/support/transition/TransitionSet.java
+++ b/transition/src/android/support/transition/TransitionSet.java
@@ -488,6 +488,15 @@
}
}
+ @Override
+ void capturePropagationValues(TransitionValues transitionValues) {
+ super.capturePropagationValues(transitionValues);
+ int numTransitions = mTransitions.size();
+ for (int i = 0; i < numTransitions; ++i) {
+ mTransitions.get(i).capturePropagationValues(transitionValues);
+ }
+ }
+
/** @hide */
@RestrictTo(LIBRARY_GROUP)
@Override
@@ -541,6 +550,15 @@
}
@Override
+ public void setEpicenterCallback(EpicenterCallback epicenterCallback) {
+ super.setEpicenterCallback(epicenterCallback);
+ int numTransitions = mTransitions.size();
+ for (int i = 0; i < numTransitions; ++i) {
+ mTransitions.get(i).setEpicenterCallback(epicenterCallback);
+ }
+ }
+
+ @Override
String toString(String indent) {
String result = super.toString(indent);
for (int i = 0; i < mTransitions.size(); ++i) {
diff --git a/transition/src/android/support/transition/TranslationAnimationCreator.java b/transition/src/android/support/transition/TranslationAnimationCreator.java
new file mode 100644
index 0000000..7f2fd1d
--- /dev/null
+++ b/transition/src/android/support/transition/TranslationAnimationCreator.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
+import android.view.View;
+
+/**
+ * This class is used by Slide and Explode to create an animator that goes from the start
+ * position to the end position. It takes into account the canceled position so that it
+ * will not blink out or shift suddenly when the transition is interrupted.
+ */
+class TranslationAnimationCreator {
+
+ /**
+ * Creates an animator that can be used for x and/or y translations. When interrupted,
+ * it sets a tag to keep track of the position so that it may be continued from position.
+ *
+ * @param view The view being moved. This may be in the overlay for onDisappear.
+ * @param values The values containing the view in the view hierarchy.
+ * @param viewPosX The x screen coordinate of view
+ * @param viewPosY The y screen coordinate of view
+ * @param startX The start translation x of view
+ * @param startY The start translation y of view
+ * @param endX The end translation x of view
+ * @param endY The end translation y of view
+ * @param interpolator The interpolator to use with this animator.
+ * @return An animator that moves from (startX, startY) to (endX, endY) unless there was
+ * a previous interruption, in which case it moves from the current position to (endX, endY).
+ */
+ static Animator createAnimation(View view, TransitionValues values, int viewPosX, int viewPosY,
+ float startX, float startY, float endX, float endY, TimeInterpolator interpolator) {
+ float terminalX = view.getTranslationX();
+ float terminalY = view.getTranslationY();
+ int[] startPosition = (int[]) values.view.getTag(R.id.transition_position);
+ if (startPosition != null) {
+ startX = startPosition[0] - viewPosX + terminalX;
+ startY = startPosition[1] - viewPosY + terminalY;
+ }
+ // Initial position is at translation startX, startY, so position is offset by that amount
+ int startPosX = viewPosX + Math.round(startX - terminalX);
+ int startPosY = viewPosY + Math.round(startY - terminalY);
+
+ view.setTranslationX(startX);
+ view.setTranslationY(startY);
+ if (startX == endX && startY == endY) {
+ return null;
+ }
+ ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(view,
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_X, startX, endX),
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, startY, endY));
+
+ TransitionPositionListener listener = new TransitionPositionListener(view, values.view,
+ startPosX, startPosY, terminalX, terminalY);
+ anim.addListener(listener);
+ AnimatorUtils.addPauseListener(anim, listener);
+ anim.setInterpolator(interpolator);
+ return anim;
+ }
+
+ private static class TransitionPositionListener extends AnimatorListenerAdapter {
+
+ private final View mViewInHierarchy;
+ private final View mMovingView;
+ private final int mStartX;
+ private final int mStartY;
+ private int[] mTransitionPosition;
+ private float mPausedX;
+ private float mPausedY;
+ private final float mTerminalX;
+ private final float mTerminalY;
+
+ private TransitionPositionListener(View movingView, View viewInHierarchy,
+ int startX, int startY, float terminalX, float terminalY) {
+ mMovingView = movingView;
+ mViewInHierarchy = viewInHierarchy;
+ mStartX = startX - Math.round(mMovingView.getTranslationX());
+ mStartY = startY - Math.round(mMovingView.getTranslationY());
+ mTerminalX = terminalX;
+ mTerminalY = terminalY;
+ mTransitionPosition = (int[]) mViewInHierarchy.getTag(R.id.transition_position);
+ if (mTransitionPosition != null) {
+ mViewInHierarchy.setTag(R.id.transition_position, null);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (mTransitionPosition == null) {
+ mTransitionPosition = new int[2];
+ }
+ mTransitionPosition[0] = Math.round(mStartX + mMovingView.getTranslationX());
+ mTransitionPosition[1] = Math.round(mStartY + mMovingView.getTranslationY());
+ mViewInHierarchy.setTag(R.id.transition_position, mTransitionPosition);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ mMovingView.setTranslationX(mTerminalX);
+ mMovingView.setTranslationY(mTerminalY);
+ }
+
+ @Override
+ public void onAnimationPause(Animator animator) {
+ mPausedX = mMovingView.getTranslationX();
+ mPausedY = mMovingView.getTranslationY();
+ mMovingView.setTranslationX(mTerminalX);
+ mMovingView.setTranslationY(mTerminalY);
+ }
+
+ @Override
+ public void onAnimationResume(Animator animator) {
+ mMovingView.setTranslationX(mPausedX);
+ mMovingView.setTranslationY(mPausedY);
+ }
+ }
+
+}
diff --git a/transition/src/android/support/transition/Visibility.java b/transition/src/android/support/transition/Visibility.java
index da56f91..0c6c9d9 100644
--- a/transition/src/android/support/transition/Visibility.java
+++ b/transition/src/android/support/transition/Visibility.java
@@ -50,7 +50,7 @@
*/
public abstract class Visibility extends Transition {
- private static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
+ static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
private static final String PROPNAME_PARENT = "android:visibility:parent";
private static final String PROPNAME_SCREEN_LOCATION = "android:visibility:screenLocation";
diff --git a/transition/src/android/support/transition/VisibilityPropagation.java b/transition/src/android/support/transition/VisibilityPropagation.java
new file mode 100644
index 0000000..c9ada2e
--- /dev/null
+++ b/transition/src/android/support/transition/VisibilityPropagation.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.transition;
+
+import android.view.View;
+
+/**
+ * Base class for <code>TransitionPropagation</code>s that care about
+ * View Visibility and the center position of the View.
+ */
+public abstract class VisibilityPropagation extends TransitionPropagation {
+
+ /**
+ * The property key used for {@link android.view.View#getVisibility()}.
+ */
+ private static final String PROPNAME_VISIBILITY = "android:visibilityPropagation:visibility";
+
+ /**
+ * The property key used for the center of the View in screen coordinates. This is an
+ * int[2] with the index 0 taking the x coordinate and index 1 taking the y coordinate.
+ */
+ private static final String PROPNAME_VIEW_CENTER = "android:visibilityPropagation:center";
+
+ private static final String[] VISIBILITY_PROPAGATION_VALUES = {
+ PROPNAME_VISIBILITY,
+ PROPNAME_VIEW_CENTER,
+ };
+
+ @Override
+ public void captureValues(TransitionValues values) {
+ View view = values.view;
+ Integer visibility = (Integer) values.values.get(Visibility.PROPNAME_VISIBILITY);
+ if (visibility == null) {
+ visibility = view.getVisibility();
+ }
+ values.values.put(PROPNAME_VISIBILITY, visibility);
+ int[] loc = new int[2];
+ view.getLocationOnScreen(loc);
+ loc[0] += Math.round(view.getTranslationX());
+ loc[0] += view.getWidth() / 2;
+ loc[1] += Math.round(view.getTranslationY());
+ loc[1] += view.getHeight() / 2;
+ values.values.put(PROPNAME_VIEW_CENTER, loc);
+ }
+
+ @Override
+ public String[] getPropagationProperties() {
+ return VISIBILITY_PROPAGATION_VALUES;
+ }
+
+ /**
+ * Returns {@link android.view.View#getVisibility()} for the View at the time the values
+ * were captured.
+ * @param values The TransitionValues captured at the start or end of the Transition.
+ * @return {@link android.view.View#getVisibility()} for the View at the time the values
+ * were captured.
+ */
+ public int getViewVisibility(TransitionValues values) {
+ if (values == null) {
+ return View.GONE;
+ }
+ Integer visibility = (Integer) values.values.get(PROPNAME_VISIBILITY);
+ if (visibility == null) {
+ return View.GONE;
+ }
+ return visibility;
+ }
+
+ /**
+ * Returns the View's center x coordinate, relative to the screen, at the time the values
+ * were captured.
+ * @param values The TransitionValues captured at the start or end of the Transition.
+ * @return the View's center x coordinate, relative to the screen, at the time the values
+ * were captured.
+ */
+ public int getViewX(TransitionValues values) {
+ return getViewCoordinate(values, 0);
+ }
+
+ /**
+ * Returns the View's center y coordinate, relative to the screen, at the time the values
+ * were captured.
+ * @param values The TransitionValues captured at the start or end of the Transition.
+ * @return the View's center y coordinate, relative to the screen, at the time the values
+ * were captured.
+ */
+ public int getViewY(TransitionValues values) {
+ return getViewCoordinate(values, 1);
+ }
+
+ private static int getViewCoordinate(TransitionValues values, int coordinateIndex) {
+ if (values == null) {
+ return -1;
+ }
+
+ int[] coordinates = (int[]) values.values.get(PROPNAME_VIEW_CENTER);
+ if (coordinates == null) {
+ return -1;
+ }
+
+ return coordinates[coordinateIndex];
+ }
+
+}
diff --git a/transition/tests/res/layout/scene1.xml b/transition/tests/res/layout/scene1.xml
index 4d83ebd..07e88b8 100644
--- a/transition/tests/res/layout/scene1.xml
+++ b/transition/tests/res/layout/scene1.xml
@@ -14,30 +14,29 @@
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="match_parent"
- android:id="@+id/container">
-
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/holder"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:transitionName="holder">
<View
- android:id="@+id/view0"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:layout_gravity="end|top"
- android:background="#f00"/>
-
+ android:id="@+id/redSquare"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#F00"
+ android:transitionName="red"/>
<View
- android:id="@+id/view1"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:layout_gravity="center"
- android:background="#0f0"/>
-
- <View
- android:id="@+id/view2"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:layout_gravity="end|bottom"
- android:background="#00f"/>
-
-</FrameLayout>
+ android:id="@+id/greenSquare"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:layout_below="@id/redSquare"
+ android:background="#0F0"
+ android:transitionName="green"/>
+ <TextView
+ android:id="@+id/hello"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/hello"
+ android:transitionName="hello"/>
+</RelativeLayout>
diff --git a/transition/tests/res/layout/scene10.xml b/transition/tests/res/layout/scene10.xml
new file mode 100644
index 0000000..7e4c804
--- /dev/null
+++ b/transition/tests/res/layout/scene10.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:transitionGroup="false"
+ android:transitionName="holder"
+ android:id="@+id/holder">
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#F00"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:id="@+id/redSquare"/>
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#0F0"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:id="@+id/greenSquare"/>
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#00F"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentBottom="true"
+ android:id="@+id/blueSquare"/>
+ <View
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="#FF0"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+ android:id="@+id/yellowSquare"/>
+</RelativeLayout>
diff --git a/transition/tests/res/layout/support_scene1.xml b/transition/tests/res/layout/support_scene1.xml
new file mode 100644
index 0000000..1c6ff90
--- /dev/null
+++ b/transition/tests/res/layout/support_scene1.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/container">
+
+ <View
+ android:id="@+id/view0"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_gravity="end|top"
+ android:background="#f00"/>
+
+ <View
+ android:id="@+id/view1"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_gravity="center"
+ android:background="#0f0"/>
+
+ <View
+ android:id="@+id/view2"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_gravity="end|bottom"
+ android:background="#00f"/>
+
+</FrameLayout>
diff --git a/transition/tests/res/transition/change_clip_bounds.xml b/transition/tests/res/transition/change_clip_bounds.xml
new file mode 100644
index 0000000..e917c54
--- /dev/null
+++ b/transition/tests/res/transition/change_clip_bounds.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<changeClipBounds/>
diff --git a/transition/tests/res/transition/explode.xml b/transition/tests/res/transition/explode.xml
new file mode 100644
index 0000000..71e0d26
--- /dev/null
+++ b/transition/tests/res/transition/explode.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<explode xmlns:android="http://schemas.android.com/apk/res/android"
+ android:transitionVisibilityMode="mode_in"/>
diff --git a/transition/tests/res/values/strings.xml b/transition/tests/res/values/strings.xml
new file mode 100644
index 0000000..741c335
--- /dev/null
+++ b/transition/tests/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="hello">Hello</string>
+</resources>
diff --git a/transition/tests/src/android/support/transition/BaseTest.java b/transition/tests/src/android/support/transition/BaseTest.java
index ddf43c3..4ffb2f9 100644
--- a/transition/tests/src/android/support/transition/BaseTest.java
+++ b/transition/tests/src/android/support/transition/BaseTest.java
@@ -22,7 +22,6 @@
import org.junit.Rule;
import org.junit.runner.RunWith;
-@SuppressWarnings("unchecked")
@RunWith(AndroidJUnit4.class)
public abstract class BaseTest {
@@ -30,7 +29,7 @@
public final ActivityTestRule<TransitionActivity> rule;
BaseTest() {
- rule = new ActivityTestRule(TransitionActivity.class);
+ rule = new ActivityTestRule<>(TransitionActivity.class);
}
}
diff --git a/transition/tests/src/android/support/transition/BaseTransitionTest.java b/transition/tests/src/android/support/transition/BaseTransitionTest.java
new file mode 100644
index 0000000..81a9526
--- /dev/null
+++ b/transition/tests/src/android/support/transition/BaseTransitionTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.transition;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.support.test.InstrumentationRegistry;
+import android.support.transition.test.R;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import org.junit.Before;
+
+import java.util.ArrayList;
+
+public abstract class BaseTransitionTest extends BaseTest {
+
+ ArrayList<View> mTransitionTargets = new ArrayList<>();
+ LinearLayout mRoot;
+ Transition mTransition;
+ Transition.TransitionListener mListener;
+ float mAnimatedValue;
+
+ @Before
+ public void setUp() {
+ InstrumentationRegistry.getInstrumentation().setInTouchMode(false);
+ mRoot = (LinearLayout) rule.getActivity().findViewById(R.id.root);
+ mTransitionTargets.clear();
+ mTransition = createTransition();
+ mListener = mock(Transition.TransitionListener.class);
+ mTransition.addListener(mListener);
+ }
+
+ Transition createTransition() {
+ return new TestTransition();
+ }
+
+ void waitForStart() {
+ verify(mListener, timeout(3000)).onTransitionStart(any(Transition.class));
+ }
+
+ void waitForEnd() {
+ verify(mListener, timeout(3000)).onTransitionEnd(any(Transition.class));
+ }
+
+ Scene loadScene(final int layoutId) throws Throwable {
+ final Scene[] scene = new Scene[1];
+ rule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ scene[0] = Scene.getSceneForLayout(mRoot, layoutId, rule.getActivity());
+ }
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ return scene[0];
+ }
+
+ void enterScene(final int layoutId) throws Throwable {
+ enterScene(loadScene(layoutId));
+ }
+
+ void enterScene(final Scene scene) throws Throwable {
+ rule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ scene.enter();
+ }
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ void resetListener() {
+ mTransition.removeListener(mListener);
+ mListener = mock(Transition.TransitionListener.class);
+ mTransition.addListener(mListener);
+ }
+
+ void setAnimatedValue(float animatedValue) {
+ mAnimatedValue = animatedValue;
+ }
+
+ public class TestTransition extends Visibility {
+
+ @Override
+ public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+ TransitionValues endValues) {
+ mTransitionTargets.add(endValues.view);
+ return ObjectAnimator.ofFloat(BaseTransitionTest.this, "animatedValue", 0, 1);
+ }
+
+ @Override
+ public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+ TransitionValues endValues) {
+ mTransitionTargets.add(startValues.view);
+ return ObjectAnimator.ofFloat(BaseTransitionTest.this, "animatedValue", 1, 0);
+ }
+
+ }
+
+}
diff --git a/transition/tests/src/android/support/transition/ChangeClipBoundsTest.java b/transition/tests/src/android/support/transition/ChangeClipBoundsTest.java
new file mode 100644
index 0000000..fb94b25
--- /dev/null
+++ b/transition/tests/src/android/support/transition/ChangeClipBoundsTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.transition;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Rect;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
+import android.support.transition.test.R;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+
+import org.junit.Test;
+
+@MediumTest
+public class ChangeClipBoundsTest extends BaseTransitionTest {
+
+ @Override
+ Transition createTransition() {
+ return new ChangeClipBounds();
+ }
+
+ @SdkSuppress(minSdkVersion = 18)
+ @Test
+ public void testChangeClipBounds() throws Throwable {
+ enterScene(R.layout.scene1);
+
+ final View redSquare = rule.getActivity().findViewById(R.id.redSquare);
+ final Rect newClip = new Rect(redSquare.getLeft() + 10, redSquare.getTop() + 10,
+ redSquare.getRight() - 10, redSquare.getBottom() - 10);
+
+ rule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertNull(ViewCompat.getClipBounds(redSquare));
+ TransitionManager.beginDelayedTransition(mRoot, mTransition);
+ ViewCompat.setClipBounds(redSquare, newClip);
+ }
+ });
+ waitForStart();
+ Thread.sleep(150);
+ rule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Rect midClip = ViewCompat.getClipBounds(redSquare);
+ assertNotNull(midClip);
+ assertTrue(midClip.left > 0 && midClip.left < newClip.left);
+ assertTrue(midClip.top > 0 && midClip.top < newClip.top);
+ assertTrue(midClip.right < redSquare.getRight() && midClip.right > newClip.right);
+ assertTrue(midClip.bottom < redSquare.getBottom()
+ && midClip.bottom > newClip.bottom);
+ }
+ });
+ waitForEnd();
+
+ rule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ final Rect endRect = ViewCompat.getClipBounds(redSquare);
+ assertNotNull(endRect);
+ assertEquals(newClip, endRect);
+ }
+ });
+
+ resetListener();
+ rule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.beginDelayedTransition(mRoot, mTransition);
+ ViewCompat.setClipBounds(redSquare, null);
+ }
+ });
+ waitForStart();
+ Thread.sleep(150);
+ rule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Rect midClip = ViewCompat.getClipBounds(redSquare);
+ assertNotNull(midClip);
+ assertTrue(midClip.left > 0 && midClip.left < newClip.left);
+ assertTrue(midClip.top > 0 && midClip.top < newClip.top);
+ assertTrue(midClip.right < redSquare.getRight() && midClip.right > newClip.right);
+ assertTrue(midClip.bottom < redSquare.getBottom()
+ && midClip.bottom > newClip.bottom);
+ }
+ });
+ waitForEnd();
+
+ rule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertNotNull(ViewCompat.getClipBounds(redSquare));
+ }
+ });
+
+ }
+
+ @Test
+ public void dummy() {
+ // Avoid "No tests found" on older devices
+ }
+
+}
diff --git a/transition/tests/src/android/support/transition/ExplodeTest.java b/transition/tests/src/android/support/transition/ExplodeTest.java
new file mode 100644
index 0000000..b421537
--- /dev/null
+++ b/transition/tests/src/android/support/transition/ExplodeTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.transition;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.transition.test.R;
+import android.view.View;
+
+import org.junit.Test;
+
+@MediumTest
+public class ExplodeTest extends BaseTransitionTest {
+
+ @Override
+ Transition createTransition() {
+ return new Explode();
+ }
+
+ @Test
+ public void testExplode() throws Throwable {
+ enterScene(R.layout.scene10);
+ final View redSquare = rule.getActivity().findViewById(R.id.redSquare);
+ final View greenSquare = rule.getActivity().findViewById(R.id.greenSquare);
+ final View blueSquare = rule.getActivity().findViewById(R.id.blueSquare);
+ final View yellowSquare = rule.getActivity().findViewById(R.id.yellowSquare);
+
+ rule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.beginDelayedTransition(mRoot, mTransition);
+ redSquare.setVisibility(View.INVISIBLE);
+ greenSquare.setVisibility(View.INVISIBLE);
+ blueSquare.setVisibility(View.INVISIBLE);
+ yellowSquare.setVisibility(View.INVISIBLE);
+ }
+ });
+ waitForStart();
+ verify(mListener, never()).onTransitionEnd(any(Transition.class));
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+ assertEquals(View.VISIBLE, greenSquare.getVisibility());
+ assertEquals(View.VISIBLE, blueSquare.getVisibility());
+ assertEquals(View.VISIBLE, yellowSquare.getVisibility());
+ float redStartX = redSquare.getTranslationX();
+ float redStartY = redSquare.getTranslationY();
+
+ SystemClock.sleep(100);
+ verifyTranslation(redSquare, true, true);
+ verifyTranslation(greenSquare, false, true);
+ verifyTranslation(blueSquare, false, false);
+ verifyTranslation(yellowSquare, true, false);
+ assertTrue(redStartX > redSquare.getTranslationX()); // moving left
+ assertTrue(redStartY > redSquare.getTranslationY()); // moving up
+ waitForEnd();
+
+ verifyNoTranslation(redSquare);
+ verifyNoTranslation(greenSquare);
+ verifyNoTranslation(blueSquare);
+ verifyNoTranslation(yellowSquare);
+ assertEquals(View.INVISIBLE, redSquare.getVisibility());
+ assertEquals(View.INVISIBLE, greenSquare.getVisibility());
+ assertEquals(View.INVISIBLE, blueSquare.getVisibility());
+ assertEquals(View.INVISIBLE, yellowSquare.getVisibility());
+ }
+
+ @Test
+ public void testImplode() throws Throwable {
+ enterScene(R.layout.scene10);
+ final View redSquare = rule.getActivity().findViewById(R.id.redSquare);
+ final View greenSquare = rule.getActivity().findViewById(R.id.greenSquare);
+ final View blueSquare = rule.getActivity().findViewById(R.id.blueSquare);
+ final View yellowSquare = rule.getActivity().findViewById(R.id.yellowSquare);
+
+ rule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ redSquare.setVisibility(View.INVISIBLE);
+ greenSquare.setVisibility(View.INVISIBLE);
+ blueSquare.setVisibility(View.INVISIBLE);
+ yellowSquare.setVisibility(View.INVISIBLE);
+ }
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ rule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.beginDelayedTransition(mRoot, mTransition);
+ redSquare.setVisibility(View.VISIBLE);
+ greenSquare.setVisibility(View.VISIBLE);
+ blueSquare.setVisibility(View.VISIBLE);
+ yellowSquare.setVisibility(View.VISIBLE);
+ }
+ });
+ waitForStart();
+
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+ assertEquals(View.VISIBLE, greenSquare.getVisibility());
+ assertEquals(View.VISIBLE, blueSquare.getVisibility());
+ assertEquals(View.VISIBLE, yellowSquare.getVisibility());
+ float redStartX = redSquare.getTranslationX();
+ float redStartY = redSquare.getTranslationY();
+
+ SystemClock.sleep(100);
+ verifyTranslation(redSquare, true, true);
+ verifyTranslation(greenSquare, false, true);
+ verifyTranslation(blueSquare, false, false);
+ verifyTranslation(yellowSquare, true, false);
+ assertTrue(redStartX < redSquare.getTranslationX()); // moving right
+ assertTrue(redStartY < redSquare.getTranslationY()); // moving down
+ waitForEnd();
+
+ verifyNoTranslation(redSquare);
+ verifyNoTranslation(greenSquare);
+ verifyNoTranslation(blueSquare);
+ verifyNoTranslation(yellowSquare);
+ assertEquals(View.VISIBLE, redSquare.getVisibility());
+ assertEquals(View.VISIBLE, greenSquare.getVisibility());
+ assertEquals(View.VISIBLE, blueSquare.getVisibility());
+ assertEquals(View.VISIBLE, yellowSquare.getVisibility());
+ }
+
+ private void verifyTranslation(View view, boolean goLeft, boolean goUp) {
+ float translationX = view.getTranslationX();
+ float translationY = view.getTranslationY();
+
+ if (goLeft) {
+ assertTrue(translationX < 0);
+ } else {
+ assertTrue(translationX > 0);
+ }
+
+ if (goUp) {
+ assertTrue(translationY < 0);
+ } else {
+ assertTrue(translationY > 0);
+ }
+ }
+
+ private void verifyNoTranslation(View view) {
+ assertEquals(0f, view.getTranslationX(), 0.0f);
+ assertEquals(0f, view.getTranslationY(), 0.0f);
+ }
+
+}
diff --git a/transition/tests/src/android/support/transition/PropagationTest.java b/transition/tests/src/android/support/transition/PropagationTest.java
new file mode 100644
index 0000000..3d57998
--- /dev/null
+++ b/transition/tests/src/android/support/transition/PropagationTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.transition;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Rect;
+import android.support.test.filters.MediumTest;
+import android.support.transition.test.R;
+import android.view.View;
+
+import org.junit.Test;
+
+@MediumTest
+public class PropagationTest extends BaseTransitionTest {
+
+ @Test
+ public void testCircularPropagation() throws Throwable {
+ enterScene(R.layout.scene10);
+ CircularPropagation propagation = new CircularPropagation();
+ mTransition.setPropagation(propagation);
+ final TransitionValues redValues = new TransitionValues();
+ redValues.view = mRoot.findViewById(R.id.redSquare);
+ propagation.captureValues(redValues);
+
+ // Only the reported propagation properties are set
+ for (String prop : propagation.getPropagationProperties()) {
+ assertTrue(redValues.values.keySet().contains(prop));
+ }
+ assertEquals(propagation.getPropagationProperties().length, redValues.values.size());
+
+ // check the visibility
+ assertEquals(View.VISIBLE, propagation.getViewVisibility(redValues));
+ assertEquals(View.GONE, propagation.getViewVisibility(null));
+
+ // Check the positions
+ int[] pos = new int[2];
+ redValues.view.getLocationOnScreen(pos);
+ pos[0] += redValues.view.getWidth() / 2;
+ pos[1] += redValues.view.getHeight() / 2;
+ assertEquals(pos[0], propagation.getViewX(redValues));
+ assertEquals(pos[1], propagation.getViewY(redValues));
+
+ mTransition.setEpicenterCallback(new Transition.EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ return new Rect(0, 0, redValues.view.getWidth(), redValues.view.getHeight());
+ }
+ });
+
+ long redDelay = getDelay(R.id.redSquare);
+ // red square's delay should be roughly 0 since it is at the epicenter
+ assertEquals(0f, redDelay, 30f);
+
+ // The green square is on the upper-right
+ long greenDelay = getDelay(R.id.greenSquare);
+ assertTrue(greenDelay < redDelay);
+
+ // The blue square is on the lower-right
+ long blueDelay = getDelay(R.id.blueSquare);
+ assertTrue(blueDelay < greenDelay);
+
+ // Test propagation speed
+ propagation.setPropagationSpeed(1000000000f);
+ assertEquals(0, getDelay(R.id.blueSquare));
+ }
+
+ private TransitionValues capturePropagationValues(int viewId) {
+ TransitionValues transitionValues = new TransitionValues();
+ transitionValues.view = mRoot.findViewById(viewId);
+ mTransition.getPropagation().captureValues(transitionValues);
+ return transitionValues;
+ }
+
+ private long getDelay(int viewId) {
+ TransitionValues transitionValues = capturePropagationValues(viewId);
+ return mTransition.getPropagation()
+ .getStartDelay(mRoot, mTransition, transitionValues, null);
+ }
+
+}
diff --git a/transition/tests/src/android/support/transition/TransitionInflaterTest.java b/transition/tests/src/android/support/transition/TransitionInflaterTest.java
index 28c97d8..bc53dc8 100644
--- a/transition/tests/src/android/support/transition/TransitionInflaterTest.java
+++ b/transition/tests/src/android/support/transition/TransitionInflaterTest.java
@@ -51,6 +51,9 @@
TransitionInflater inflater = TransitionInflater.from(rule.getActivity());
// TODO: Add more Transition types
verifyFadeProperties(inflater.inflateTransition(R.transition.fade));
+ verifyExplodeProperties(inflater.inflateTransition(R.transition.explode));
+ verifyChangeClipBoundsProperties(
+ inflater.inflateTransition(R.transition.change_clip_bounds));
verifyAutoTransitionProperties(inflater.inflateTransition(R.transition.auto_transition));
verifyTransitionSetProperties(inflater.inflateTransition(R.transition.transition_set));
verifyCustomTransitionProperties(
@@ -68,6 +71,16 @@
assertEquals(Fade.OUT, fade.getMode());
}
+ private void verifyExplodeProperties(Transition transition) {
+ assertTrue(transition instanceof Explode);
+ Visibility visibility = (Visibility) transition;
+ assertEquals(Visibility.MODE_IN, visibility.getMode());
+ }
+
+ private void verifyChangeClipBoundsProperties(Transition transition) {
+ assertTrue(transition instanceof ChangeClipBounds);
+ }
+
private void verifyAutoTransitionProperties(Transition transition) {
assertTrue(transition instanceof AutoTransition);
}
diff --git a/transition/tests/src/android/support/transition/TransitionTest.java b/transition/tests/src/android/support/transition/TransitionTest.java
index f9370c9..335c07b 100644
--- a/transition/tests/src/android/support/transition/TransitionTest.java
+++ b/transition/tests/src/android/support/transition/TransitionTest.java
@@ -20,6 +20,7 @@
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.sameInstance;
@@ -33,6 +34,7 @@
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
+import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.test.annotation.UiThreadTest;
@@ -289,6 +291,39 @@
verify(animatorListener, never()).onAnimationStart(any(Animator.class));
}
+ @Test
+ public void testEpicenter() throws Throwable {
+ final Transition transition = new EmptyTransition();
+ final Transition.EpicenterCallback epicenterCallback = new Transition.EpicenterCallback() {
+ private Rect mRect = new Rect();
+
+ @Override
+ public Rect onGetEpicenter(Transition t) {
+ assertThat(t, is(sameInstance(transition)));
+ mRect.set(1, 2, 3, 4);
+ return mRect;
+ }
+ };
+ transition.setEpicenterCallback(epicenterCallback);
+ assertThat(transition.getEpicenterCallback(),
+ is(sameInstance(transition.getEpicenterCallback())));
+ Rect rect = transition.getEpicenter();
+ assertNotNull(rect);
+ assertThat(rect.left, is(1));
+ assertThat(rect.top, is(2));
+ assertThat(rect.right, is(3));
+ assertThat(rect.bottom, is(4));
+ }
+
+ @Test
+ public void testSetPropagation() throws Throwable {
+ final Transition transition = new EmptyTransition();
+ assertThat(transition.getPropagation(), is(nullValue()));
+ final TransitionPropagation propagation = new CircularPropagation();
+ transition.setPropagation(propagation);
+ assertThat(propagation, is(sameInstance(propagation)));
+ }
+
private void showInitialScene() throws Throwable {
SyncRunnable enter0 = new SyncRunnable();
mScenes[0].setEnterAction(enter0);
diff --git a/tv-provider/AndroidManifest.xml b/tv-provider/AndroidManifest.xml
index e479417..f07d090 100644
--- a/tv-provider/AndroidManifest.xml
+++ b/tv-provider/AndroidManifest.xml
@@ -16,6 +16,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.media.tv">
<uses-sdk android:minSdkVersion="21"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
+ <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/tv-provider/src/android/support/media/tv/BasePreviewProgram.java b/tv-provider/src/android/support/media/tv/BasePreviewProgram.java
index da43f3d..6648ea4 100644
--- a/tv-provider/src/android/support/media/tv/BasePreviewProgram.java
+++ b/tv-provider/src/android/support/media/tv/BasePreviewProgram.java
@@ -327,6 +327,18 @@
* TV Input Framework database.
*/
public ContentValues toContentValues() {
+ return toContentValues(false);
+ }
+
+ /**
+ * Returns fields of the BasePreviewProgram in the ContentValues format to be easily inserted
+ * into the TV Input Framework database.
+ *
+ * @param includeProtectedFields Whether the fields protected by system is included or not.
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ public ContentValues toContentValues(boolean includeProtectedFields) {
ContentValues values = super.toContentValues();
if (BuildCompat.isAtLeastO()) {
if (!TextUtils.isEmpty(mInternalProviderId)) {
@@ -399,12 +411,16 @@
if (!TextUtils.isEmpty(mReviewRating)) {
values.put(BasePreviewProgramColumns.COLUMN_REVIEW_RATING, mReviewRating);
}
- if (mBrowsable != INVALID_INT_VALUE) {
- values.put(BasePreviewProgramColumns.COLUMN_BROWSABLE, mBrowsable);
- }
if (!TextUtils.isEmpty(mContentId)) {
values.put(BasePreviewProgramColumns.COLUMN_CONTENT_ID, mContentId);
}
+ if (includeProtectedFields) {
+ if (BuildCompat.isAtLeastO()) {
+ if (mBrowsable != INVALID_INT_VALUE) {
+ values.put(BasePreviewProgramColumns.COLUMN_BROWSABLE, mBrowsable);
+ }
+ }
+ }
}
return values;
}
diff --git a/tv-provider/src/android/support/media/tv/Channel.java b/tv-provider/src/android/support/media/tv/Channel.java
index 795ffa6..7e68755 100644
--- a/tv-provider/src/android/support/media/tv/Channel.java
+++ b/tv-provider/src/android/support/media/tv/Channel.java
@@ -17,6 +17,7 @@
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import android.annotation.TargetApi;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
@@ -62,6 +63,7 @@
* }
* </pre>
*/
+@TargetApi(21)
public final class Channel {
/**
* @hide
@@ -253,7 +255,6 @@
return Intent.parseUri(mAppLinkIntentUri.toString(), Intent.URI_INTENT_SCHEME);
}
-
/**
* @return The value of {@link Channels#COLUMN_NETWORK_AFFILIATION} for the channel.
*/
@@ -357,6 +358,18 @@
* TV Input Framework database.
*/
public ContentValues toContentValues() {
+ return toContentValues(false);
+ }
+
+ /**
+ * Returns fields of the Channel in the ContentValues format to be easily inserted into the
+ * TV Input Framework database.
+ *
+ * @param includeProtectedFields Whether the fields protected by system is included or not.
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ public ContentValues toContentValues(boolean includeProtectedFields) {
ContentValues values = new ContentValues();
if (mId != INVALID_CHANNEL_ID) {
values.put(Channels._ID, mId);
@@ -408,8 +421,6 @@
values.put(Channels.COLUMN_NETWORK_AFFILIATION, mNetworkAffiliation);
values.put(Channels.COLUMN_SEARCHABLE, mSearchable);
values.put(Channels.COLUMN_SERVICE_TYPE, mServiceType);
- values.put(Channels.COLUMN_BROWSABLE, mBrowsable);
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
values.put(Channels.COLUMN_APP_LINK_COLOR, mAppLinkColor);
@@ -449,7 +460,13 @@
}
if (BuildCompat.isAtLeastO()) {
values.put(Channels.COLUMN_TRANSIENT, mTransient);
- values.put(Channels.COLUMN_SYSTEM_APPROVED, mSystemApproved);
+ }
+
+ if (includeProtectedFields) {
+ values.put(Channels.COLUMN_BROWSABLE, mBrowsable);
+ if (BuildCompat.isAtLeastO()) {
+ values.put(Channels.COLUMN_SYSTEM_APPROVED, mSystemApproved);
+ }
}
return values;
}
diff --git a/tv-provider/src/android/support/media/tv/ChannelLogoUtils.java b/tv-provider/src/android/support/media/tv/ChannelLogoUtils.java
new file mode 100644
index 0000000..20b79f3
--- /dev/null
+++ b/tv-provider/src/android/support/media/tv/ChannelLogoUtils.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.media.tv;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.sqlite.SQLiteException;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.util.Log;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+
+/** A utility class for conveniently storing and loading channel logos. */
+@WorkerThread
+public class ChannelLogoUtils {
+ private static final String TAG = "ChannelLogoUtils";
+
+ private static final int CONNECTION_TIMEOUT_MS_FOR_URLCONNECTION = 3000; // 3 sec
+ private static final int READ_TIMEOUT_MS_FOR_URLCONNECTION = 10000; // 10 sec
+
+ /**
+ * Stores channel logo in the system content provider from the given URI. The method will try
+ * to fetch the image file and decode it into {@link Bitmap}. Once the image is successfully
+ * fetched, it will be stored in the system content provider and associated with the given
+ * channel ID.
+ *
+ * <p>The URI provided to this method can be a URL referring to a image file residing in some
+ * remote site/server, or a URI in one of the following formats:
+ *
+ * <ul>
+ * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+ * <li>android.resource ({@link android.content.ContentResolver
+ * #SCHEME_ANDROID_RESOURCE})</li>
+ * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+ * </ul>
+ *
+ * <p>This method should be run in a worker thread since it may require network connection,
+ * which will raise an exception if it's running in the main thread.
+ *
+ * @param context the context used to access the system content provider
+ * @param channelId the ID of the target channel with which the fetched logo should be
+ * associated
+ * @param logoUri the {@link Uri} of the logo file to be fetched and stored in the system
+ * provider
+ *
+ * @return {@code true} if successfully fetched the image file referred by the give logo URI
+ * and stored it in the system content provider, or {@code false} if failed.
+ *
+ * @see #loadChannelLogo(Context, long)
+ */
+ public static boolean storeChannelLogo(@NonNull Context context, long channelId,
+ @NonNull Uri logoUri) {
+ String scheme = logoUri.normalizeScheme().getScheme();
+ URLConnection urlConnection = null;
+ InputStream inputStream = null;
+ Bitmap fetchedLogo = null;
+ try {
+ if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)
+ || ContentResolver.SCHEME_FILE.equals(scheme)
+ || ContentResolver.SCHEME_CONTENT.equals(scheme)) {
+ // A local resource
+ inputStream = context.getContentResolver().openInputStream(logoUri);
+ } else {
+ // A remote resource, should be an valid URL.
+ urlConnection = getUrlConnection(logoUri.toString());
+ inputStream = urlConnection.getInputStream();
+ }
+ fetchedLogo = BitmapFactory.decodeStream(inputStream);
+ } catch (IOException e) {
+ Log.i(TAG, "Failed to get logo from the URI: " + logoUri + "\n", e);
+ } finally {
+ if (inputStream != null) {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ // Do nothing.
+ }
+ }
+ if (urlConnection instanceof HttpURLConnection) {
+ ((HttpURLConnection) urlConnection).disconnect();
+ }
+ }
+ return fetchedLogo != null && storeChannelLogo(context, channelId, fetchedLogo);
+ }
+
+ /**
+ * Stores the given channel logo {@link Bitmap} in the system content provider and associate
+ * it with the given channel ID.
+ *
+ * @param context the context used to access the system content provider
+ * @param channelId the ID of the target channel with which the given logo should be associated
+ * @param logo the logo image to be stored
+ *
+ * @return {@code true} if successfully stored the logo in the system content provider,
+ * otherwise {@code false}.
+ *
+ * @see #loadChannelLogo(Context, long)
+ */
+ public static boolean storeChannelLogo(@NonNull Context context, long channelId,
+ @NonNull Bitmap logo) {
+ boolean result = false;
+ Uri localUri = TvContract.buildChannelLogoUri(channelId);
+ try (OutputStream outputStream = context.getContentResolver().openOutputStream(localUri)) {
+ result = logo.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
+ outputStream.flush();
+ } catch (SQLiteException | IOException e) {
+ Log.i(TAG, "Failed to store the logo to the system content provider.\n", e);
+ }
+ return result;
+ }
+
+ /**
+ * A convenient helper method to get the channel logo associated to the given channel ID from
+ * the system content provider.
+ *
+ * @param context the context used to access the system content provider
+ * @param channelId the ID of the channel whose logo is supposed to be loaded
+ *
+ * @return the requested channel logo in {@link Bitmap}, or {@code null} if not available.
+ *
+ * @see #storeChannelLogo(Context, long, Uri)
+ * @see #storeChannelLogo(Context, long, Bitmap)
+ */
+ public static Bitmap loadChannelLogo(@NonNull Context context, long channelId) {
+ Bitmap channelLogo = null;
+ try {
+ channelLogo = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(
+ TvContract.buildChannelLogoUri(channelId)));
+ } catch (FileNotFoundException e) {
+ // Channel logo is not found in the content provider.
+ Log.i(TAG, "Channel logo for channel (ID:" + channelId + ") not found.", e);
+ }
+ return channelLogo;
+ }
+
+ private static URLConnection getUrlConnection(String uriString) throws IOException {
+ URLConnection urlConnection = new URL(uriString).openConnection();
+ urlConnection.setConnectTimeout(CONNECTION_TIMEOUT_MS_FOR_URLCONNECTION);
+ urlConnection.setReadTimeout(READ_TIMEOUT_MS_FOR_URLCONNECTION);
+ return urlConnection;
+ }
+}
diff --git a/tv-provider/src/android/support/media/tv/PreviewProgram.java b/tv-provider/src/android/support/media/tv/PreviewProgram.java
index 5a34ed0..6de7cf2 100644
--- a/tv-provider/src/android/support/media/tv/PreviewProgram.java
+++ b/tv-provider/src/android/support/media/tv/PreviewProgram.java
@@ -116,7 +116,19 @@
* TV Input Framework database.
*/
public ContentValues toContentValues() {
- ContentValues values = super.toContentValues();
+ return toContentValues(false);
+ }
+
+ /**
+ * Returns fields of the PreviewProgram in the ContentValues format to be easily inserted
+ * into the TV Input Framework database.
+ *
+ * @param includeProtectedFields Whether the fields protected by system is included or not.
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ public ContentValues toContentValues(boolean includeProtectedFields) {
+ ContentValues values = super.toContentValues(includeProtectedFields);
if (mChannelId != INVALID_LONG_VALUE) {
values.put(PreviewPrograms.COLUMN_CHANNEL_ID, mChannelId);
} else {
diff --git a/tv-provider/src/android/support/media/tv/Program.java b/tv-provider/src/android/support/media/tv/Program.java
index b8e1861..d1b6cce 100644
--- a/tv-provider/src/android/support/media/tv/Program.java
+++ b/tv-provider/src/android/support/media/tv/Program.java
@@ -17,6 +17,7 @@
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import android.annotation.TargetApi;
import android.content.ContentValues;
import android.database.Cursor;
import android.os.Build;
@@ -57,6 +58,7 @@
* }
* </pre>
*/
+@TargetApi(21)
public final class Program extends BaseProgram implements Comparable<Program> {
/**
* @hide
diff --git a/tv-provider/src/android/support/media/tv/WatchNextProgram.java b/tv-provider/src/android/support/media/tv/WatchNextProgram.java
index 664c0f9..1e48c2b 100644
--- a/tv-provider/src/android/support/media/tv/WatchNextProgram.java
+++ b/tv-provider/src/android/support/media/tv/WatchNextProgram.java
@@ -120,7 +120,19 @@
* TV Input Framework database.
*/
public ContentValues toContentValues() {
- ContentValues values = super.toContentValues();
+ return toContentValues(false);
+ }
+
+ /**
+ * Returns fields of the WatchNextProgram in the ContentValues format to be easily inserted
+ * into the TV Input Framework database.
+ *
+ * @param includeProtectedFields Whether the fields protected by system is included or not.
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ public ContentValues toContentValues(boolean includeProtectedFields) {
+ ContentValues values = super.toContentValues(includeProtectedFields);
if (!TextUtils.isEmpty(mWatchNextType)) {
values.put(WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE, mWatchNextType);
}
diff --git a/tv-provider/tests/res/drawable/test_icon.png b/tv-provider/tests/res/drawable/test_icon.png
new file mode 100644
index 0000000..d6a6ea4
--- /dev/null
+++ b/tv-provider/tests/res/drawable/test_icon.png
Binary files differ
diff --git a/tv-provider/tests/src/android/support/media/tv/ChannelLogoUtilsTest.java b/tv-provider/tests/src/android/support/media/tv/ChannelLogoUtilsTest.java
new file mode 100644
index 0000000..cd6a0e9
--- /dev/null
+++ b/tv-provider/tests/src/android/support/media/tv/ChannelLogoUtilsTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.media.tv;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.support.media.tv.test.R;
+import android.test.AndroidTestCase;
+
+public class ChannelLogoUtilsTest extends AndroidTestCase {
+ private static final String FAKE_INPUT_ID = "ChannelLogoUtils.test";
+
+ private ContentResolver mContentResolver;
+ private Uri mChannelUri;
+ private long mChannelId;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mContentResolver = getContext().getContentResolver();
+ ContentValues contentValues = new Channel.Builder()
+ .setInputId(FAKE_INPUT_ID)
+ .setType(TvContractCompat.Channels.TYPE_OTHER).build().toContentValues();
+ mChannelUri = mContentResolver.insert(TvContract.Channels.CONTENT_URI, contentValues);
+ mChannelId = ContentUris.parseId(mChannelUri);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ mContentResolver.delete(mChannelUri, null, null);
+ }
+
+ public void testStoreChannelLogo_fromBitmap() {
+ assertNull(ChannelLogoUtils.loadChannelLogo(getContext(), mChannelId));
+ Bitmap logo = BitmapFactory.decodeResource(getContext().getResources(),
+ R.drawable.test_icon);
+ assertNotNull(logo);
+ assertTrue(ChannelLogoUtils.storeChannelLogo(getContext(), mChannelId, logo));
+ // Workaround: the file status is not consistent between openInputStream/openOutputStream,
+ // wait 10 secs to make sure that the logo file is written into the disk.
+ SystemClock.sleep(10000);
+ assertNotNull(ChannelLogoUtils.loadChannelLogo(getContext(), mChannelId));
+ }
+
+ public void testStoreChannelLogo_fromResUri() {
+ assertNull(ChannelLogoUtils.loadChannelLogo(getContext(), mChannelId));
+ int resId = R.drawable.test_icon;
+ Resources res = getContext().getResources();
+ Uri logoUri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+ .authority(res.getResourcePackageName(resId))
+ .appendPath(res.getResourceTypeName(resId))
+ .appendPath(res.getResourceEntryName(resId))
+ .build();
+ assertTrue(ChannelLogoUtils.storeChannelLogo(getContext(), mChannelId, logoUri));
+ // Workaround: the file status is not consistent between openInputStream/openOutputStream,
+ // wait 10 secs to make sure that the logo file is written into the disk.
+ SystemClock.sleep(10000);
+ assertNotNull(ChannelLogoUtils.loadChannelLogo(getContext(), mChannelId));
+ }
+}
diff --git a/tv-provider/tests/src/android/support/media/tv/ChannelTest.java b/tv-provider/tests/src/android/support/media/tv/ChannelTest.java
index 6783148..cf085b1 100644
--- a/tv-provider/tests/src/android/support/media/tv/ChannelTest.java
+++ b/tv-provider/tests/src/android/support/media/tv/ChannelTest.java
@@ -15,11 +15,15 @@
*/
package android.support.media.tv;
+import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
+import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Build;
+import android.support.media.tv.TvContractCompat.Channels;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SdkSuppress;
import android.support.test.filters.SmallTest;
import android.support.v4.os.BuildCompat;
@@ -33,18 +37,27 @@
* values from them
*/
@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
public class ChannelTest extends TestCase {
private static final String KEY_SPLASHSCREEN = "splashscreen";
private static final String KEY_PREMIUM_CHANNEL = "premium";
private static final String SPLASHSCREEN_URL = "http://example.com/splashscreen.jpg";
+ @Override
+ protected void tearDown() {
+ if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+ return;
+ }
+ ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+ resolver.delete(Channels.CONTENT_URI, null, null);
+ }
+
@Test
public void testEmptyChannel() {
Channel emptyChannel = new Channel.Builder()
.build();
- ContentValues contentValues = emptyChannel.toContentValues();
- compareChannel(emptyChannel, Channel.fromCursor(getChannelCursor(contentValues)));
+ ContentValues contentValues = emptyChannel.toContentValues(true);
+ compareChannel(emptyChannel, Channel.fromCursor(getChannelCursor(contentValues)), true);
}
@Test
@@ -60,16 +73,47 @@
Intent.URI_INTENT_SCHEME)))
.setOriginalNetworkId(0)
.build();
- ContentValues contentValues = sampleChannel.toContentValues();
- compareChannel(sampleChannel, Channel.fromCursor(getChannelCursor(contentValues)));
+ ContentValues contentValues = sampleChannel.toContentValues(true);
+ compareChannel(sampleChannel, Channel.fromCursor(getChannelCursor(contentValues)), true);
Channel clonedSampleChannel = new Channel.Builder(sampleChannel).build();
- compareChannel(sampleChannel, clonedSampleChannel);
+ compareChannel(sampleChannel, clonedSampleChannel, true);
}
@Test
public void testFullyPopulatedChannel() {
- Channel fullyPopulatedChannel = new Channel.Builder()
+ Channel fullyPopulatedChannel = createFullyPopulatedChannel();
+ ContentValues contentValues = fullyPopulatedChannel.toContentValues(true);
+ compareChannel(fullyPopulatedChannel, Channel.fromCursor(getChannelCursor(contentValues)),
+ true);
+
+ Channel clonedFullyPopulatedChannel = new Channel.Builder(fullyPopulatedChannel).build();
+ compareChannel(fullyPopulatedChannel, clonedFullyPopulatedChannel, true);
+ }
+
+ @Test
+ public void testChannelWithSystemContentProvider() {
+ if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+ return;
+ }
+ Channel fullyPopulatedChannel = createFullyPopulatedChannel();
+ ContentValues contentValues = fullyPopulatedChannel.toContentValues();
+ ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+ Uri channelUri = resolver.insert(Channels.CONTENT_URI, contentValues);
+ assertNotNull(channelUri);
+
+ Channel channelFromSystemDb;
+ try (Cursor cursor = resolver.query(channelUri, Channel.PROJECTION, null, null, null)) {
+ assertNotNull(cursor);
+ assertEquals(1, cursor.getCount());
+ cursor.moveToNext();
+ channelFromSystemDb = Channel.fromCursor(cursor);
+ }
+ compareChannel(fullyPopulatedChannel, channelFromSystemDb, false);
+ }
+
+ private static Channel createFullyPopulatedChannel() {
+ return new Channel.Builder()
.setAppLinkColor(0x00FF0000)
.setAppLinkIconUri(Uri.parse("http://example.com/icon.png"))
.setAppLinkIntent(new Intent())
@@ -81,7 +125,7 @@
.setInputId("TestInputService")
.setNetworkAffiliation("Network Affiliation")
.setOriginalNetworkId(2)
- .setPackageName("com.example.android.sampletvinput")
+ .setPackageName("android.support.media.tv.test")
.setSearchable(false)
.setServiceId(3)
.setTransportStreamId(4)
@@ -96,19 +140,14 @@
.setBrowsable(true)
.setSystemApproved(true)
.build();
- ContentValues contentValues = fullyPopulatedChannel.toContentValues();
- compareChannel(fullyPopulatedChannel, Channel.fromCursor(getChannelCursor(contentValues)));
-
- Channel clonedFullyPopulatedChannel = new Channel.Builder(fullyPopulatedChannel).build();
- compareChannel(fullyPopulatedChannel, clonedFullyPopulatedChannel);
}
- private static void compareChannel(Channel channelA, Channel channelB) {
+ private static void compareChannel(Channel channelA, Channel channelB,
+ boolean includeIdAndProtectedFields) {
assertEquals(channelA.isSearchable(), channelB.isSearchable());
assertEquals(channelA.getDescription(), channelB.getDescription());
assertEquals(channelA.getDisplayName(), channelB.getDisplayName());
assertEquals(channelA.getDisplayNumber(), channelB.getDisplayNumber());
- assertEquals(channelA.getId(), channelB.getId());
assertEquals(channelA.getInputId(), channelB.getInputId());
assertEquals(channelA.getNetworkAffiliation(), channelB.getNetworkAffiliation());
assertEquals(channelA.getOriginalNetworkId(), channelB.getOriginalNetworkId());
@@ -118,7 +157,6 @@
assertEquals(channelA.getTransportStreamId(), channelB.getTransportStreamId());
assertEquals(channelA.getType(), channelB.getType());
assertEquals(channelA.getVideoFormat(), channelB.getVideoFormat());
- assertEquals(channelA.isBrowsable(), channelB.isBrowsable());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
assertEquals(channelA.getAppLinkColor(), channelB.getAppLinkColor());
assertEquals(channelA.getAppLinkIconUri(), channelB.getAppLinkIconUri());
@@ -128,10 +166,19 @@
}
if (BuildCompat.isAtLeastO()) {
assertEquals(channelA.isTransient(), channelB.isTransient());
- assertEquals(channelA.isSystemApproved(), channelB.isSystemApproved());
- assertEquals(channelA.toContentValues(), channelB.toContentValues());
}
- assertEquals(channelA.toString(), channelB.toString());
+ if (includeIdAndProtectedFields) {
+ // Skip row ID since the one from system DB has the valid ID while the other does not.
+ assertEquals(channelA.getId(), channelB.getId());
+ // When we insert a channel using toContentValues() to the system, we drop some
+ // protected fields since they only can be modified by system apps.
+ assertEquals(channelA.isBrowsable(), channelB.isBrowsable());
+ if (BuildCompat.isAtLeastO()) {
+ assertEquals(channelA.isSystemApproved(), channelB.isSystemApproved());
+ }
+ assertEquals(channelA.toContentValues(), channelB.toContentValues());
+ assertEquals(channelA.toString(), channelB.toString());
+ }
}
private static MatrixCursor getChannelCursor(ContentValues contentValues) {
diff --git a/tv-provider/tests/src/android/support/media/tv/PreviewProgramTest.java b/tv-provider/tests/src/android/support/media/tv/PreviewProgramTest.java
index bfacc40..20bc8ab 100644
--- a/tv-provider/tests/src/android/support/media/tv/PreviewProgramTest.java
+++ b/tv-provider/tests/src/android/support/media/tv/PreviewProgramTest.java
@@ -15,12 +15,17 @@
*/
package android.support.media.tv;
+import android.content.ContentResolver;
+import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
+import android.database.Cursor;
import android.database.MatrixCursor;
import android.media.tv.TvContentRating;
import android.net.Uri;
+import android.support.media.tv.TvContractCompat.Channels;
import android.support.media.tv.TvContractCompat.PreviewPrograms;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.v4.os.BuildCompat;
@@ -38,16 +43,30 @@
*/
@SmallTest
public class PreviewProgramTest extends TestCase {
+
+ @Override
+ protected void tearDown() {
+ // TODO: Use @SdkSuppress once Build.VERSION_CODES.O has a right value.
+ if (!BuildCompat.isAtLeastO()) {
+ return;
+ }
+ if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+ return;
+ }
+ ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+ resolver.delete(Channels.CONTENT_URI, null, null);
+ }
+
@Test
public void testEmptyPreviewProgram() {
- // TODO: Use @SdkSuppress once Build.VERSION_CODES.O has a right value.
if (!BuildCompat.isAtLeastO()) {
return;
}
PreviewProgram emptyProgram = new PreviewProgram.Builder().build();
ContentValues contentValues = emptyProgram.toContentValues();
compareProgram(emptyProgram,
- PreviewProgram.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)));
+ PreviewProgram.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)),
+ true);
}
@Test
@@ -64,13 +83,13 @@
.setChannelId(3)
.setWeight(70)
.build();
- ContentValues contentValues = sampleProgram.toContentValues();
+ ContentValues contentValues = sampleProgram.toContentValues(true);
compareProgram(sampleProgram,
PreviewProgram.fromCursor(
- getProgramCursor(PreviewProgram.PROJECTION, contentValues)));
+ getProgramCursor(PreviewProgram.PROJECTION, contentValues)), true);
PreviewProgram clonedSampleProgram = new PreviewProgram.Builder(sampleProgram).build();
- compareProgram(sampleProgram, clonedSampleProgram);
+ compareProgram(sampleProgram, clonedSampleProgram, true);
}
@Test
@@ -78,63 +97,46 @@
if (!BuildCompat.isAtLeastO()) {
return;
}
- PreviewProgram fullyPopulatedProgram = new PreviewProgram.Builder()
- .setTitle("Google")
- .setInternalProviderId("ID-4321")
- .setChannelId(3)
- .setWeight(100)
- .setPreviewVideoUri(Uri.parse("http://example.com/preview-video.mpg"))
- .setLastPlaybackPositionMillis(0)
- .setDurationMillis(60 * 1000)
- .setAppLinkIntentUri(Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(
- Intent.URI_INTENT_SCHEME)))
- .setTransient(false)
- .setType(PreviewPrograms.TYPE_MOVIE)
- .setPosterArtAspectRatio(PreviewPrograms.ASPECT_RATIO_2_3)
- .setThumbnailAspectRatio(PreviewPrograms.ASPECT_RATIO_16_9)
- .setLogoUri(Uri.parse("http://example.com/program-logo.mpg"))
- .setAvailability(PreviewPrograms.AVAILABILITY_AVAILABLE)
- .setStartingPrice("12.99 USD")
- .setOfferPrice("4.99 USD")
- .setReleaseDate("1997")
- .setItemCount(3)
- .setLive(false)
- .setInteractionType(PreviewPrograms.INTERACTION_TYPE_LIKES)
- .setInteractionCount(10200)
- .setAuthor("author_name")
- .setReviewRatingStyle(PreviewPrograms.REVIEW_RATING_STYLE_STARS)
- .setReviewRating("4.5")
- .setSearchable(false)
- .setThumbnailUri(Uri.parse("http://example.com/thumbnail.png"))
- .setAudioLanguages(new String [] {"eng", "kor"})
- .setCanonicalGenres(new String[] {TvContractCompat.Programs.Genres.MOVIES})
- .setContentRatings(new TvContentRating[] {
- TvContentRating.createRating("com.android.tv", "US_TV", "US_TV_Y7")})
- .setDescription("This is a sample program")
- .setEpisodeNumber("Pilot", 0)
- .setEpisodeTitle("Hello World")
- .setLongDescription("This is a longer description than the previous description")
- .setPosterArtUri(Uri.parse("http://example.com/poster.png"))
- .setSeasonNumber("The Final Season", 7)
- .setSeasonTitle("The Final Season")
- .setVideoHeight(1080)
- .setVideoWidth(1920)
- .setInternalProviderFlag1(0x4)
- .setInternalProviderFlag2(0x3)
- .setInternalProviderFlag3(0x2)
- .setInternalProviderFlag4(0x1)
- .setBrowsable(true)
- .setContentId("CID-8642")
- .build();
-
- ContentValues contentValues = fullyPopulatedProgram.toContentValues();
+ PreviewProgram fullyPopulatedProgram = createFullyPopulatedPreviewProgram(3);
+ ContentValues contentValues = fullyPopulatedProgram.toContentValues(true);
compareProgram(fullyPopulatedProgram,
PreviewProgram.fromCursor(
- getProgramCursor(PreviewProgram.PROJECTION, contentValues)));
+ getProgramCursor(PreviewProgram.PROJECTION, contentValues)), true);
PreviewProgram clonedFullyPopulatedProgram =
new PreviewProgram.Builder(fullyPopulatedProgram).build();
- compareProgram(fullyPopulatedProgram, clonedFullyPopulatedProgram);
+ compareProgram(fullyPopulatedProgram, clonedFullyPopulatedProgram, true);
+ }
+
+ @Test
+ public void testChannelWithSystemContentProvider() {
+ if (!BuildCompat.isAtLeastO()) {
+ return;
+ }
+ if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+ return;
+ }
+ Channel channel = new Channel.Builder()
+ .setInputId("TestInputService")
+ .setType(TvContractCompat.Channels.TYPE_PREVIEW)
+ .build();
+ ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+ Uri channelUri = resolver.insert(Channels.CONTENT_URI, channel.toContentValues());
+ assertNotNull(channelUri);
+
+ PreviewProgram fullyPopulatedProgram = createFullyPopulatedPreviewProgram(
+ ContentUris.parseId(channelUri));
+ Uri previewProgramUri = resolver.insert(PreviewPrograms.CONTENT_URI,
+ fullyPopulatedProgram.toContentValues());
+
+ PreviewProgram programFromSystemDb;
+ try (Cursor cursor = resolver.query(previewProgramUri, null, null, null, null)) {
+ assertNotNull(cursor);
+ assertEquals(1, cursor.getCount());
+ cursor.moveToNext();
+ programFromSystemDb = PreviewProgram.fromCursor(cursor);
+ }
+ compareProgram(fullyPopulatedProgram, programFromSystemDb, false);
}
@Test
@@ -204,16 +206,69 @@
PreviewPrograms.COLUMN_REVIEW_RATING,
};
- ContentValues contentValues = previewProgram.toContentValues();
+ ContentValues contentValues = previewProgram.toContentValues(true);
compareProgram(previewProgram,
- PreviewProgram.fromCursor(getProgramCursor(partialProjection, contentValues)));
+ PreviewProgram.fromCursor(getProgramCursor(partialProjection, contentValues)),
+ true);
PreviewProgram clonedFullyPopulatedProgram =
new PreviewProgram.Builder(previewProgram).build();
- compareProgram(previewProgram, clonedFullyPopulatedProgram);
+ compareProgram(previewProgram, clonedFullyPopulatedProgram, true);
}
- private static void compareProgram(PreviewProgram programA, PreviewProgram programB) {
+ private static PreviewProgram createFullyPopulatedPreviewProgram(long channelId) {
+ return new PreviewProgram.Builder()
+ .setTitle("Google")
+ .setInternalProviderId("ID-4321")
+ .setChannelId(channelId)
+ .setWeight(100)
+ .setPreviewVideoUri(Uri.parse("http://example.com/preview-video.mpg"))
+ .setLastPlaybackPositionMillis(0)
+ .setDurationMillis(60 * 1000)
+ .setAppLinkIntentUri(Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(
+ Intent.URI_INTENT_SCHEME)))
+ .setTransient(false)
+ .setType(PreviewPrograms.TYPE_MOVIE)
+ .setPosterArtAspectRatio(PreviewPrograms.ASPECT_RATIO_2_3)
+ .setThumbnailAspectRatio(PreviewPrograms.ASPECT_RATIO_16_9)
+ .setLogoUri(Uri.parse("http://example.com/program-logo.mpg"))
+ .setAvailability(PreviewPrograms.AVAILABILITY_AVAILABLE)
+ .setStartingPrice("12.99 USD")
+ .setOfferPrice("4.99 USD")
+ .setReleaseDate("1997")
+ .setItemCount(3)
+ .setLive(false)
+ .setInteractionType(PreviewPrograms.INTERACTION_TYPE_LIKES)
+ .setInteractionCount(10200)
+ .setAuthor("author_name")
+ .setReviewRatingStyle(PreviewPrograms.REVIEW_RATING_STYLE_STARS)
+ .setReviewRating("4.5")
+ .setSearchable(false)
+ .setThumbnailUri(Uri.parse("http://example.com/thumbnail.png"))
+ .setAudioLanguages(new String [] {"eng", "kor"})
+ .setCanonicalGenres(new String[] {TvContractCompat.Programs.Genres.MOVIES})
+ .setContentRatings(new TvContentRating[] {
+ TvContentRating.createRating("com.android.tv", "US_TV", "US_TV_Y7")})
+ .setDescription("This is a sample program")
+ .setEpisodeNumber("Pilot", 0)
+ .setEpisodeTitle("Hello World")
+ .setLongDescription("This is a longer description than the previous description")
+ .setPosterArtUri(Uri.parse("http://example.com/poster.png"))
+ .setSeasonNumber("The Final Season", 7)
+ .setSeasonTitle("The Final Season")
+ .setVideoHeight(1080)
+ .setVideoWidth(1920)
+ .setInternalProviderFlag1(0x4)
+ .setInternalProviderFlag2(0x3)
+ .setInternalProviderFlag3(0x2)
+ .setInternalProviderFlag4(0x1)
+ .setBrowsable(true)
+ .setContentId("CID-8642")
+ .build();
+ }
+
+ private static void compareProgram(PreviewProgram programA, PreviewProgram programB,
+ boolean includeIdAndProtectedFields) {
assertTrue(Arrays.equals(programA.getAudioLanguages(), programB.getAudioLanguages()));
assertTrue(Arrays.deepEquals(programA.getCanonicalGenres(), programB.getCanonicalGenres()));
assertEquals(programA.getChannelId(), programB.getChannelId());
@@ -223,7 +278,6 @@
assertEquals(programA.getEpisodeTitle(), programB.getEpisodeTitle());
assertEquals(programA.getLongDescription(), programB.getLongDescription());
assertEquals(programA.getPosterArtUri(), programB.getPosterArtUri());
- assertEquals(programA.getId(), programB.getId());
assertEquals(programA.getSeasonNumber(), programB.getSeasonNumber());
assertEquals(programA.getThumbnailUri(), programB.getThumbnailUri());
assertEquals(programA.getTitle(), programB.getTitle());
@@ -258,12 +312,17 @@
assertEquals(programA.getAuthor(), programB.getAuthor());
assertEquals(programA.getReviewRatingStyle(), programB.getReviewRatingStyle());
assertEquals(programA.getReviewRating(), programB.getReviewRating());
- assertEquals(programA.isBrowsable(), programB.isBrowsable());
assertEquals(programA.getContentId(), programB.getContentId());
-
- assertEquals(programA.toContentValues(), programB.toContentValues());
assertEquals(programA.toString(), programB.toString());
- assertEquals(programA, programB);
+ if (includeIdAndProtectedFields) {
+ // Skip row ID since the one from system DB has the valid ID while the other does not.
+ assertEquals(programA.getId(), programB.getId());
+ // When we insert a channel using toContentValues() to the system, we drop some
+ // protected fields since they only can be modified by system apps.
+ assertEquals(programA.isBrowsable(), programB.isBrowsable());
+ assertEquals(programA.toContentValues(), programB.toContentValues());
+ assertEquals(programA, programB);
+ }
}
private static MatrixCursor getProgramCursor(String[] projection, ContentValues contentValues) {
diff --git a/tv-provider/tests/src/android/support/media/tv/ProgramTest.java b/tv-provider/tests/src/android/support/media/tv/ProgramTest.java
index 6d2e947..ec66068 100644
--- a/tv-provider/tests/src/android/support/media/tv/ProgramTest.java
+++ b/tv-provider/tests/src/android/support/media/tv/ProgramTest.java
@@ -15,11 +15,17 @@
*/
package android.support.media.tv;
+import android.content.ContentResolver;
+import android.content.ContentUris;
import android.content.ContentValues;
+import android.database.Cursor;
import android.database.MatrixCursor;
import android.media.tv.TvContentRating;
import android.net.Uri;
import android.os.Build;
+import android.support.media.tv.TvContractCompat.Channels;
+import android.support.media.tv.TvContractCompat.Programs;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SdkSuppress;
import android.support.test.filters.SmallTest;
@@ -35,15 +41,24 @@
* values from them.
*/
@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
public class ProgramTest extends TestCase {
+ @Override
+ protected void tearDown() {
+ if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+ return;
+ }
+ ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+ resolver.delete(Channels.CONTENT_URI, null, null);
+ }
+
@Test
public void testEmptyProgram() {
Program emptyProgram = new Program.Builder()
.build();
ContentValues contentValues = emptyProgram.toContentValues();
compareProgram(emptyProgram,
- Program.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)));
+ Program.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)), true);
}
@Test
@@ -60,15 +75,54 @@
.build();
ContentValues contentValues = sampleProgram.toContentValues();
compareProgram(sampleProgram,
- Program.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)));
+ Program.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)), true);
Program clonedSampleProgram = new Program.Builder(sampleProgram).build();
- compareProgram(sampleProgram, clonedSampleProgram);
+ compareProgram(sampleProgram, clonedSampleProgram, true);
}
@Test
public void testFullyPopulatedProgram() {
- Program fullyPopulatedProgram = new Program.Builder()
+ Program fullyPopulatedProgram = createFullyPopulatedProgram(3);
+
+ ContentValues contentValues = fullyPopulatedProgram.toContentValues();
+ compareProgram(fullyPopulatedProgram,
+ Program.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)), true);
+
+ Program clonedFullyPopulatedProgram = new Program.Builder(fullyPopulatedProgram).build();
+ compareProgram(fullyPopulatedProgram, clonedFullyPopulatedProgram, true);
+ }
+
+ @Test
+ public void testChannelWithSystemContentProvider() {
+ if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+ return;
+ }
+ Channel channel = new Channel.Builder()
+ .setInputId("TestInputService")
+ .setType(TvContractCompat.Channels.TYPE_OTHER)
+ .build();
+ ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+ Uri channelUri = resolver.insert(Channels.CONTENT_URI, channel.toContentValues());
+ assertNotNull(channelUri);
+
+ Program fullyPopulatedProgram =
+ createFullyPopulatedProgram(ContentUris.parseId(channelUri));
+ Uri programUri = resolver.insert(Programs.CONTENT_URI,
+ fullyPopulatedProgram.toContentValues());
+
+ Program programFromSystemDb;
+ try (Cursor cursor = resolver.query(programUri, null, null, null, null)) {
+ assertNotNull(cursor);
+ assertEquals(1, cursor.getCount());
+ cursor.moveToNext();
+ programFromSystemDb = Program.fromCursor(cursor);
+ }
+ compareProgram(fullyPopulatedProgram, programFromSystemDb, false);
+ }
+
+ private static Program createFullyPopulatedProgram(long channelId) {
+ return new Program.Builder()
.setSearchable(false)
.setThumbnailUri(Uri.parse("http://example.com/thumbnail.png"))
.setAudioLanguages(new String [] {"eng", "kor"})
@@ -89,22 +143,16 @@
.setInternalProviderFlag2(0x3)
.setInternalProviderFlag3(0x2)
.setInternalProviderFlag4(0x1)
- .setChannelId(3)
+ .setChannelId(channelId)
.setStartTimeUtcMillis(0)
.setEndTimeUtcMillis(1000)
.setBroadcastGenres(new String[] {"Music", "Family"})
.setRecordingProhibited(false)
.build();
-
- ContentValues contentValues = fullyPopulatedProgram.toContentValues();
- compareProgram(fullyPopulatedProgram,
- Program.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)));
-
- Program clonedFullyPopulatedProgram = new Program.Builder(fullyPopulatedProgram).build();
- compareProgram(fullyPopulatedProgram, clonedFullyPopulatedProgram);
}
- private static void compareProgram(Program programA, Program programB) {
+ private static void compareProgram(Program programA, Program programB,
+ boolean includeIdAndProtectedFields) {
assertTrue(Arrays.equals(programA.getAudioLanguages(), programB.getAudioLanguages()));
assertTrue(Arrays.deepEquals(programA.getBroadcastGenres(), programB.getBroadcastGenres()));
assertTrue(Arrays.deepEquals(programA.getCanonicalGenres(), programB.getCanonicalGenres()));
@@ -116,7 +164,6 @@
assertEquals(programA.getEpisodeTitle(), programB.getEpisodeTitle());
assertEquals(programA.getLongDescription(), programB.getLongDescription());
assertEquals(programA.getPosterArtUri(), programB.getPosterArtUri());
- assertEquals(programA.getId(), programB.getId());
assertEquals(programA.getSeasonNumber(), programB.getSeasonNumber());
assertEquals(programA.getStartTimeUtcMillis(), programB.getStartTimeUtcMillis());
assertEquals(programA.getThumbnailUri(), programB.getThumbnailUri());
@@ -135,9 +182,13 @@
assertTrue(Objects.equals(programA.isRecordingProhibited(),
programB.isRecordingProhibited()));
}
- assertEquals(programA.toContentValues(), programB.toContentValues());
assertEquals(programA.toString(), programB.toString());
- assertEquals(programA, programB);
+ if (includeIdAndProtectedFields) {
+ // Skip row ID since the one from system DB has the valid ID while the other does not.
+ assertEquals(programA.getId(), programB.getId());
+ assertEquals(programA.toContentValues(), programB.toContentValues());
+ assertEquals(programA, programB);
+ }
}
private static MatrixCursor getProgramCursor(String[] projection, ContentValues contentValues) {
diff --git a/compat/jellybean/android/support/v4/app/ShareCompatJB.java b/tv-provider/tests/src/android/support/media/tv/Utils.java
similarity index 60%
rename from compat/jellybean/android/support/v4/app/ShareCompatJB.java
rename to tv-provider/tests/src/android/support/media/tv/Utils.java
index e65f952..a6ff0ad 100644
--- a/compat/jellybean/android/support/v4/app/ShareCompatJB.java
+++ b/tv-provider/tests/src/android/support/media/tv/Utils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-package android.support.v4.app;
+package android.support.media.tv;
-import android.support.annotation.RequiresApi;
-import android.text.Html;
+import android.content.Context;
+import android.content.pm.PackageManager;
-@RequiresApi(16)
-class ShareCompatJB {
- public static String escapeHtml(CharSequence html) {
- return Html.escapeHtml(html);
+public class Utils {
+ private Utils() { }
+
+ public static boolean hasTvInputFramework(Context context) {
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LIVE_TV);
}
}
diff --git a/tv-provider/tests/src/android/support/media/tv/WatchNextProgramTest.java b/tv-provider/tests/src/android/support/media/tv/WatchNextProgramTest.java
index fa137c0..724644a 100644
--- a/tv-provider/tests/src/android/support/media/tv/WatchNextProgramTest.java
+++ b/tv-provider/tests/src/android/support/media/tv/WatchNextProgramTest.java
@@ -16,12 +16,15 @@
package android.support.media.tv;
+import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
+import android.database.Cursor;
import android.database.MatrixCursor;
import android.media.tv.TvContentRating;
import android.net.Uri;
import android.support.media.tv.TvContractCompat.WatchNextPrograms;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.v4.os.BuildCompat;
@@ -39,16 +42,30 @@
*/
@SmallTest
public class WatchNextProgramTest extends TestCase {
- @Test
- public void testEmptyPreviewProgram() {
+
+ @Override
+ protected void tearDown() {
// TODO: Use @SdkSuppress once Build.VERSION_CODES.O has a right value.
if (!BuildCompat.isAtLeastO()) {
return;
}
+ if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+ return;
+ }
+ ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+ resolver.delete(WatchNextPrograms.CONTENT_URI, null, null);
+ }
+
+ @Test
+ public void testEmptyPreviewProgram() {
+ if (!BuildCompat.isAtLeastO()) {
+ return;
+ }
WatchNextProgram emptyProgram = new WatchNextProgram.Builder().build();
- ContentValues contentValues = emptyProgram.toContentValues();
+ ContentValues contentValues = emptyProgram.toContentValues(true);
compareProgram(emptyProgram,
- WatchNextProgram.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)));
+ WatchNextProgram.fromCursor(getProgramCursor(Program.PROJECTION, contentValues)),
+ true);
}
@Test
@@ -63,13 +80,13 @@
.setSeasonNumber("The Final Season", 7)
.setThumbnailUri(Uri.parse("http://www.example.com/programs/poster.png"))
.build();
- ContentValues contentValues = sampleProgram.toContentValues();
+ ContentValues contentValues = sampleProgram.toContentValues(true);
compareProgram(sampleProgram,
WatchNextProgram.fromCursor(
- getProgramCursor(WatchNextProgram.PROJECTION, contentValues)));
+ getProgramCursor(WatchNextProgram.PROJECTION, contentValues)), true);
WatchNextProgram clonedSampleProgram = new WatchNextProgram.Builder(sampleProgram).build();
- compareProgram(sampleProgram, clonedSampleProgram);
+ compareProgram(sampleProgram, clonedSampleProgram, true);
}
@Test
@@ -77,62 +94,38 @@
if (!BuildCompat.isAtLeastO()) {
return;
}
- WatchNextProgram fullyPopulatedProgram = new WatchNextProgram.Builder()
- .setTitle("Google")
- .setInternalProviderId("ID-4321")
- .setPreviewVideoUri(Uri.parse("http://example.com/preview-video.mpg"))
- .setLastPlaybackPositionMillis(0)
- .setDurationMillis(60 * 1000)
- .setAppLinkIntentUri(Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(
- Intent.URI_INTENT_SCHEME)))
- .setTransient(false)
- .setType(WatchNextPrograms.TYPE_MOVIE)
- .setWatchNextType(WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
- .setPosterArtAspectRatio(WatchNextPrograms.ASPECT_RATIO_2_3)
- .setThumbnailAspectRatio(WatchNextPrograms.ASPECT_RATIO_16_9)
- .setLogoUri(Uri.parse("http://example.com/program-logo.mpg"))
- .setAvailability(WatchNextPrograms.AVAILABILITY_AVAILABLE)
- .setStartingPrice("12.99 USD")
- .setOfferPrice("4.99 USD")
- .setReleaseDate("1997")
- .setItemCount(3)
- .setLive(false)
- .setInteractionType(WatchNextPrograms.INTERACTION_TYPE_LIKES)
- .setInteractionCount(10200)
- .setAuthor("author_name")
- .setReviewRatingStyle(WatchNextPrograms.REVIEW_RATING_STYLE_STARS)
- .setReviewRating("4.5")
- .setSearchable(false)
- .setThumbnailUri(Uri.parse("http://example.com/thumbnail.png"))
- .setAudioLanguages(new String [] {"eng", "kor"})
- .setCanonicalGenres(new String[] {TvContractCompat.Programs.Genres.MOVIES})
- .setContentRatings(new TvContentRating[] {
- TvContentRating.createRating("com.android.tv", "US_TV", "US_TV_Y7")})
- .setDescription("This is a sample program")
- .setEpisodeNumber("Pilot", 0)
- .setEpisodeTitle("Hello World")
- .setLongDescription("This is a longer description than the previous description")
- .setPosterArtUri(Uri.parse("http://example.com/poster.png"))
- .setSeasonNumber("The Final Season", 7)
- .setSeasonTitle("The Final Season")
- .setVideoHeight(1080)
- .setVideoWidth(1920)
- .setInternalProviderFlag1(0x4)
- .setInternalProviderFlag2(0x3)
- .setInternalProviderFlag3(0x2)
- .setInternalProviderFlag4(0x1)
- .setBrowsable(true)
- .setContentId("CID-8442")
- .build();
-
- ContentValues contentValues = fullyPopulatedProgram.toContentValues();
+ WatchNextProgram fullyPopulatedProgram = createFullyPopulatedWatchNextProgram();
+ ContentValues contentValues = fullyPopulatedProgram.toContentValues(true);
compareProgram(fullyPopulatedProgram,
WatchNextProgram.fromCursor(
- getProgramCursor(WatchNextProgram.PROJECTION, contentValues)));
+ getProgramCursor(WatchNextProgram.PROJECTION, contentValues)), true);
WatchNextProgram clonedFullyPopulatedProgram =
new WatchNextProgram.Builder(fullyPopulatedProgram).build();
- compareProgram(fullyPopulatedProgram, clonedFullyPopulatedProgram);
+ compareProgram(fullyPopulatedProgram, clonedFullyPopulatedProgram, true);
+ }
+
+ @Test
+ public void testChannelWithSystemContentProvider() {
+ if (!BuildCompat.isAtLeastO()) {
+ return;
+ }
+ if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+ return;
+ }
+ WatchNextProgram fullyPopulatedProgram = createFullyPopulatedWatchNextProgram();
+ ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver();
+ Uri watchNextProgramUri = resolver.insert(WatchNextPrograms.CONTENT_URI,
+ fullyPopulatedProgram.toContentValues());
+
+ WatchNextProgram programFromSystemDb;
+ try (Cursor cursor = resolver.query(watchNextProgramUri, null, null, null, null)) {
+ assertNotNull(cursor);
+ assertEquals(1, cursor.getCount());
+ cursor.moveToNext();
+ programFromSystemDb = WatchNextProgram.fromCursor(cursor);
+ }
+ compareProgram(fullyPopulatedProgram, programFromSystemDb, false);
}
@Test
@@ -198,16 +191,68 @@
WatchNextPrograms.COLUMN_REVIEW_RATING,
};
- ContentValues contentValues = previewProgram.toContentValues();
+ ContentValues contentValues = previewProgram.toContentValues(true);
compareProgram(previewProgram,
- WatchNextProgram.fromCursor(getProgramCursor(partialProjection, contentValues)));
+ WatchNextProgram.fromCursor(getProgramCursor(partialProjection, contentValues)),
+ true);
WatchNextProgram clonedFullyPopulatedProgram =
new WatchNextProgram.Builder(previewProgram).build();
- compareProgram(previewProgram, clonedFullyPopulatedProgram);
+ compareProgram(previewProgram, clonedFullyPopulatedProgram, true);
}
- private static void compareProgram(WatchNextProgram programA, WatchNextProgram programB) {
+ private static WatchNextProgram createFullyPopulatedWatchNextProgram() {
+ return new WatchNextProgram.Builder()
+ .setTitle("Google")
+ .setInternalProviderId("ID-4321")
+ .setPreviewVideoUri(Uri.parse("http://example.com/preview-video.mpg"))
+ .setLastPlaybackPositionMillis(0)
+ .setDurationMillis(60 * 1000)
+ .setAppLinkIntentUri(Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(
+ Intent.URI_INTENT_SCHEME)))
+ .setTransient(false)
+ .setType(WatchNextPrograms.TYPE_MOVIE)
+ .setWatchNextType(WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
+ .setPosterArtAspectRatio(WatchNextPrograms.ASPECT_RATIO_2_3)
+ .setThumbnailAspectRatio(WatchNextPrograms.ASPECT_RATIO_16_9)
+ .setLogoUri(Uri.parse("http://example.com/program-logo.mpg"))
+ .setAvailability(WatchNextPrograms.AVAILABILITY_AVAILABLE)
+ .setStartingPrice("12.99 USD")
+ .setOfferPrice("4.99 USD")
+ .setReleaseDate("1997")
+ .setItemCount(3)
+ .setLive(false)
+ .setInteractionType(WatchNextPrograms.INTERACTION_TYPE_LIKES)
+ .setInteractionCount(10200)
+ .setAuthor("author_name")
+ .setReviewRatingStyle(WatchNextPrograms.REVIEW_RATING_STYLE_STARS)
+ .setReviewRating("4.5")
+ .setSearchable(false)
+ .setThumbnailUri(Uri.parse("http://example.com/thumbnail.png"))
+ .setAudioLanguages(new String [] {"eng", "kor"})
+ .setCanonicalGenres(new String[] {TvContractCompat.Programs.Genres.MOVIES})
+ .setContentRatings(new TvContentRating[] {
+ TvContentRating.createRating("com.android.tv", "US_TV", "US_TV_Y7")})
+ .setDescription("This is a sample program")
+ .setEpisodeNumber("Pilot", 0)
+ .setEpisodeTitle("Hello World")
+ .setLongDescription("This is a longer description than the previous description")
+ .setPosterArtUri(Uri.parse("http://example.com/poster.png"))
+ .setSeasonNumber("The Final Season", 7)
+ .setSeasonTitle("The Final Season")
+ .setVideoHeight(1080)
+ .setVideoWidth(1920)
+ .setInternalProviderFlag1(0x4)
+ .setInternalProviderFlag2(0x3)
+ .setInternalProviderFlag3(0x2)
+ .setInternalProviderFlag4(0x1)
+ .setBrowsable(true)
+ .setContentId("CID-8442")
+ .build();
+ }
+
+ private static void compareProgram(WatchNextProgram programA, WatchNextProgram programB,
+ boolean includeIdAndProtectedFields) {
assertTrue(Arrays.equals(programA.getAudioLanguages(), programB.getAudioLanguages()));
assertTrue(Arrays.deepEquals(programA.getCanonicalGenres(), programB.getCanonicalGenres()));
assertTrue(Arrays.deepEquals(programA.getContentRatings(), programB.getContentRatings()));
@@ -216,7 +261,6 @@
assertEquals(programA.getEpisodeTitle(), programB.getEpisodeTitle());
assertEquals(programA.getLongDescription(), programB.getLongDescription());
assertEquals(programA.getPosterArtUri(), programB.getPosterArtUri());
- assertEquals(programA.getId(), programB.getId());
assertEquals(programA.getSeasonNumber(), programB.getSeasonNumber());
assertEquals(programA.getThumbnailUri(), programB.getThumbnailUri());
assertEquals(programA.getTitle(), programB.getTitle());
@@ -251,12 +295,17 @@
assertEquals(programA.getAuthor(), programB.getAuthor());
assertEquals(programA.getReviewRatingStyle(), programB.getReviewRatingStyle());
assertEquals(programA.getReviewRating(), programB.getReviewRating());
- assertEquals(programA.isBrowsable(), programB.isBrowsable());
assertEquals(programA.getContentId(), programB.getContentId());
-
- assertEquals(programA.toContentValues(), programB.toContentValues());
assertEquals(programA.toString(), programB.toString());
- assertEquals(programA, programB);
+ if (includeIdAndProtectedFields) {
+ // Skip row ID since the one from system DB has the valid ID while the other does not.
+ assertEquals(programA.getId(), programB.getId());
+ // When we insert a channel using toContentValues() to the system, we drop some
+ // protected fields since they only can be modified by system apps.
+ assertEquals(programA.isBrowsable(), programB.isBrowsable());
+ assertEquals(programA.toContentValues(), programB.toContentValues());
+ assertEquals(programA, programB);
+ }
}
private static MatrixCursor getProgramCursor(String[] projection, ContentValues contentValues) {
diff --git a/v13/AndroidManifest.xml b/v13/AndroidManifest.xml
index 65ef182..6eea520 100644
--- a/v13/AndroidManifest.xml
+++ b/v13/AndroidManifest.xml
@@ -17,6 +17,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="android.support.v13">
<uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.v13"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}"/>
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/v13/java/android/support/v13/app/ActivityCompat.java b/v13/java/android/support/v13/app/ActivityCompat.java
index 5c8ee42..b0c3c30 100644
--- a/v13/java/android/support/v13/app/ActivityCompat.java
+++ b/v13/java/android/support/v13/app/ActivityCompat.java
@@ -38,6 +38,10 @@
return DragAndDropPermissionsCompat.request(activity, dragEvent);
}
+ /**
+ * This class should not be instantiated, but the constructor must be
+ * visible for the class to be extended.
+ */
protected ActivityCompat() {
// Not publicly instantiable, but may be extended.
}
diff --git a/v14/preference/AndroidManifest.xml b/v14/preference/AndroidManifest.xml
index 74cff2e..95bab75 100644
--- a/v14/preference/AndroidManifest.xml
+++ b/v14/preference/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.v14.preference">
<uses-sdk android:minSdkVersion="14" />
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/v14/preference/NOTICES.md b/v14/preference/NOTICES.md
deleted file mode 100644
index a390782..0000000
--- a/v14/preference/NOTICES.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Change Log
-
-## [23.1.0](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v14/preference) (2015-09-28)
-
-**Breakage and deprecation notices:**
-
-- EditTextPreferenceDialogFragment
- - onAddEditTextToDialogView has been removed. Any code depending on overriding this method should
- be moved to onBindDialogView.
- - The EditText view is now expected to be present in the dialog layout file with the id
- @android:id/edit, and is no longer created in code.
diff --git a/v17/leanback/.classpath b/v17/leanback/.classpath
deleted file mode 100644
index f568681..0000000
--- a/v17/leanback/.classpath
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
- <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
- <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="api21"/>
- <classpathentry kind="src" path="api23"/>
- <classpathentry kind="src" path="jbmr2"/>
- <classpathentry kind="src" path="common"/>
- <classpathentry kind="src" path="kitkat"/>
- <classpathentry kind="src" path="gen"/>
- <classpathentry kind="output" path="bin/classes"/>
-</classpath>
diff --git a/v17/leanback/.gitignore b/v17/leanback/.gitignore
deleted file mode 100644
index c3c25e0..0000000
--- a/v17/leanback/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-.settings
-bin
-libs
-gen
diff --git a/v17/leanback/.project b/v17/leanback/.project
deleted file mode 100644
index 9d191cd..0000000
--- a/v17/leanback/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>android-support-v17-leanback</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ApkBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
diff --git a/v17/leanback/Android.mk b/v17/leanback/Android.mk
index 6e62436..dbfe43a 100644
--- a/v17/leanback/Android.mk
+++ b/v17/leanback/Android.mk
@@ -46,31 +46,3 @@
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Documentation
-# ===========================================================
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-v17-leanback
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_MODULE_TAGS := optional
-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 := external/doclava/res/assets/templates-sdk
-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)
diff --git a/v17/leanback/AndroidManifest.xml b/v17/leanback/AndroidManifest.xml
index ded4ce8..9176231 100644
--- a/v17/leanback/AndroidManifest.xml
+++ b/v17/leanback/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.v17.leanback">
<uses-sdk android:minSdkVersion="17"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/v17/leanback/README.txt b/v17/leanback/README.txt
deleted file mode 100644
index f3dbe92..0000000
--- a/v17/leanback/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-Library Project including Leanback framework support.
diff --git a/v17/leanback/project.properties b/v17/leanback/project.properties
deleted file mode 100644
index b2ef7dc..0000000
--- a/v17/leanback/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-23
-android.library=true
diff --git a/v17/leanback/res/layout/lb_playback_controls.xml b/v17/leanback/res/layout/lb_playback_controls.xml
index dda7e06..df2be51 100644
--- a/v17/leanback/res/layout/lb_playback_controls.xml
+++ b/v17/leanback/res/layout/lb_playback_controls.xml
@@ -30,55 +30,43 @@
android:layoutDirection="ltr"
android:progressDrawable="@drawable/lb_playback_progress_bar" />
- <android.support.v17.leanback.widget.PersistentFocusWrapper
- android:id="@+id/controls_container_focus_wrapper"
+ <FrameLayout
+ android:id="@+id/controls_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
+ <android.support.v17.leanback.widget.ControlBar
+ android:id="@+id/control_bar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layoutDirection="ltr"
+ android:layout_gravity="center_horizontal"
+ android:orientation="horizontal" />
+
<FrameLayout
- android:id="@+id/controls_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" >
+ android:id="@+id/more_actions_dock"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end" />
- <android.support.v17.leanback.widget.PersistentFocusWrapper
- android:id="@+id/control_bar_focus_wrapper"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal">
+ <TextView
+ android:id="@+id/current_time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|left"
+ android:layout_marginStart="@dimen/lb_playback_current_time_margin_start"
+ android:paddingTop="@dimen/lb_playback_time_padding_top"
+ style="?attr/playbackControlsTimeStyle" />
- <android.support.v17.leanback.widget.ControlBar
- android:id="@+id/control_bar"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layoutDirection="ltr"
- android:orientation="horizontal" />
- </android.support.v17.leanback.widget.PersistentFocusWrapper>
+ <TextView
+ android:id="@+id/total_time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|right"
+ android:layout_marginEnd="@dimen/lb_playback_total_time_margin_end"
+ android:paddingTop="@dimen/lb_playback_time_padding_top"
+ style="?attr/playbackControlsTimeStyle" />
- <FrameLayout
- android:id="@+id/more_actions_dock"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end" />
-
- <TextView
- android:id="@+id/current_time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|left"
- android:layout_marginStart="@dimen/lb_playback_current_time_margin_start"
- android:paddingTop="@dimen/lb_playback_time_padding_top"
- style="?attr/playbackControlsTimeStyle" />
-
- <TextView
- android:id="@+id/total_time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|right"
- android:layout_marginEnd="@dimen/lb_playback_total_time_margin_end"
- android:paddingTop="@dimen/lb_playback_time_padding_top"
- style="?attr/playbackControlsTimeStyle" />
-
- </FrameLayout>
- </android.support.v17.leanback.widget.PersistentFocusWrapper>
+ </FrameLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/v17/leanback/res/values-da/strings.xml b/v17/leanback/res/values-da/strings.xml
index cf0e26e..3881fd6 100644
--- a/v17/leanback/res/values-da/strings.xml
+++ b/v17/leanback/res/values-da/strings.xml
@@ -49,7 +49,7 @@
<string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Deaktiver undertekster"</string>
<string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Tilføj billedet i billedtilstand"</string>
<string name="lb_playback_controls_shown" msgid="6382160135512023238">"Knapperne til afspilning er synlige"</string>
- <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Knapperne til afspilning er skjult. Tryk på D-pad for at se dem"</string>
+ <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Knapperne til afspilning er skjult. Tryk på D-pad\'en for at se dem"</string>
<string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Afslut"</string>
<string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Fortsæt"</string>
<string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
diff --git a/v17/leanback/res/values-gu/strings.xml b/v17/leanback/res/values-gu/strings.xml
index bc021cb..4285f52 100644
--- a/v17/leanback/res/values-gu/strings.xml
+++ b/v17/leanback/res/values-gu/strings.xml
@@ -48,10 +48,8 @@
<string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ઉપશીર્ષક સક્ષમ કરો"</string>
<string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"વિગતવાર ઉપશીર્ષકોને અક્ષમ કરો"</string>
<string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ચિત્ર મોડમાં ચિત્ર દાખલ કરો"</string>
- <!-- no translation found for lb_playback_controls_shown (6382160135512023238) -->
- <skip />
- <!-- no translation found for lb_playback_controls_hidden (8940984081242033574) -->
- <skip />
+ <string name="lb_playback_controls_shown" msgid="6382160135512023238">"મીડિયા નિયંત્રણો બતાવેલા છે"</string>
+ <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"મીડિયા નિયંત્રણો છુપાયેલા છે, તે બતાવવા માટે d-પૅડ દબાવો"</string>
<string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"સમાપ્ત કરો"</string>
<string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ચાલુ રાખો"</string>
<string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
diff --git a/v17/leanback/res/values-iw/strings.xml b/v17/leanback/res/values-iw/strings.xml
index 4d6d7f4..be2740b 100644
--- a/v17/leanback/res/values-iw/strings.xml
+++ b/v17/leanback/res/values-iw/strings.xml
@@ -48,10 +48,8 @@
<string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"הפעל כתוביות"</string>
<string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"השבת כתוביות"</string>
<string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"הזן את התמונה במצב תמונה"</string>
- <!-- no translation found for lb_playback_controls_shown (6382160135512023238) -->
- <skip />
- <!-- no translation found for lb_playback_controls_hidden (8940984081242033574) -->
- <skip />
+ <string name="lb_playback_controls_shown" msgid="6382160135512023238">"פקדי המדיה מוצגים"</string>
+ <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"פקדי המדיה מוסתרים. הקש על ה-d-pad כדי להציג אותם"</string>
<string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"סיום"</string>
<string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"המשך"</string>
<string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
diff --git a/v17/leanback/res/values-te/strings.xml b/v17/leanback/res/values-te/strings.xml
index e1b3ba2..f9cb803 100644
--- a/v17/leanback/res/values-te/strings.xml
+++ b/v17/leanback/res/values-te/strings.xml
@@ -48,10 +48,8 @@
<string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"సంవృత శీర్షికలను ప్రారంభించు"</string>
<string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"సంవృత శీర్షికలను నిలిపివేయి"</string>
<string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"చిత్రంలో చిత్రం మోడ్లోకి ప్రవేశించండి"</string>
- <!-- no translation found for lb_playback_controls_shown (6382160135512023238) -->
- <skip />
- <!-- no translation found for lb_playback_controls_hidden (8940984081242033574) -->
- <skip />
+ <string name="lb_playback_controls_shown" msgid="6382160135512023238">"మీడియా నియంత్రణలు చూపబడ్డాయి"</string>
+ <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"మీడియా నియంత్రణలు దాచబడ్డాయి, చూపించడానికి d-ప్యాడ్ నొక్కండి"</string>
<string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ముగించు"</string>
<string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"కొనసాగించు"</string>
<string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
diff --git a/v17/leanback/res/values-uz/strings.xml b/v17/leanback/res/values-uz/strings.xml
index c491358..5a4151f 100644
--- a/v17/leanback/res/values-uz/strings.xml
+++ b/v17/leanback/res/values-uz/strings.xml
@@ -48,8 +48,8 @@
<string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Taglavhalarni yoqish"</string>
<string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Taglavhalarni o‘chirib qo‘yish"</string>
<string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Tasvir ichida tasvir rejimiga kirish"</string>
- <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Boshqaruv elementlari ko‘rsatilgan"</string>
- <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Boshqaruv elementlari yashirilgan, ko‘rsatish uchun D-pad tugmasini bosing"</string>
+ <string name="lb_playback_controls_shown" msgid="6382160135512023238">"Boshqaruv elementlari ochiq"</string>
+ <string name="lb_playback_controls_hidden" msgid="8940984081242033574">"Boshqaruv elementlari berkitilgan, ochish uchun D-pad tugmasini bosing"</string>
<string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Tugatish"</string>
<string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Davom etish"</string>
<string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
diff --git a/v17/leanback/src/.readme b/v17/leanback/src/.readme
deleted file mode 100644
index 4bcebad..0000000
--- a/v17/leanback/src/.readme
+++ /dev/null
@@ -1,2 +0,0 @@
-This hidden file is there to ensure there is an src folder.
-Once we support binary library this will go away.
\ No newline at end of file
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
index 8ab731f..ff5ef2e 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
@@ -26,6 +26,7 @@
/**
* @hide
*/
+@SuppressWarnings("FragmentNotInstantiable")
class BaseFragment extends BrandedFragment {
/**
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
index 7d08738..62ee0d4 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
@@ -29,6 +29,7 @@
/**
* @hide
*/
+@SuppressWarnings("FragmentNotInstantiable")
class BaseSupportFragment extends BrandedSupportFragment {
/**
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
index ed20153..6672398 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
@@ -1468,6 +1468,7 @@
getChildFragmentManager().beginTransaction()
.replace(R.id.scale_frame, new Fragment()).commit();
gridView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @SuppressWarnings("ReferenceEquality")
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
index 43e10b2..5d0cee8 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
@@ -1471,6 +1471,7 @@
getChildFragmentManager().beginTransaction()
.replace(R.id.scale_frame, new Fragment()).commit();
gridView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @SuppressWarnings("ReferenceEquality")
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java b/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java
index 564c3c7..aa2a51f 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java
@@ -185,7 +185,7 @@
// No need to save/restore the logo resource ID, because the logo animation will not appear when
// the fragment is restored.
private int mLogoResourceId;
- boolean mEnterTransitionFinished;
+ boolean mLogoAnimationFinished;
int mCurrentPageIndex;
private AnimatorSet mAnimator;
@@ -193,7 +193,7 @@
private final OnClickListener mOnClickListener = new OnClickListener() {
@Override
public void onClick(View view) {
- if (!mEnterTransitionFinished) {
+ if (!mLogoAnimationFinished) {
// Do not change page until the enter transition finishes.
return;
}
@@ -208,7 +208,7 @@
private final OnKeyListener mOnKeyListener = new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (!mEnterTransitionFinished) {
+ if (!mLogoAnimationFinished) {
// Ignore key event until the enter transition finishes.
return keyCode != KeyEvent.KEYCODE_BACK;
}
@@ -241,13 +241,28 @@
}
};
- void moveToPreviousPage() {
+ /**
+ * Navigates to the previous page.
+ */
+ protected void moveToPreviousPage() {
+ if (!mLogoAnimationFinished) {
+ // Ignore if the logo enter transition is in progress.
+ return;
+ }
if (mCurrentPageIndex > 0) {
--mCurrentPageIndex;
onPageChangedInternal(mCurrentPageIndex + 1);
}
}
- void moveToNextPage() {
+
+ /**
+ * Navigates to the next page.
+ */
+ protected void moveToNextPage() {
+ if (!mLogoAnimationFinished) {
+ // Ignore if the logo enter transition is in progress.
+ return;
+ }
if (mCurrentPageIndex < getPageCount() - 1) {
++mCurrentPageIndex;
onPageChangedInternal(mCurrentPageIndex - 1);
@@ -281,7 +296,7 @@
}
if (savedInstanceState == null) {
mCurrentPageIndex = 0;
- mEnterTransitionFinished = false;
+ mLogoAnimationFinished = false;
mPageIndicator.onPageSelected(0, false);
view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
@Override
@@ -294,7 +309,7 @@
}
});
} else {
- mEnterTransitionFinished = true;
+ mLogoAnimationFinished = true;
mCurrentPageIndex = savedInstanceState.getInt(KEY_CURRENT_PAGE_INDEX);
initializeViews(view);
}
@@ -463,10 +478,19 @@
// Header views.
mTitleView.setText(getPageTitle(mCurrentPageIndex));
mDescriptionView.setText(getPageDescription(mCurrentPageIndex));
+ onLogoAnimationFinished();
+ }
+
+ /**
+ * Called immediately after fragment views become visible. This method gives subclasses a chance
+ * to initialize themselves. If a logo animation is specified, calling this method is delayed
+ * until after the logo animation is complete.
+ */
+ protected void onLogoAnimationFinished() {
}
void startEnterAnimation() {
- mEnterTransitionFinished = true;
+ mLogoAnimationFinished = true;
initializeViews(getView());
List<Animator> animators = new ArrayList<>();
final Context context = FragmentUtil.getContext(this);
@@ -525,6 +549,15 @@
}
/**
+ * Returns whether the logo enter transition is finished.
+ *
+ * @return {@code true} if the logo enter transition is finished, {@code false} otherwise
+ */
+ protected final boolean isLogoAnimationFinished() {
+ return mLogoAnimationFinished;
+ }
+
+ /**
* Returns the page count.
*
* @return The page count.
diff --git a/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java
index 5df8f41..59a0f5f 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java
@@ -188,7 +188,7 @@
// No need to save/restore the logo resource ID, because the logo animation will not appear when
// the fragment is restored.
private int mLogoResourceId;
- boolean mEnterTransitionFinished;
+ boolean mLogoAnimationFinished;
int mCurrentPageIndex;
private AnimatorSet mAnimator;
@@ -196,7 +196,7 @@
private final OnClickListener mOnClickListener = new OnClickListener() {
@Override
public void onClick(View view) {
- if (!mEnterTransitionFinished) {
+ if (!mLogoAnimationFinished) {
// Do not change page until the enter transition finishes.
return;
}
@@ -211,7 +211,7 @@
private final OnKeyListener mOnKeyListener = new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (!mEnterTransitionFinished) {
+ if (!mLogoAnimationFinished) {
// Ignore key event until the enter transition finishes.
return keyCode != KeyEvent.KEYCODE_BACK;
}
@@ -244,13 +244,28 @@
}
};
- void moveToPreviousPage() {
+ /**
+ * Navigates to the previous page.
+ */
+ protected void moveToPreviousPage() {
+ if (!mLogoAnimationFinished) {
+ // Ignore if the logo enter transition is in progress.
+ return;
+ }
if (mCurrentPageIndex > 0) {
--mCurrentPageIndex;
onPageChangedInternal(mCurrentPageIndex + 1);
}
}
- void moveToNextPage() {
+
+ /**
+ * Navigates to the next page.
+ */
+ protected void moveToNextPage() {
+ if (!mLogoAnimationFinished) {
+ // Ignore if the logo enter transition is in progress.
+ return;
+ }
if (mCurrentPageIndex < getPageCount() - 1) {
++mCurrentPageIndex;
onPageChangedInternal(mCurrentPageIndex - 1);
@@ -284,7 +299,7 @@
}
if (savedInstanceState == null) {
mCurrentPageIndex = 0;
- mEnterTransitionFinished = false;
+ mLogoAnimationFinished = false;
mPageIndicator.onPageSelected(0, false);
view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
@Override
@@ -297,7 +312,7 @@
}
});
} else {
- mEnterTransitionFinished = true;
+ mLogoAnimationFinished = true;
mCurrentPageIndex = savedInstanceState.getInt(KEY_CURRENT_PAGE_INDEX);
initializeViews(view);
}
@@ -466,10 +481,19 @@
// Header views.
mTitleView.setText(getPageTitle(mCurrentPageIndex));
mDescriptionView.setText(getPageDescription(mCurrentPageIndex));
+ onLogoAnimationFinished();
+ }
+
+ /**
+ * Called immediately after fragment views become visible. This method gives subclasses a chance
+ * to initialize themselves. If a logo animation is specified, calling this method is delayed
+ * until after the logo animation is complete.
+ */
+ protected void onLogoAnimationFinished() {
}
void startEnterAnimation() {
- mEnterTransitionFinished = true;
+ mLogoAnimationFinished = true;
initializeViews(getView());
List<Animator> animators = new ArrayList<>();
final Context context = getContext();
@@ -528,6 +552,15 @@
}
/**
+ * Returns whether the logo enter transition is finished.
+ *
+ * @return {@code true} if the logo enter transition is finished, {@code false} otherwise
+ */
+ protected final boolean isLogoAnimationFinished() {
+ return mLogoAnimationFinished;
+ }
+
+ /**
* Returns the page count.
*
* @return The page count.
diff --git a/v17/leanback/src/android/support/v17/leanback/media/MediaPlayerGlue.java b/v17/leanback/src/android/support/v17/leanback/media/MediaPlayerGlue.java
index dd0127c..204f922 100644
--- a/v17/leanback/src/android/support/v17/leanback/media/MediaPlayerGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/media/MediaPlayerGlue.java
@@ -382,17 +382,12 @@
* @see MediaPlayer#setDataSource(String)
*/
public boolean setMediaSource(Uri uri) {
- if (mMediaSourceUri != null && mMediaSourceUri.equals(uri)) {
+ if (mMediaSourceUri != null ? mMediaSourceUri.equals(uri) : uri == null) {
return false;
}
- if (mMediaSourceUri != null || mMediaSourcePath != null) {
- mMediaSourceUri = uri;
- mMediaSourcePath = null;
- prepareMediaForPlaying();
- } else {
- mMediaSourceUri = uri;
- prepareMediaForPlaying();
- }
+ mMediaSourceUri = uri;
+ mMediaSourcePath = null;
+ prepareMediaForPlaying();
return true;
}
@@ -404,17 +399,12 @@
* @see MediaPlayer#setDataSource(String)
*/
public boolean setMediaSource(String path) {
- if (mMediaSourcePath != null && mMediaSourcePath.equals(mMediaSourcePath)) {
+ if (mMediaSourcePath != null ? mMediaSourcePath.equals(path) : path == null) {
return false;
}
- if (mMediaSourceUri != null || mMediaSourcePath != null) {
- mMediaSourceUri = null;
- mMediaSourcePath = path;
- prepareMediaForPlaying();
- } else {
- mMediaSourcePath = path;
- prepareMediaForPlaying();
- }
+ mMediaSourceUri = null;
+ mMediaSourcePath = path;
+ prepareMediaForPlaying();
return true;
}
diff --git a/v17/leanback/src/android/support/v17/leanback/util/StateMachine.java b/v17/leanback/src/android/support/v17/leanback/util/StateMachine.java
index a00bbc5..b9d2f2d 100644
--- a/v17/leanback/src/android/support/v17/leanback/util/StateMachine.java
+++ b/v17/leanback/src/android/support/v17/leanback/util/StateMachine.java
@@ -109,11 +109,6 @@
public final int getStatus() {
return mStatus;
}
-
- @Override
- public final boolean equals(Object other) {
- return this == other;
- }
}
private boolean mSorted = true;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java
index c607d64..196fa93 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java
@@ -372,7 +372,7 @@
* @param position The index of the child view to display.
*/
public void setSelectedMediaItemNumberView(int position) {
- if (position >= 0 & position < mMediaItemNumberViewFlipper.getChildCount()) {
+ if (position >= 0 && position < mMediaItemNumberViewFlipper.getChildCount()) {
mMediaItemNumberViewFlipper.setDisplayedChild(position);
}
}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ActionPresenterSelector.java b/v17/leanback/src/android/support/v17/leanback/widget/ActionPresenterSelector.java
index 1064c45..1ced4d4 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ActionPresenterSelector.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ActionPresenterSelector.java
@@ -55,7 +55,7 @@
}
}
- class OneLineActionPresenter extends Presenter {
+ static class OneLineActionPresenter extends Presenter {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
View v = LayoutInflater.from(parent.getContext())
@@ -77,7 +77,7 @@
}
}
- class TwoLineActionPresenter extends Presenter {
+ static class TwoLineActionPresenter extends Presenter {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
View v = LayoutInflater.from(parent.getContext())
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ArrayObjectAdapter.java b/v17/leanback/src/android/support/v17/leanback/widget/ArrayObjectAdapter.java
index 68190d4..b31d30a 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ArrayObjectAdapter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ArrayObjectAdapter.java
@@ -90,7 +90,7 @@
/**
* Inserts an item into this adapter at the specified index.
- * If the index is >= {@link #size} an exception will be thrown.
+ * If the index is > {@link #size} an exception will be thrown.
*
* @param index The index at which the item should be inserted.
* @param item The item to insert into the adapter.
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ControlBar.java b/v17/leanback/src/android/support/v17/leanback/widget/ControlBar.java
index 7146371..be5fd83 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ControlBar.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ControlBar.java
@@ -17,8 +17,11 @@
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.LinearLayout;
+import java.util.ArrayList;
+
class ControlBar extends LinearLayout {
public interface OnChildFocusedListener {
@@ -27,6 +30,7 @@
private int mChildMarginFromCenter;
private OnChildFocusedListener mOnChildFocusedListener;
+ int mLastFocusIndex = -1;
public ControlBar(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -37,13 +41,28 @@
}
@Override
- public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+ protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
if (getChildCount() > 0) {
- if (getChildAt(getChildCount() / 2).requestFocus(direction, previouslyFocusedRect)) {
+ int index = mLastFocusIndex >= 0 && mLastFocusIndex < getChildCount()
+ ? mLastFocusIndex : getChildCount() / 2;
+ if (getChildAt(index).requestFocus(direction, previouslyFocusedRect)) {
return true;
}
}
- return super.requestFocus(direction, previouslyFocusedRect);
+ return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
+ }
+
+ @Override
+ public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+ if ((direction == ViewGroup.FOCUS_UP || direction == ViewGroup.FOCUS_DOWN)) {
+ if (mLastFocusIndex >= 0 && mLastFocusIndex < getChildCount()) {
+ views.add(getChildAt(mLastFocusIndex));
+ } else if (getChildCount() > 0) {
+ views.add(getChildAt(getChildCount() / 2));
+ }
+ } else {
+ super.addFocusables(views, direction, focusableMode);
+ }
}
public void setOnChildFocusedListener(OnChildFocusedListener listener) {
@@ -57,6 +76,7 @@
@Override
public void requestChildFocus (View child, View focused) {
super.requestChildFocus(child, focused);
+ mLastFocusIndex = indexOfChild(child);
if (mOnChildFocusedListener != null) {
mOnChildFocusedListener.onChildFocusedListener(child, focused);
}
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 0bd03f1..50f0da3 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
@@ -182,8 +182,8 @@
void setSharedElementEnterTransition(Activity activity, String sharedElementName,
long timeoutMs) {
- if (activity == null && !TextUtils.isEmpty(sharedElementName)
- || activity != null && TextUtils.isEmpty(sharedElementName)) {
+ if ((activity == null && !TextUtils.isEmpty(sharedElementName))
+ || (activity != null && TextUtils.isEmpty(sharedElementName))) {
throw new IllegalArgumentException();
}
if (activity == mActivityToRunTransition
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
index 728d31f..988a9fc 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
@@ -251,7 +251,7 @@
}
}
- class HeaderFocusAnimator extends FocusAnimator {
+ static class HeaderFocusAnimator extends FocusAnimator {
ItemBridgeAdapter.ViewHolder mViewHolder;
HeaderFocusAnimator(View view, float scale, int duration) {
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 570a7f2..c2d57b6 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
@@ -74,8 +74,8 @@
public void setSharedElementEnterTransition(Activity activity, String sharedElementName,
long timeoutMs) {
- if (activity == null && !TextUtils.isEmpty(sharedElementName)
- || activity != null && TextUtils.isEmpty(sharedElementName)) {
+ if ((activity == null && !TextUtils.isEmpty(sharedElementName))
+ || (activity != null && TextUtils.isEmpty(sharedElementName))) {
throw new IllegalArgumentException();
}
if (activity == mActivityToRunTransition
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
index 9098743..c2780be 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -1655,14 +1655,14 @@
? Gravity.getAbsoluteGravity(mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK,
View.LAYOUT_DIRECTION_RTL)
: mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
- if (mOrientation == HORIZONTAL && verticalGravity == Gravity.TOP
- || mOrientation == VERTICAL && horizontalGravity == Gravity.LEFT) {
+ if ((mOrientation == HORIZONTAL && verticalGravity == Gravity.TOP)
+ || (mOrientation == VERTICAL && horizontalGravity == Gravity.LEFT)) {
// do nothing
- } else if (mOrientation == HORIZONTAL && verticalGravity == Gravity.BOTTOM
- || mOrientation == VERTICAL && horizontalGravity == Gravity.RIGHT) {
+ } else if ((mOrientation == HORIZONTAL && verticalGravity == Gravity.BOTTOM)
+ || (mOrientation == VERTICAL && horizontalGravity == Gravity.RIGHT)) {
startSecondary += getRowSizeSecondary(rowIndex) - sizeSecondary;
- } else if (mOrientation == HORIZONTAL && verticalGravity == Gravity.CENTER_VERTICAL
- || mOrientation == VERTICAL && horizontalGravity == Gravity.CENTER_HORIZONTAL) {
+ } else if ((mOrientation == HORIZONTAL && verticalGravity == Gravity.CENTER_VERTICAL)
+ || (mOrientation == VERTICAL && horizontalGravity == Gravity.CENTER_HORIZONTAL)) {
startSecondary += (getRowSizeSecondary(rowIndex) - sizeSecondary) / 2;
}
int left, top, right, bottom;
@@ -2418,7 +2418,7 @@
public void setSelection(int position, int subposition, boolean smooth,
int primaryScrollExtra) {
- if (mFocusPosition != position && position != NO_POSITION
+ if ((mFocusPosition != position && position != NO_POSITION)
|| subposition != mSubFocusPosition || primaryScrollExtra != mPrimaryScrollExtra) {
scrollToSelection(position, subposition, smooth, primaryScrollExtra);
}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
index 62ff2d7..632c287 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
@@ -13,15 +13,14 @@
*/
package android.support.v17.leanback.widget;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes;
import android.support.v17.leanback.R;
import android.support.v4.content.ContextCompat;
-
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
import android.text.InputType;
import java.util.List;
@@ -201,10 +200,10 @@
mTitle = mContext.getString(R.string.lb_guidedaction_continue_title);
} else if (id == ACTION_ID_YES) {
mId = ACTION_ID_YES;
- mTitle = mContext.getString(android.R.string.yes);
+ mTitle = mContext.getString(android.R.string.ok);
} else if (id == ACTION_ID_NO) {
mId = ACTION_ID_NO;
- mTitle = mContext.getString(android.R.string.no);
+ mTitle = mContext.getString(android.R.string.cancel);
}
return (B) this;
}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacetHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacetHelper.java
index 3230848..b0e681a 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacetHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacetHelper.java
@@ -55,8 +55,9 @@
}
}
if (facet.mOffsetPercent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) {
- alignPos += ((view == itemView ? p.getOpticalWidth(view) : view.getWidth())
- * facet.mOffsetPercent) / 100f;
+ alignPos += Math.round(
+ ((view == itemView ? p.getOpticalWidth(view) : view.getWidth())
+ * facet.mOffsetPercent) / 100f);
}
if (itemView != view) {
sRect.left = alignPos;
@@ -74,8 +75,9 @@
}
}
if (facet.mOffsetPercent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) {
- alignPos += ((view == itemView ? p.getOpticalHeight(view) : view.getHeight())
- * facet.mOffsetPercent) / 100f;
+ alignPos += Math.round(
+ ((view == itemView ? p.getOpticalHeight(view) : view.getHeight())
+ * facet.mOffsetPercent) / 100f);
}
if (itemView != view) {
sRect.top = alignPos;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
index 79d1407..01e9de3 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
@@ -63,7 +63,6 @@
StringBuilder mCurrentTimeStringBuilder = new StringBuilder();
int mCurrentTimeMarginStart;
int mTotalTimeMarginEnd;
- final PersistentFocusWrapper mControlsFocusWrapper;
ViewHolder(View rootView) {
super(rootView);
@@ -91,7 +90,6 @@
((MarginLayoutParams) mCurrentTime.getLayoutParams()).getMarginStart();
mTotalTimeMarginEnd =
((MarginLayoutParams) mTotalTime.getLayoutParams()).getMarginEnd();
- mControlsFocusWrapper = (PersistentFocusWrapper) mControlBar.getParent();
}
void showMoreActions(boolean show) {
@@ -298,7 +296,6 @@
}
public void resetFocus(ViewHolder vh) {
- vh.mControlsFocusWrapper.clearSelection();
vh.mControlBar.requestFocus();
}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java
index 3556057..4bc00c6 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java
@@ -213,6 +213,7 @@
return space;
}
+ @SuppressWarnings("ReferenceEquality")
protected static float getFontDescent(TextView textView, Paint fontMeasurePaint) {
if (fontMeasurePaint.getTextSize() != textView.getTextSize()) {
fontMeasurePaint.setTextSize(textView.getTextSize());
diff --git a/v17/leanback/tests/README.txt b/v17/leanback/tests/README.txt
deleted file mode 100644
index cf64349..0000000
--- a/v17/leanback/tests/README.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Test project for support leanback.
-
-RUN TESTS
-Using gradle
-1. cd frameworks/support
-2. ./gradlew support-leanback-v17:connectedCheck --info
-
-Using adb
-adb shell am instrument -w android.support.v17.leanback.tests/android.test.InstrumentationTestRunner
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayFragmentTest.java
index be78f79..08fa838 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayFragmentTest.java
@@ -20,7 +20,9 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.support.test.filters.FlakyTest;
import android.support.test.filters.MediumTest;
+import android.support.test.filters.Suppress;
import android.support.test.runner.AndroidJUnit4;
import android.support.v17.leanback.test.R;
import android.view.View;
@@ -43,6 +45,8 @@
assertTrue(fragment.getView().hasFocus());
}
+ @FlakyTest
+ @Suppress
@Test
public void alignmentRowToBottom() throws Throwable {
launchAndWaitActivity(PlaybackOverlayTestFragment.class,
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ControlBarTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ControlBarTest.java
new file mode 100644
index 0000000..ea5b400
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ControlBarTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.widget;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertSame;
+import static junit.framework.Assert.assertTrue;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+
+public class ControlBarTest {
+
+ @Test
+ public void defaultFocus() {
+ Context context = InstrumentationRegistry.getTargetContext();
+ final ControlBar bar = new ControlBar(context, null);
+ final TextView v1 = new Button(context);
+ bar.addView(v1, 100, 100);
+ final TextView v2 = new Button(context);
+ bar.addView(v2, 100, 100);
+ final TextView v3 = new Button(context);
+ bar.addView(v3, 100, 100);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ bar.requestFocus(View.FOCUS_DOWN);
+ }
+ }
+ );
+ assertTrue(v2.hasFocus());
+ }
+
+ @Test
+ public void persistFocus() {
+ Context context = InstrumentationRegistry.getTargetContext();
+ final LinearLayout rootView = new LinearLayout(context);
+ final ControlBar bar = new ControlBar(context, null);
+ rootView.addView(bar, 800, 100);
+ final Button barSibling = new Button(context);
+ rootView.addView(barSibling, 100, 100);
+ final TextView v1 = new Button(context);
+ bar.addView(v1, 100, 100);
+ final TextView v2 = new Button(context);
+ bar.addView(v2, 100, 100);
+ final TextView v3 = new Button(context);
+ bar.addView(v3, 100, 100);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ v3.requestFocus(View.FOCUS_DOWN);
+ }
+ }
+ );
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ barSibling.requestFocus();
+ }
+ }
+ );
+ assertFalse(bar.hasFocus());
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ bar.requestFocus(View.FOCUS_RIGHT);
+ }
+ }
+ );
+ assertTrue(v3.hasFocus());
+ }
+
+ @Test
+ public void getFocusables() {
+ Context context = InstrumentationRegistry.getTargetContext();
+ final LinearLayout rootView = new LinearLayout(context);
+ final ControlBar bar = new ControlBar(context, null);
+ rootView.addView(bar, 800, 100);
+ final Button barSibling = new Button(context);
+ rootView.addView(barSibling, 100, 100);
+ final TextView v1 = new Button(context);
+ bar.addView(v1, 100, 100);
+ final TextView v2 = new Button(context);
+ bar.addView(v2, 100, 100);
+ final TextView v3 = new Button(context);
+ bar.addView(v3, 100, 100);
+
+ ArrayList<View> focusables = new ArrayList();
+ bar.addFocusables(focusables, View.FOCUS_DOWN);
+ assertEquals(1, focusables.size());
+ assertSame(focusables.get(0), v2);
+ focusables.clear();
+ bar.addFocusables(focusables, View.FOCUS_UP);
+ assertEquals(1, focusables.size());
+ assertSame(focusables.get(0), v2);
+ focusables.clear();
+ bar.addFocusables(focusables, View.FOCUS_LEFT);
+ assertEquals(3, focusables.size());
+ focusables.clear();
+ bar.addFocusables(focusables, View.FOCUS_RIGHT);
+ assertEquals(3, focusables.size());
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ v3.requestFocus(View.FOCUS_DOWN);
+ }
+ }
+ );
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ barSibling.requestFocus();
+ }
+ }
+ );
+ assertFalse(bar.hasFocus());
+ focusables.clear();
+ bar.addFocusables(focusables, View.FOCUS_DOWN);
+ assertEquals(1, focusables.size());
+ assertSame(focusables.get(0), v3);
+ focusables.clear();
+ bar.addFocusables(focusables, View.FOCUS_UP);
+ assertEquals(1, focusables.size());
+ assertSame(focusables.get(0), v3);
+ focusables.clear();
+ bar.addFocusables(focusables, View.FOCUS_LEFT);
+ assertEquals(3, focusables.size());
+ focusables.clear();
+ bar.addFocusables(focusables, View.FOCUS_RIGHT);
+ assertEquals(3, focusables.size());
+
+ }
+}
diff --git a/v4/AndroidManifest.xml b/v4/AndroidManifest.xml
index 642a916..470f5c2 100644
--- a/v4/AndroidManifest.xml
+++ b/v4/AndroidManifest.xml
@@ -17,6 +17,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="android.support.v4">
<uses-sdk android:minSdkVersion="14" tools:overrideLibrary="android.support.v4"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/v7/appcompat/.classpath b/v7/appcompat/.classpath
deleted file mode 100644
index a4763d1..0000000
--- a/v7/appcompat/.classpath
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="gen"/>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
- <classpathentry kind="output" path="bin/classes"/>
-</classpath>
diff --git a/v7/appcompat/.gitignore b/v7/appcompat/.gitignore
deleted file mode 100644
index c3c25e0..0000000
--- a/v7/appcompat/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-.settings
-bin
-libs
-gen
diff --git a/v7/appcompat/.project b/v7/appcompat/.project
deleted file mode 100644
index 957d33d..0000000
--- a/v7/appcompat/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>android-support-v7-appcompat</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ApkBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
diff --git a/v7/appcompat/AndroidManifest.xml b/v7/appcompat/AndroidManifest.xml
index c1ff659..7de91ff 100644
--- a/v7/appcompat/AndroidManifest.xml
+++ b/v7/appcompat/AndroidManifest.xml
@@ -18,6 +18,7 @@
package="android.support.v7.appcompat">
<uses-sdk android:minSdkVersion="14"
tools:overrideLibrary="android.support.graphics.drawable.animated"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application/>
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/v7/appcompat/README.txt b/v7/appcompat/README.txt
deleted file mode 100644
index 8e8de05..0000000
--- a/v7/appcompat/README.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Library Project including compatibility ActionBar.
-
-This can be used by an Android project to provide
-access to ActionBar on applications running on API 7+.
-
-There is technically no source, but the src folder is necessary
-to ensure that the build system works. The content is actually
-located in libs/android-support-v7-appcompat.jar.
-The accompanying resources must also be included in the application.
-
diff --git a/v7/appcompat/THEMES.txt b/v7/appcompat/THEMES
similarity index 100%
rename from v7/appcompat/THEMES.txt
rename to v7/appcompat/THEMES
diff --git a/v7/appcompat/project.properties b/v7/appcompat/project.properties
deleted file mode 100644
index 91d2b02..0000000
--- a/v7/appcompat/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-19
-android.library=true
diff --git a/v7/appcompat/res-public/values/public_attrs.xml b/v7/appcompat/res-public/values/public_attrs.xml
index 3961f49..2e16fe7 100644
--- a/v7/appcompat/res-public/values/public_attrs.xml
+++ b/v7/appcompat/res-public/values/public_attrs.xml
@@ -180,6 +180,7 @@
<public type="attr" name="textAllCaps"/>
<public type="attr" name="textAppearanceLargePopupMenu"/>
<public type="attr" name="textAppearanceListItem"/>
+ <public type="attr" name="textAppearanceListItemSecondary"/>
<public type="attr" name="textAppearanceListItemSmall"/>
<public type="attr" name="textAppearancePopupMenuHeader"/>
<public type="attr" name="textAppearanceSearchResultSubtitle"/>
diff --git a/v7/appcompat/res/values/attrs.xml b/v7/appcompat/res/values/attrs.xml
index dfefd6e..1603b90 100644
--- a/v7/appcompat/res/values/attrs.xml
+++ b/v7/appcompat/res/values/attrs.xml
@@ -280,10 +280,11 @@
<!-- The preferred TextAppearance for the primary text of list items. -->
<attr name="textAppearanceListItem" format="reference"/>
+ <!-- The preferred TextAppearance for the secondary text of list items. -->
+ <attr name="textAppearanceListItemSecondary" format="reference" />
<!-- The preferred TextAppearance for the primary text of small list items. -->
<attr name="textAppearanceListItemSmall" format="reference"/>
-
<!-- ============ -->
<!-- Panel styles -->
<!-- ============ -->
@@ -1059,6 +1060,7 @@
<attr name="android:textSize" />
<attr name="android:textColor" />
<attr name="android:textColorHint"/>
+ <attr name="android:textColorLink"/>
<attr name="android:textStyle" />
<attr name="android:typeface" />
<attr name="textAllCaps" />
diff --git a/v7/appcompat/res/values/themes_base.xml b/v7/appcompat/res/values/themes_base.xml
index 2844d2b..dbdc605 100644
--- a/v7/appcompat/res/values/themes_base.xml
+++ b/v7/appcompat/res/values/themes_base.xml
@@ -181,6 +181,7 @@
<!-- List attributes -->
<item name="textAppearanceListItem">@style/TextAppearance.AppCompat.Subhead</item>
<item name="textAppearanceListItemSmall">@style/TextAppearance.AppCompat.Subhead</item>
+ <item name="textAppearanceListItemSecondary">@style/TextAppearance.AppCompat.Body1</item>
<item name="listPreferredItemHeight">64dp</item>
<item name="listPreferredItemHeightSmall">48dp</item>
<item name="listPreferredItemHeightLarge">80dp</item>
@@ -346,6 +347,7 @@
<!-- List attributes -->
<item name="textAppearanceListItem">@style/TextAppearance.AppCompat.Subhead</item>
<item name="textAppearanceListItemSmall">@style/TextAppearance.AppCompat.Subhead</item>
+ <item name="textAppearanceListItemSecondary">@style/TextAppearance.AppCompat.Body1</item>
<item name="listPreferredItemHeight">64dp</item>
<item name="listPreferredItemHeightSmall">48dp</item>
<item name="listPreferredItemHeightLarge">80dp</item>
diff --git a/v7/appcompat/src/.readme b/v7/appcompat/src/.readme
deleted file mode 100644
index 4bcebad..0000000
--- a/v7/appcompat/src/.readme
+++ /dev/null
@@ -1,2 +0,0 @@
-This hidden file is there to ensure there is an src folder.
-Once we support binary library this will go away.
\ No newline at end of file
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBar.java b/v7/appcompat/src/android/support/v7/app/ActionBar.java
index e26e813..c619596 100644
--- a/v7/appcompat/src/android/support/v7/app/ActionBar.java
+++ b/v7/appcompat/src/android/support/v7/app/ActionBar.java
@@ -900,7 +900,7 @@
* call {@link #setHomeActionContentDescription(int) setHomeActionContentDescription()}
* to provide a correct description of the action for accessibility support.</p>
*
- * @param resId Resource ID of a drawable to use for the up indicator, or 0
+ * @param resId Resource ID of a drawable to use for the up indicator, or null
* to use the theme's default
*
* @see #setDisplayOptions(int, int)
@@ -1069,6 +1069,12 @@
/** @hide */
@RestrictTo(LIBRARY_GROUP)
+ public boolean closeOptionsMenu() {
+ return false;
+ }
+
+ /** @hide */
+ @RestrictTo(LIBRARY_GROUP)
public boolean invalidateOptionsMenu() {
return false;
}
@@ -1100,7 +1106,9 @@
* Attempts to move focus to the ActionBar if it does not already contain the focus.
*
* @return {@code true} if focus changes or {@code false} if focus doesn't change.
+ * @hide
*/
+ @RestrictTo(LIBRARY_GROUP)
boolean requestFocus() {
return false;
}
@@ -1285,7 +1293,7 @@
* @see #setContentDescription(CharSequence)
* @see #getContentDescription()
*/
- public abstract Tab setContentDescription(int resId);
+ public abstract Tab setContentDescription(@StringRes int resId);
/**
* Set a description of this tab's content for use in accessibility support.
diff --git a/v7/appcompat/src/android/support/v7/app/AlertDialog.java b/v7/appcompat/src/android/support/v7/app/AlertDialog.java
index 051f5cc..c73e619 100644
--- a/v7/appcompat/src/android/support/v7/app/AlertDialog.java
+++ b/v7/appcompat/src/android/support/v7/app/AlertDialog.java
@@ -187,7 +187,9 @@
/**
* Internal api to allow hinting for the best button panel layout.
+ * @hide
*/
+ @RestrictTo(LIBRARY_GROUP)
void setButtonPanelLayoutHint(int layoutHint) {
mAlert.setButtonPanelLayoutHint(layoutHint);
}
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
index 6c0f125..57e4570 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
@@ -538,6 +538,14 @@
return true;
}
}
+ // Let support action bars open menus in response to the menu key prioritized over
+ // the window handling it
+ final int keyCode = event.getKeyCode();
+ final ActionBar actionBar = getSupportActionBar();
+ if (keyCode == KeyEvent.KEYCODE_MENU
+ && actionBar != null && actionBar.onMenuKeyEvent(event)) {
+ return true;
+ }
return super.dispatchKeyEvent(event);
}
@@ -576,4 +584,22 @@
}
return super.onKeyDown(keyCode, event);
}
+
+ @Override
+ public void openOptionsMenu() {
+ ActionBar actionBar = getSupportActionBar();
+ if (getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL)
+ && (actionBar == null || !actionBar.openOptionsMenu())) {
+ super.openOptionsMenu();
+ }
+ }
+
+ @Override
+ public void closeOptionsMenu() {
+ ActionBar actionBar = getSupportActionBar();
+ if (getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL)
+ && (actionBar == null || !actionBar.closeOptionsMenu())) {
+ super.closeOptionsMenu();
+ }
+ }
}
diff --git a/v7/appcompat/src/android/support/v7/app/ToolbarActionBar.java b/v7/appcompat/src/android/support/v7/app/ToolbarActionBar.java
index c124bc9..9ac70af 100644
--- a/v7/appcompat/src/android/support/v7/app/ToolbarActionBar.java
+++ b/v7/appcompat/src/android/support/v7/app/ToolbarActionBar.java
@@ -70,9 +70,9 @@
}
};
- public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window.Callback callback) {
+ ToolbarActionBar(Toolbar toolbar, CharSequence title, Window.Callback windowCallback) {
mDecorToolbar = new ToolbarWidgetWrapper(toolbar, false);
- mWindowCallback = new ToolbarCallbackWrapper(callback);
+ mWindowCallback = new ToolbarCallbackWrapper(windowCallback);
mDecorToolbar.setWindowCallback(mWindowCallback);
toolbar.setOnMenuItemClickListener(mMenuClicker);
mDecorToolbar.setWindowTitle(title);
@@ -428,6 +428,11 @@
}
@Override
+ public boolean closeOptionsMenu() {
+ return mDecorToolbar.hideOverflowMenu();
+ }
+
+ @Override
public boolean invalidateOptionsMenu() {
mDecorToolbar.getViewGroup().removeCallbacks(mMenuInvalidator);
ViewCompat.postOnAnimation(mDecorToolbar.getViewGroup(), mMenuInvalidator);
diff --git a/v7/appcompat/src/android/support/v7/view/ContextThemeWrapper.java b/v7/appcompat/src/android/support/v7/view/ContextThemeWrapper.java
index 44c0478..5069812 100644
--- a/v7/appcompat/src/android/support/v7/view/ContextThemeWrapper.java
+++ b/v7/appcompat/src/android/support/v7/view/ContextThemeWrapper.java
@@ -21,7 +21,9 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.AssetManager;
+import android.content.res.Configuration;
import android.content.res.Resources;
+import android.os.Build;
import android.support.annotation.RestrictTo;
import android.support.annotation.StyleRes;
import android.support.v7.appcompat.R;
@@ -38,18 +40,103 @@
private int mThemeResource;
private Resources.Theme mTheme;
private LayoutInflater mInflater;
+ private Configuration mOverrideConfiguration;
+ private Resources mResources;
+ /**
+ * Creates a new context wrapper with no theme and no base context.
+ * <p>
+ * <stong>Note:</strong> A base context <strong>must</strong> be attached
+ * using {@link #attachBaseContext(Context)} before calling any other
+ * method on the newly constructed context wrapper.
+ */
+ public ContextThemeWrapper() {
+ super(null);
+ }
+
+ /**
+ * Creates a new context wrapper with the specified theme.
+ * <p>
+ * The specified theme will be applied on top of the base context's theme.
+ * Any attributes not explicitly defined in the theme identified by
+ * <var>themeResId</var> will retain their original values.
+ *
+ * @param base the base context
+ * @param themeResId the resource ID of the theme to be applied on top of
+ * the base context's theme
+ */
public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
super(base);
mThemeResource = themeResId;
}
+ /**
+ * Creates a new context wrapper with the specified theme.
+ * <p>
+ * Unlike {@link #ContextThemeWrapper(Context, int)}, the theme passed to
+ * this constructor will completely replace the base context's theme.
+ *
+ * @param base the base context
+ * @param theme the theme against which resources should be inflated
+ */
public ContextThemeWrapper(Context base, Resources.Theme theme) {
super(base);
mTheme = theme;
}
@Override
+ protected void attachBaseContext(Context newBase) {
+ super.attachBaseContext(newBase);
+ }
+
+ /**
+ * Call to set an "override configuration" on this context -- this is
+ * a configuration that replies one or more values of the standard
+ * configuration that is applied to the context. See
+ * {@link Context#createConfigurationContext(Configuration)} for more
+ * information.
+ *
+ * <p>This method can only be called once, and must be called before any
+ * calls to {@link #getResources()} or {@link #getAssets()} are made.
+ */
+ public void applyOverrideConfiguration(Configuration overrideConfiguration) {
+ if (mResources != null) {
+ throw new IllegalStateException(
+ "getResources() or getAssets() has already been called");
+ }
+ if (mOverrideConfiguration != null) {
+ throw new IllegalStateException("Override configuration has already been set");
+ }
+ mOverrideConfiguration = new Configuration(overrideConfiguration);
+ }
+
+ /**
+ * Used by ActivityThread to apply the overridden configuration to onConfigurationChange
+ * callbacks.
+ * @hide
+ */
+ public Configuration getOverrideConfiguration() {
+ return mOverrideConfiguration;
+ }
+
+ @Override
+ public Resources getResources() {
+ return getResourcesInternal();
+ }
+
+ private Resources getResourcesInternal() {
+ if (mResources == null) {
+ if (mOverrideConfiguration == null) {
+ mResources = super.getResources();
+ } else if (Build.VERSION.SDK_INT >= 17) {
+ final Context resContext = createConfigurationContext(mOverrideConfiguration);
+ mResources = resContext.getResources();
+ }
+ }
+ return mResources;
+ }
+
+ @Override
public void setTheme(int resid) {
if (mThemeResource != resid) {
mThemeResource = resid;
diff --git a/v7/appcompat/src/android/support/v7/view/menu/CascadingMenuPopup.java b/v7/appcompat/src/android/support/v7/view/menu/CascadingMenuPopup.java
index 4b3fd42..ad34238 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/CascadingMenuPopup.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/CascadingMenuPopup.java
@@ -228,6 +228,7 @@
popupWindow.setAnchorView(mAnchorView);
popupWindow.setDropDownGravity(mDropDownGravity);
popupWindow.setModal(true);
+ popupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
return popupWindow;
}
@@ -435,9 +436,11 @@
popupWindow.show();
+ final ListView listView = popupWindow.getListView();
+ listView.setOnKeyListener(this);
+
// If this is the root menu, show the title if requested.
if (parentInfo == null && mShowTitle && menu.getHeaderTitle() != null) {
- final ListView listView = popupWindow.getListView();
final FrameLayout titleItemView = (FrameLayout) inflater.inflate(
R.layout.abc_popup_menu_header_item_layout, listView, false);
final TextView titleView = (TextView) titleItemView.findViewById(android.R.id.title);
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionBarContainer.java b/v7/appcompat/src/android/support/v7/widget/ActionBarContainer.java
index eff105e..532c87d 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionBarContainer.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionBarContainer.java
@@ -211,6 +211,14 @@
return true;
}
+ @Override
+ public boolean onHoverEvent(MotionEvent ev) {
+ super.onHoverEvent(ev);
+
+ // An action bar always eats hover events.
+ return true;
+ }
+
public void setTabContainer(ScrollingTabContainerView tabView) {
if (mTabContainer != null) {
removeView(mTabContainer);
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
index 0dc5759..f63a723 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
@@ -84,6 +84,7 @@
boolean allCapsSet = false;
ColorStateList textColor = null;
ColorStateList textColorHint = null;
+ ColorStateList textColorLink = null;
// First check TextAppearance's textAllCaps value
if (ap != -1) {
@@ -102,6 +103,10 @@
textColorHint = a.getColorStateList(
R.styleable.TextAppearance_android_textColorHint);
}
+ if (a.hasValue(R.styleable.TextAppearance_android_textColorLink)) {
+ textColorLink = a.getColorStateList(
+ R.styleable.TextAppearance_android_textColorLink);
+ }
}
a.recycle();
}
@@ -123,6 +128,10 @@
textColorHint = a.getColorStateList(
R.styleable.TextAppearance_android_textColorHint);
}
+ if (a.hasValue(R.styleable.TextAppearance_android_textColorLink)) {
+ textColorLink = a.getColorStateList(
+ R.styleable.TextAppearance_android_textColorLink);
+ }
}
a.recycle();
@@ -132,6 +141,9 @@
if (textColorHint != null) {
mView.setHintTextColor(textColorHint);
}
+ if (textColorLink != null) {
+ mView.setLinkTextColor(textColorLink);
+ }
if (!hasPwdTm && allCapsSet) {
setAllCaps(allCaps);
}
diff --git a/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java b/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java
index a276ec7..cc68d11 100644
--- a/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java
+++ b/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java
@@ -46,6 +46,8 @@
private int mLastWidthSize = -1;
+ private int mMinimumHeight = 0;
+
public ButtonBarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
final boolean allowStackingDefault =
@@ -148,6 +150,11 @@
return -1;
}
+ @Override
+ public int getMinimumHeight() {
+ return Math.max(mMinimumHeight, super.getMinimumHeight());
+ }
+
private void setStacked(boolean stacked) {
setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
setGravity(stacked ? Gravity.RIGHT : Gravity.BOTTOM);
diff --git a/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java b/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java
index a6ddb34..c363301 100644
--- a/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java
+++ b/v7/appcompat/src/android/support/v7/widget/ListPopupWindow.java
@@ -938,6 +938,7 @@
* @return true if the event was handled, false if it was ignored.
*
* @see #setModal(boolean)
+ * @see #onKeyUp(int, KeyEvent)
*/
public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
// when the drop down is shown, we drive it directly
@@ -1033,6 +1034,7 @@
* @return true if the event was handled, false if it was ignored.
*
* @see #setModal(boolean)
+ * @see #onKeyDown(int, KeyEvent)
*/
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
if (isShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
diff --git a/v7/appcompat/src/android/support/v7/widget/SuggestionsAdapter.java b/v7/appcompat/src/android/support/v7/widget/SuggestionsAdapter.java
index 9f2975f..24dafb3 100644
--- a/v7/appcompat/src/android/support/v7/widget/SuggestionsAdapter.java
+++ b/v7/appcompat/src/android/support/v7/widget/SuggestionsAdapter.java
@@ -110,7 +110,7 @@
* copied to the query text field.
* <p>
*
- * @param refine which queries to refine. Possible values are {@link #REFINE_NONE},
+ * @param refineWhat which queries to refine. Possible values are {@link #REFINE_NONE},
* {@link #REFINE_BY_ENTRY}, and {@link #REFINE_ALL}.
*/
public void setQueryRefinement(int refineWhat) {
@@ -461,6 +461,29 @@
}
/**
+ * This method is overridden purely to provide a bit of protection against
+ * flaky content providers.
+ *
+ * @see android.widget.CursorAdapter#getDropDownView(int, View, ViewGroup)
+ */
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ try {
+ return super.getDropDownView(position, convertView, parent);
+ } catch (RuntimeException e) {
+ Log.w(LOG_TAG, "Search suggestions cursor threw exception.", e);
+ // Put exception string in item title
+ final View v = newDropDownView(mContext, mCursor, parent);
+ if (v != null) {
+ final ChildViewCache views = (ChildViewCache) v.getTag();
+ final TextView tv = views.mText1;
+ tv.setText(e.toString());
+ }
+ return v;
+ }
+ }
+
+ /**
* Gets a drawable given a value provided by a suggestion provider.
*
* This value could be just the string value of a resource id
diff --git a/v7/appcompat/src/android/support/v7/widget/Toolbar.java b/v7/appcompat/src/android/support/v7/widget/Toolbar.java
index 1865df1..bd770c2 100644
--- a/v7/appcompat/src/android/support/v7/widget/Toolbar.java
+++ b/v7/appcompat/src/android/support/v7/widget/Toolbar.java
@@ -2169,6 +2169,17 @@
}
/**
+ * Accessor to enable LayoutLib to get ActionMenuPresenter directly.
+ */
+ ActionMenuPresenter getOuterActionMenuPresenter() {
+ return mOuterActionMenuPresenter;
+ }
+
+ Context getPopupContext() {
+ return mPopupContext;
+ }
+
+ /**
* Interface responsible for receiving menu item click events if the items themselves
* do not have individual item click listeners.
*/
diff --git a/v7/appcompat/tests/res/color/color_state_list_link.xml b/v7/appcompat/tests/res/color/color_state_list_link.xml
new file mode 100644
index 0000000..36dd761
--- /dev/null
+++ b/v7/appcompat/tests/res/color/color_state_list_link.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@color/lilac_disabled"
+ android:state_enabled="false"/>
+ <item android:color="@color/lilac_default"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/tests/res/layout/appcompat_textview_activity.xml b/v7/appcompat/tests/res/layout/appcompat_textview_activity.xml
index 9fd3f29..b2aafe5 100644
--- a/v7/appcompat/tests/res/layout/appcompat_textview_activity.xml
+++ b/v7/appcompat/tests/res/layout/appcompat_textview_activity.xml
@@ -104,6 +104,20 @@
android:text="@string/sample_text2"
android:textColor="?android:attr/textColorSecondary" />
+ <android.support.v7.widget.AppCompatTextView
+ android:id="@+id/view_text_link_enabled"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/text_link_enabled"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ <android.support.v7.widget.AppCompatTextView
+ android:id="@+id/view_text_link_disabled"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ android:text="@string/text_link_disabled" />
+
</LinearLayout>
</ScrollView>
diff --git a/v7/appcompat/tests/res/menu/sample_actions.xml b/v7/appcompat/tests/res/menu/sample_actions.xml
index 8b3f893..e72c4d2 100644
--- a/v7/appcompat/tests/res/menu/sample_actions.xml
+++ b/v7/appcompat/tests/res/menu/sample_actions.xml
@@ -21,7 +21,7 @@
app:contentDescription="@string/search_menu_description"
app:tooltipText="@string/search_menu_tooltip"
app:showAsAction="always|collapseActionView"
- app:actionViewClass="android.support.v7.widget.SearchView"/>
+ app:actionViewClass="android.support.v7.app.CustomCollapsibleView"/>
<item android:id="@+id/action_alpha_shortcut"
android:title="@string/alpha_menu_title"
diff --git a/v7/appcompat/tests/res/values/strings.xml b/v7/appcompat/tests/res/values/strings.xml
index fbc132b..0ded756 100644
--- a/v7/appcompat/tests/res/values/strings.xml
+++ b/v7/appcompat/tests/res/values/strings.xml
@@ -79,4 +79,7 @@
<string name="alpha_menu_title">Alpha</string>
<string name="alpha_menu_description">Alpha description</string>
<string name="alpha_menu_tooltip">Alpha tooltip</string>
+
+ <string name="text_link_enabled">With <a href="http://www.google.com">link</a> enabled</string>
+ <string name="text_link_disabled">With <a href="http://www.google.com">link</a> disabled</string>
</resources>
diff --git a/v7/appcompat/tests/res/values/styles.xml b/v7/appcompat/tests/res/values/styles.xml
index 1b416a4..5831122 100644
--- a/v7/appcompat/tests/res/values/styles.xml
+++ b/v7/appcompat/tests/res/values/styles.xml
@@ -28,6 +28,7 @@
<style name="Theme.TextColors" parent="@style/Theme.AppCompat.Light">
<item name="android:textColorPrimary">#FF0000FF</item>
<item name="android:textColorSecondary">@color/color_state_list_sand</item>
+ <item name="android:textColorLink">@color/color_state_list_link</item>
</style>
<style name="TextStyleAllCapsOn" parent="@android:style/TextAppearance.Medium">
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
index ecdc64f..6112d34 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
@@ -35,6 +35,9 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.pm.PackageManager;
import android.support.annotation.RequiresApi;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SdkSuppress;
@@ -84,6 +87,11 @@
(FitWindowsContentLayout) getActivity().findViewById(R.id.test_content);
assertNotNull(content);
+ if (!canShowSystemUi(getActivity())) {
+ // Device cannot show system UI so setSystemUiVisibility will do nothing.
+ return;
+ }
+
// Call setSystemUiVisibility with flags which will cause window insets to be dispatched
final int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
onView(withId(R.id.test_content)).perform(setSystemUiVisibility(flags));
@@ -98,6 +106,11 @@
final View content = getActivity().findViewById(R.id.test_content);
assertNotNull(content);
+ if (!canShowSystemUi(getActivity())) {
+ // Device cannot show system UI so setSystemUiVisibility will do nothing.
+ return;
+ }
+
// Create a spy of one of our test listener and set it on our content
final View.OnApplyWindowInsetsListener spyListener
= spy(new TestOnApplyWindowInsetsListener());
@@ -183,6 +196,15 @@
assertNull(actionMode);
}
+ @SuppressWarnings("deprecation")
+ @SuppressLint("InlinedApi")
+ private static boolean canShowSystemUi(Activity activity) {
+ PackageManager manager = activity.getPackageManager();
+ return !manager.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
+ && !manager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ && !manager.hasSystemFeature(PackageManager.FEATURE_WATCH);
+ }
+
protected void testSupportActionModeAppCompatCallbacks(final boolean fromWindow) {
final A activity = getActivity();
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
index fbfd963..88f60ef 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
@@ -21,6 +21,7 @@
import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
@@ -39,6 +40,7 @@
import android.view.Menu;
import android.view.MenuItem;
+import org.hamcrest.Matchers;
import org.junit.Test;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -93,24 +95,25 @@
@Test
@LargeTest
- public void testBackCollapsesSearchView() throws InterruptedException {
+ public void testBackCollapsesActionView() throws InterruptedException {
// Click on the Search menu item
onView(withId(R.id.action_search)).perform(click());
- // Check that the SearchView is displayed
- onView(withId(R.id.search_bar)).check(matches(isDisplayed()));
+ // Check that the action view is displayed (expanded)
+ onView(withClassName(Matchers.is(CustomCollapsibleView.class.getName())))
+ .check(matches(isDisplayed()));
- // Wait for the IME to show
+ // Let things settle
getInstrumentation().waitForIdleSync();
- // Now send a back event to dismiss the IME
+ // Now send a back event to collapse the custom action view
getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
- // ...and another to collapse the SearchView
- getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+ getInstrumentation().waitForIdleSync();
// Check that the Activity is still running
assertFalse(getActivity().isFinishing());
assertFalse(getActivity().isDestroyed());
- // ...and that the SearchView is not attached
- onView(withId(R.id.search_bar)).check(doesNotExist());
+ // ... and that our action view is not attached
+ onView(withClassName(Matchers.is(CustomCollapsibleView.class.getName())))
+ .check(doesNotExist());
}
@Test
diff --git a/v7/appcompat/tests/src/android/support/v7/app/CustomCollapsibleView.java b/v7/appcompat/tests/src/android/support/v7/app/CustomCollapsibleView.java
new file mode 100644
index 0000000..27e6f8a
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/app/CustomCollapsibleView.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.app;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.support.v7.view.CollapsibleActionView;
+import android.util.AttributeSet;
+import android.view.View;
+
+public class CustomCollapsibleView extends View implements CollapsibleActionView {
+ public CustomCollapsibleView(Context context) {
+ this(context, null, 0);
+ }
+
+ public CustomCollapsibleView(Context context,
+ @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CustomCollapsibleView(Context context,
+ @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setBackgroundColor(0xFFFF0000);
+ }
+
+ @Override
+ public void onActionViewExpanded() {
+ }
+
+ @Override
+ public void onActionViewCollapsed() {
+ }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
index 9b8051e..34c5d64 100755
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package android.support.v7.app;
import static android.support.test.espresso.Espresso.onView;
@@ -44,8 +45,10 @@
import android.support.test.espresso.action.GeneralSwipeAction;
import android.support.test.espresso.action.Press;
import android.support.test.espresso.action.Swipe;
+import android.support.test.filters.FlakyTest;
import android.support.test.filters.LargeTest;
import android.support.test.filters.MediumTest;
+import android.support.test.filters.Suppress;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.appcompat.test.R;
@@ -425,6 +428,8 @@
mDrawerLayout.removeDrawerListener(mockedListener);
}
+ @Suppress
+ @FlakyTest(bugId = 33659300)
@Test
@MediumTest
public void testDrawerListenerCallbacksOnOpeningViaSwipes() {
diff --git a/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java b/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java
index cdaecc0..08d66c4 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java
@@ -16,8 +16,66 @@
package android.support.v7.app;
+import android.support.test.filters.SmallTest;
+import android.support.v7.widget.Toolbar;
+import android.view.KeyEvent;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
public class KeyEventsTestCaseWithToolbar extends BaseKeyEventsTestCase<ToolbarAppCompatActivity> {
public KeyEventsTestCaseWithToolbar() {
super(ToolbarAppCompatActivity.class);
}
+
+ @Test
+ @SmallTest
+ @Override
+ public void testMenuKeyEventReachesActivity() throws InterruptedException {
+ // With Toolbar, MENU key gets sent-to (and consumed by) Toolbar rather than Activity
+ }
+
+ @Test
+ @SmallTest
+ public void testMenuKeyOpensToolbarMenu() {
+ // Base test only checks that *a* menu is opened, we check here that the toolbar's menu
+ // specifically is opened.
+ Toolbar toolbar = mActivityTestRule.getActivity().getToolbar();
+ assertFalse(toolbar.isOverflowMenuShowing());
+
+ getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+ getInstrumentation().waitForIdleSync();
+ assertTrue(toolbar.isOverflowMenuShowing());
+
+ getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
+ getInstrumentation().waitForIdleSync();
+ assertFalse(toolbar.isOverflowMenuShowing());
+ }
+
+ @Test
+ @SmallTest
+ public void testOpenMenuOpensToolbarMenu() throws Throwable {
+ Toolbar toolbar = mActivityTestRule.getActivity().getToolbar();
+ assertFalse(toolbar.isOverflowMenuShowing());
+
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mActivityTestRule.getActivity().openOptionsMenu();
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+ assertTrue(toolbar.isOverflowMenuShowing());
+
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mActivityTestRule.getActivity().closeOptionsMenu();
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+ assertFalse(toolbar.isOverflowMenuShowing());
+ }
}
diff --git a/v7/appcompat/tests/src/android/support/v7/app/ToolbarAppCompatActivity.java b/v7/appcompat/tests/src/android/support/v7/app/ToolbarAppCompatActivity.java
index 9042363..3213f40 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/ToolbarAppCompatActivity.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/ToolbarAppCompatActivity.java
@@ -34,4 +34,8 @@
mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
}
+
+ public Toolbar getToolbar() {
+ return mToolbar;
+ }
}
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
index d9c3c55..0a28102 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertEquals;
+import android.content.res.ColorStateList;
import android.graphics.Color;
import android.support.test.filters.SmallTest;
import android.support.v4.content.ContextCompat;
@@ -118,4 +119,18 @@
assertEquals(ContextCompat.getColor(textView.getContext(), R.color.sand_disabled),
textView.getCurrentTextColor());
}
+
+ private void verifyTextLinkColor(TextView textView) {
+ ColorStateList linkColorStateList = textView.getLinkTextColors();
+ assertEquals(ContextCompat.getColor(textView.getContext(), R.color.lilac_default),
+ linkColorStateList.getColorForState(new int[] { android.R.attr.state_enabled}, 0));
+ assertEquals(ContextCompat.getColor(textView.getContext(), R.color.lilac_disabled),
+ linkColorStateList.getColorForState(new int[] { -android.R.attr.state_enabled}, 0));
+ }
+
+ @Test
+ public void testTextLinkColor() {
+ verifyTextLinkColor((TextView) mContainer.findViewById(R.id.view_text_link_enabled));
+ verifyTextLinkColor((TextView) mContainer.findViewById(R.id.view_text_link_disabled));
+ }
}
diff --git a/v7/cardview/AndroidManifest.xml b/v7/cardview/AndroidManifest.xml
index b83c815..8fcf55a 100644
--- a/v7/cardview/AndroidManifest.xml
+++ b/v7/cardview/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.v7.cardview">
<uses-sdk android:minSdkVersion="14"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/v7/cardview/src/.readme b/v7/cardview/src/.readme
deleted file mode 100644
index 4bcebad..0000000
--- a/v7/cardview/src/.readme
+++ /dev/null
@@ -1,2 +0,0 @@
-This hidden file is there to ensure there is an src folder.
-Once we support binary library this will go away.
\ No newline at end of file
diff --git a/v7/gridlayout/.classpath b/v7/gridlayout/.classpath
deleted file mode 100644
index a4763d1..0000000
--- a/v7/gridlayout/.classpath
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="gen"/>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
- <classpathentry kind="output" path="bin/classes"/>
-</classpath>
diff --git a/v7/gridlayout/.gitignore b/v7/gridlayout/.gitignore
deleted file mode 100644
index c3c25e0..0000000
--- a/v7/gridlayout/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-.settings
-bin
-libs
-gen
diff --git a/v7/gridlayout/.project b/v7/gridlayout/.project
deleted file mode 100644
index 1e67516..0000000
--- a/v7/gridlayout/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>android-support-v7-gridlayout</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ApkBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
diff --git a/v7/gridlayout/AndroidManifest.xml b/v7/gridlayout/AndroidManifest.xml
index 6f12842..b6cb783 100644
--- a/v7/gridlayout/AndroidManifest.xml
+++ b/v7/gridlayout/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.v7.gridlayout">
<uses-sdk android:minSdkVersion="14"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/v7/gridlayout/README.txt b/v7/gridlayout/README.txt
deleted file mode 100644
index e754187..0000000
--- a/v7/gridlayout/README.txt
+++ /dev/null
@@ -1,66 +0,0 @@
-Library Project including compatibility GridLayout.
-
-This can be used by an Android project to provide
-access to GridLayout on applications running on API 7+.
-
-There is technically no source, but the src folder is necessary
-to ensure that the build system works. The content is actually
-located in libs/android-support-v7-gridlayout.jar.
-The accompanying resources must also be included in the application.
-
-
-USAGE:
-
-Make sure you use <android.support.v7.widget.GridLayout> in your
-layouts instead of <GridLayout>.
-Same for <android.support.v4.widget.Space> instead of <Space>.
-
-Additionally, all of GridLayout's attributes should be put in the
-namespace of the app, as those attributes have been redefined in
-the library so that it can run on older platforms that don't offer
-those attributes in their namespace.
-
-To know which attributes need the application namespace, look at
-the two declare-styleable declared in res/values/attrs.xml
-
-
-
-For instance:
-
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.v7.widget.GridLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto" <==== the namespace used for the library project
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- app:columnCount="6" > <===== notice how we're using app:columnCount here, not android:columnCount!
-
- <Button
- android:id="@+id/button1"
- app:layout_column="1" <=== again, note the app: namespace
- app:layout_columnSpan="2"
- app:layout_gravity="left"
- app:layout_row="1"
- android:text="Button" />
-
- <CheckBox
- android:id="@+id/checkBox1"
- app:layout_column="4"
- app:layout_gravity="left"
- app:layout_row="2"
- android:text="CheckBox" />
-
- <Button
- android:id="@+id/button2"
- app:layout_column="5"
- app:layout_gravity="left"
- app:layout_row="3"
- android:text="Button" />
-
- <android.support.v4.widget.Space <=== space widgets also need the full support package path
- android:layout_width="21dp" <=== use the android namespace for width, height etc -- only use app: for the grid layout library's new resources
- android:layout_height="1dp"
- app:layout_column="0"
- app:layout_gravity="fill_horizontal"
- app:layout_row="0" />
-
diff --git a/v7/gridlayout/project.properties b/v7/gridlayout/project.properties
deleted file mode 100644
index 1e106c3..0000000
--- a/v7/gridlayout/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-7
-android.library=true
\ No newline at end of file
diff --git a/v7/gridlayout/src/.readme b/v7/gridlayout/src/.readme
deleted file mode 100644
index 4bcebad..0000000
--- a/v7/gridlayout/src/.readme
+++ /dev/null
@@ -1,2 +0,0 @@
-This hidden file is there to ensure there is an src folder.
-Once we support binary library this will go away.
\ No newline at end of file
diff --git a/v7/mediarouter/.classpath b/v7/mediarouter/.classpath
deleted file mode 100644
index a4763d1..0000000
--- a/v7/mediarouter/.classpath
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="gen"/>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
- <classpathentry kind="output" path="bin/classes"/>
-</classpath>
diff --git a/v7/mediarouter/.gitignore b/v7/mediarouter/.gitignore
deleted file mode 100644
index c3c25e0..0000000
--- a/v7/mediarouter/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-.settings
-bin
-libs
-gen
diff --git a/v7/mediarouter/.project b/v7/mediarouter/.project
deleted file mode 100644
index 2eca48b..0000000
--- a/v7/mediarouter/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>android-support-v7-mediarouter</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ApkBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
diff --git a/v7/mediarouter/AndroidManifest.xml b/v7/mediarouter/AndroidManifest.xml
index 5466168..c4577f9 100644
--- a/v7/mediarouter/AndroidManifest.xml
+++ b/v7/mediarouter/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.v7.mediarouter">
<uses-sdk android:minSdkVersion="14"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/v7/mediarouter/README.txt b/v7/mediarouter/README.txt
deleted file mode 100644
index 5d99185..0000000
--- a/v7/mediarouter/README.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Library Project including compatibility MediaRouter.
-
-This can be used by an Android project to provide
-access to MediaRouter on applications running on API 7+.
-
-There is technically no source, but the src folder is necessary
-to ensure that the build system works. The content is actually
-located in libs/android-support-v7-mediarouter.jar.
-The accompanying resources must also be included in the application.
-
diff --git a/v7/mediarouter/project.properties b/v7/mediarouter/project.properties
deleted file mode 100644
index 484dab0..0000000
--- a/v7/mediarouter/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-17
-android.library=true
diff --git a/v7/mediarouter/src/.readme b/v7/mediarouter/src/.readme
deleted file mode 100644
index 4bcebad..0000000
--- a/v7/mediarouter/src/.readme
+++ /dev/null
@@ -1,2 +0,0 @@
-This hidden file is there to ensure there is an src folder.
-Once we support binary library this will go away.
\ No newline at end of file
diff --git a/v7/palette/.classpath b/v7/palette/.classpath
deleted file mode 100644
index 43cb38c..0000000
--- a/v7/palette/.classpath
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="gen"/>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
- <classpathentry kind="output" path="bin/classes"/>
-</classpath>
diff --git a/v7/palette/.project b/v7/palette/.project
deleted file mode 100644
index 76e11a6..0000000
--- a/v7/palette/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>android-support-v7-palette</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ApkBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
diff --git a/v7/palette/AndroidManifest.xml b/v7/palette/AndroidManifest.xml
index 78735ba..8e5ffaa 100644
--- a/v7/palette/AndroidManifest.xml
+++ b/v7/palette/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.v7.palette">
<uses-sdk android:minSdkVersion="14"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/v7/palette/README.txt b/v7/palette/README.txt
deleted file mode 100644
index 4a8b5e4..0000000
--- a/v7/palette/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-Library Project including Palette for color extraction from images
diff --git a/v7/palette/project.properties b/v7/palette/project.properties
deleted file mode 100644
index 1e106c3..0000000
--- a/v7/palette/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-7
-android.library=true
\ No newline at end of file
diff --git a/v7/preference/AndroidManifest.xml b/v7/preference/AndroidManifest.xml
index 6923656..772b410 100644
--- a/v7/preference/AndroidManifest.xml
+++ b/v7/preference/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.v7.preference">
<uses-sdk android:minSdkVersion="14" />
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
- <application />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/v7/recyclerview/AndroidManifest.xml b/v7/recyclerview/AndroidManifest.xml
index 5eef157..5b03882 100644
--- a/v7/recyclerview/AndroidManifest.xml
+++ b/v7/recyclerview/AndroidManifest.xml
@@ -16,5 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.v7.recyclerview">
<uses-sdk android:minSdkVersion="14"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>
diff --git a/v7/recyclerview/src/android/support/v7/util/DiffUtil.java b/v7/recyclerview/src/android/support/v7/util/DiffUtil.java
index 6f0a078..6302666 100644
--- a/v7/recyclerview/src/android/support/v7/util/DiffUtil.java
+++ b/v7/recyclerview/src/android/support/v7/util/DiffUtil.java
@@ -205,7 +205,7 @@
// we can reach k from k - 1 or k + 1. Check which one is further in the graph
int x;
final boolean removal;
- if (k == -d || k != d && forward[kOffset + k - 1] < forward[kOffset + k + 1]) {
+ if (k == -d || (k != d && forward[kOffset + k - 1] < forward[kOffset + k + 1])) {
x = forward[kOffset + k + 1];
removal = false;
} else {
@@ -238,8 +238,8 @@
final int backwardK = k + delta;
int x;
final boolean removal;
- if (backwardK == d + delta || backwardK != -d + delta
- && backward[kOffset + backwardK - 1] < backward[kOffset + backwardK + 1]) {
+ if (backwardK == d + delta || (backwardK != -d + delta
+ && backward[kOffset + backwardK - 1] < backward[kOffset + backwardK + 1])) {
x = backward[kOffset + backwardK - 1];
removal = false;
} else {
diff --git a/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java b/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java
index 33ad434..11600a8 100644
--- a/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java
+++ b/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java
@@ -255,8 +255,8 @@
public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
int toX, int toY) {
final View view = holder.itemView;
- fromX += holder.itemView.getTranslationX();
- fromY += holder.itemView.getTranslationY();
+ fromX += (int) holder.itemView.getTranslationX();
+ fromY += (int) holder.itemView.getTranslationY();
resetAnimation(holder);
int deltaX = toX - fromX;
int deltaY = toY - fromY;
diff --git a/v7/recyclerview/src/android/support/v7/widget/FastScroller.java b/v7/recyclerview/src/android/support/v7/widget/FastScroller.java
index 9e45909..fbe234b 100644
--- a/v7/recyclerview/src/android/support/v7/widget/FastScroller.java
+++ b/v7/recyclerview/src/android/support/v7/widget/FastScroller.java
@@ -228,7 +228,7 @@
switch (mAnimationState) {
case ANIMATION_STATE_FADING_OUT:
mShowHideAnimator.cancel();
- // no break
+ // fall through
case ANIMATION_STATE_OUT:
mAnimationState = ANIMATION_STATE_FADING_IN;
mShowHideAnimator.setFloatValues((float) mShowHideAnimator.getAnimatedValue(), 1);
@@ -248,7 +248,7 @@
switch (mAnimationState) {
case ANIMATION_STATE_FADING_IN:
mShowHideAnimator.cancel();
- // no break
+ // fall through
case ANIMATION_STATE_IN:
mAnimationState = ANIMATION_STATE_FADING_OUT;
mShowHideAnimator.setFloatValues((float) mShowHideAnimator.getAnimatedValue(), 0);
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index 6e9ea69..342a740 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -4892,7 +4892,7 @@
private float distanceInfluenceForSnapDuration(float f) {
f -= 0.5f; // center the values about 0.
- f *= 0.3f * Math.PI / 2.0f;
+ f *= 0.3f * (float) Math.PI / 2.0f;
return (float) Math.sin(f);
}
@@ -11162,8 +11162,8 @@
* @param scrollVector The vector that points to the target scroll position
*/
protected void normalize(PointF scrollVector) {
- final double magnitude = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y
- * scrollVector.y);
+ final float magnitude = (float) Math.sqrt(scrollVector.x * scrollVector.x
+ + scrollVector.y * scrollVector.y);
scrollVector.x /= magnitude;
scrollVector.y /= magnitude;
}
diff --git a/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchHelper.java b/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchHelper.java
index a0a86b0..b0a2cb3 100644
--- a/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchHelper.java
+++ b/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchHelper.java
@@ -2283,7 +2283,7 @@
}
}
- private class RecoverAnimation implements Animator.AnimatorListener {
+ private static class RecoverAnimation implements Animator.AnimatorListener {
final float mStartDx;
diff --git a/v7/recyclerview/tests/Android.mk b/v7/recyclerview/tests/Android.mk
index 9c523b7..c6299d7 100644
--- a/v7/recyclerview/tests/Android.mk
+++ b/v7/recyclerview/tests/Android.mk
@@ -32,6 +32,7 @@
android-support-annotations
LOCAL_PACKAGE_NAME := RecyclerViewTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
--extra-packages android.support.v7.recyclerview
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 2142832..f2b56a1 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
package android.support.v7.widget;
import static android.support.v7.widget.RecyclerView.NO_POSITION;
@@ -51,8 +50,10 @@
import android.os.Build;
import android.os.SystemClock;
import android.support.annotation.Nullable;
+import android.support.test.filters.FlakyTest;
import android.support.test.filters.LargeTest;
import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.Suppress;
import android.support.test.runner.AndroidJUnit4;
import android.support.v4.view.ViewCompat;
import android.support.v7.util.TouchUtils;
@@ -1486,7 +1487,10 @@
}
}
+ @Suppress
+ @FlakyTest(bugId = 33949798)
@Test
+ @LargeTest
public void hasPendingUpdatesBeforeFirstLayout() throws Throwable {
RecyclerView recyclerView = new RecyclerView(getActivity());
TestLayoutManager layoutManager = new DumbLayoutManager();
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerBaseConfigSetTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerBaseConfigSetTest.java
index 984db5b..6a32572 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerBaseConfigSetTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerBaseConfigSetTest.java
@@ -16,7 +16,6 @@
package android.support.v7.widget;
-
import static android.support.v7.widget.LayoutState.LAYOUT_START;
import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
import static android.support.v7.widget.StaggeredGridLayoutManager.HORIZONTAL;
@@ -34,7 +33,9 @@
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.test.filters.FlakyTest;
import android.support.test.filters.LargeTest;
+import android.support.test.filters.Suppress;
import android.util.Log;
import android.view.View;
import android.view.ViewParent;
@@ -738,7 +739,10 @@
consistentRelayoutTest(mConfig, true);
}
+ @Suppress
+ @FlakyTest(bugId = 34158822)
@Test
+ @LargeTest
public void dontRecycleViewsTranslatedOutOfBoundsFromStart() throws Throwable {
final Config config = ((Config) mConfig.clone()).itemCount(1000);
setupByConfig(config);
diff --git a/wearable/AndroidManifest.xml b/wearable/AndroidManifest.xml
index 77ac1b6..e4b1897 100644
--- a/wearable/AndroidManifest.xml
+++ b/wearable/AndroidManifest.xml
@@ -16,5 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.support.wearable">
<uses-sdk android:minSdkVersion="22"/>
- <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ <application>
+ <meta-data android:name="android.support.VERSION" android:value="${support-version}" />
+ </application>
</manifest>