Add 23.1.1 support library API files am: 8868c3d91a
am: 4b9aa834b8
* commit '4b9aa834b8cc5954335af50aff11a1df9ff12522':
Add 23.1.1 support library API files
diff --git a/annotations/src/android/support/annotation/BinderThread.java b/annotations/src/android/support/annotation/BinderThread.java
index 4bb6754..a9e5db5 100644
--- a/annotations/src/android/support/annotation/BinderThread.java
+++ b/annotations/src/android/support/annotation/BinderThread.java
@@ -29,10 +29,10 @@
* on the binder thread.
* <p>
* Example:
- * <pre>{@code
- * (@BinderThread
+ * <pre><code>
+ * @BinderThread
* public BeamShareData createBeamShareData() { ... }
- * }</pre>
+ * </code></pre>
*/
@Retention(CLASS)
@Target({METHOD,CONSTRUCTOR,TYPE})
diff --git a/annotations/src/android/support/annotation/CallSuper.java b/annotations/src/android/support/annotation/CallSuper.java
index 7180417..f9f8bee 100644
--- a/annotations/src/android/support/annotation/CallSuper.java
+++ b/annotations/src/android/support/annotation/CallSuper.java
@@ -25,10 +25,10 @@
* Denotes that any overriding methods should invoke this method as well.
* <p>
* Example:
- * <pre>{@code
+ * <pre><code>
* @CallSuper
* public abstract void onFocusLost();
- * }</pre>
+ * </code></pre>
*/
@Retention(CLASS)
@Target({METHOD})
diff --git a/annotations/src/android/support/annotation/CheckResult.java b/annotations/src/android/support/annotation/CheckResult.java
index d527edc..405ea4b 100644
--- a/annotations/src/android/support/annotation/CheckResult.java
+++ b/annotations/src/android/support/annotation/CheckResult.java
@@ -29,7 +29,7 @@
* <p>
* Example:
* <pre>{@code
- * public @CheckResult String trim(String s) { return s.trim(); }
+ * public @CheckResult String trim(String s) { return s.trim(); }
* ...
* s.trim(); // this is probably an error
* s = s.trim(); // ok
diff --git a/annotations/src/android/support/annotation/ColorInt.java b/annotations/src/android/support/annotation/ColorInt.java
index 9b3cb7e..4fa46eb 100644
--- a/annotations/src/android/support/annotation/ColorInt.java
+++ b/annotations/src/android/support/annotation/ColorInt.java
@@ -31,7 +31,7 @@
* <p>
* Example:
* <pre>{@code
- * public abstract void setTextColor(@ColorInt int color);
+ * public abstract void setTextColor(@ColorInt int color);
* }</pre>
*/
@Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/FloatRange.java b/annotations/src/android/support/annotation/FloatRange.java
index 130d834..f129e1b 100644
--- a/annotations/src/android/support/annotation/FloatRange.java
+++ b/annotations/src/android/support/annotation/FloatRange.java
@@ -28,12 +28,12 @@
* Denotes that the annotated element should be a float or double in the given range
* <p>
* Example:
- * <pre>{@code
+ * <pre><code>
* @FloatRange(from=0.0,to=1.0)
* public float getAlpha() {
* ...
* }
- * }</pre>
+ * </code></pre>
*/
@Retention(CLASS)
@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE})
diff --git a/annotations/src/android/support/annotation/IntDef.java b/annotations/src/android/support/annotation/IntDef.java
index fedd7b4..be2e2b8 100644
--- a/annotations/src/android/support/annotation/IntDef.java
+++ b/annotations/src/android/support/annotation/IntDef.java
@@ -32,24 +32,24 @@
* multiple constants can be combined.
* <p>
* Example:
- * <pre>{@code
+ * <pre><code>
* @Retention(SOURCE)
- * @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
- * public @interface NavigationMode {}
+ * @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
+ * public @interface NavigationMode {}
* public static final int NAVIGATION_MODE_STANDARD = 0;
* public static final int NAVIGATION_MODE_LIST = 1;
* public static final int NAVIGATION_MODE_TABS = 2;
* ...
- * public abstract void setNavigationMode(@NavigationMode int mode);
+ * public abstract void setNavigationMode(@NavigationMode int mode);
* @NavigationMode
* public abstract int getNavigationMode();
- * }</pre>
+ * </code></pre>
* For a flag, set the flag attribute:
- * <pre>{@code
+ * <pre><code>
* @IntDef(
* flag = true
- * value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
- * }</pre>
+ * value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
+ * </code></pre>
*/
@Retention(SOURCE)
@Target({ANNOTATION_TYPE})
diff --git a/annotations/src/android/support/annotation/IntRange.java b/annotations/src/android/support/annotation/IntRange.java
index 6011276..d489acb 100644
--- a/annotations/src/android/support/annotation/IntRange.java
+++ b/annotations/src/android/support/annotation/IntRange.java
@@ -18,6 +18,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.METHOD;
@@ -28,15 +29,15 @@
* Denotes that the annotated element should be an int or long in the given range
* <p>
* Example:
- * <pre>{@code
+ * <pre><code>
* @IntRange(from=0,to=255)
* public int getAlpha() {
* ...
* }
- * }</pre>
+ * </code></pre>
*/
@Retention(CLASS)
-@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE})
+@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE})
public @interface IntRange {
/** Smallest value, inclusive */
long from() default Long.MIN_VALUE;
diff --git a/annotations/src/android/support/annotation/Keep.java b/annotations/src/android/support/annotation/Keep.java
index de1c9e5..111971c 100644
--- a/annotations/src/android/support/annotation/Keep.java
+++ b/annotations/src/android/support/annotation/Keep.java
@@ -33,12 +33,12 @@
* so a compiler may think that the code is unused.
* <p>
* Example:
- * <pre>{@code
+ * <pre><code>
* @Keep
* public void foo() {
* ...
* }
- * }</pre>
+ * </code></pre>
*/
@Retention(CLASS)
@Target({PACKAGE,TYPE,ANNOTATION_TYPE,CONSTRUCTOR,METHOD,FIELD})
diff --git a/annotations/src/android/support/annotation/MainThread.java b/annotations/src/android/support/annotation/MainThread.java
index 28aee06..dce4c71 100644
--- a/annotations/src/android/support/annotation/MainThread.java
+++ b/annotations/src/android/support/annotation/MainThread.java
@@ -29,10 +29,10 @@
* on the main thread.
* <p>
* Example:
- * <pre>{@code
+ * <pre><code>
* @MainThread
* public void deliverResult(D data) { ... }
- * }</pre>
+ * </code></pre>
*/
@Retention(CLASS)
@Target({METHOD,CONSTRUCTOR,TYPE})
diff --git a/annotations/src/android/support/annotation/RequiresPermission.java b/annotations/src/android/support/annotation/RequiresPermission.java
index bb9afb2..8337b56 100644
--- a/annotations/src/android/support/annotation/RequiresPermission.java
+++ b/annotations/src/android/support/annotation/RequiresPermission.java
@@ -22,40 +22,59 @@
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.CLASS;
/**
* Denotes that the annotated element requires (or may require) one or more permissions.
* <p/>
* Example of requiring a single permission:
- * <pre>{@code
+ * <pre><code>
* @RequiresPermission(Manifest.permission.SET_WALLPAPER)
* public abstract void setWallpaper(Bitmap bitmap) throws IOException;
*
* @RequiresPermission(ACCESS_COARSE_LOCATION)
* public abstract Location getLastKnownLocation(String provider);
- * }</pre>
+ * </code></pre>
* Example of requiring at least one permission from a set:
- * <pre>{@code
+ * <pre><code>
* @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
* public abstract Location getLastKnownLocation(String provider);
- * }</pre>
+ * </code></pre>
* Example of requiring multiple permissions:
- * <pre>{@code
+ * <pre><code>
* @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
* public abstract Location getLastKnownLocation(String provider);
- * }</pre>
+ * </code></pre>
* Example of requiring separate read and write permissions for a content provider:
- * <pre>{@code
- * @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
- * @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
+ * <pre><code>
+ * @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
+ * @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
* public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
+ * </code></pre>
+ * <p>
+ * When specified on a parameter, the annotation indicates that the method requires
+ * a permission which depends on the value of the parameter. For example, consider
+ * {@link android.app.Activity#startActivity(android.content.Intent)}:
+ * <pre>{@code
+ * public void startActivity(@RequiresPermission Intent intent) { ... }
* }</pre>
- *
- * @hide
+ * Notice how there are no actual permission names listed in the annotation. The actual
+ * permissions required will depend on the particular intent passed in. For example,
+ * the code may look like this:
+ * <pre>{@code
+ * Intent intent = new Intent(Intent.ACTION_CALL);
+ * startActivity(intent);
+ * }</pre>
+ * and the actual permission requirement for this particular intent is described on
+ * the Intent name itself:
+ * <pre><code>
+ * @RequiresPermission(Manifest.permission.CALL_PHONE)
+ * public static final String ACTION_CALL = "android.intent.action.CALL";
+ * </code></pre>
*/
@Retention(CLASS)
-@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD})
+@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD,PARAMETER})
public @interface RequiresPermission {
/**
* The name of the permission that is required, if precisely one permission
@@ -87,18 +106,28 @@
boolean conditional() default false;
/**
- * Specifies that the given permission is required for read operations
+ * Specifies that the given permission is required for read operations.
+ * <p>
+ * When specified on a parameter, the annotation indicates that the method requires
+ * a permission which depends on the value of the parameter (and typically
+ * the corresponding field passed in will be one of a set of constants which have
+ * been annotated with a {@code @RequiresPermission} annotation.)
*/
- @Target(FIELD)
+ @Target({FIELD, METHOD, PARAMETER})
@interface Read {
- RequiresPermission value();
+ RequiresPermission value() default @RequiresPermission;
}
/**
- * Specifies that the given permission is required for write operations
+ * Specifies that the given permission is required for write operations.
+ * <p>
+ * When specified on a parameter, the annotation indicates that the method requires
+ * a permission which depends on the value of the parameter (and typically
+ * the corresponding field passed in will be one of a set of constants which have
+ * been annotated with a {@code @RequiresPermission} annotation.)
*/
- @Target(FIELD)
+ @Target({FIELD, METHOD, PARAMETER})
@interface Write {
- RequiresPermission value();
+ RequiresPermission value() default @RequiresPermission;
}
}
diff --git a/annotations/src/android/support/annotation/Size.java b/annotations/src/android/support/annotation/Size.java
index 306fdf3..bd1d7bd 100644
--- a/annotations/src/android/support/annotation/Size.java
+++ b/annotations/src/android/support/annotation/Size.java
@@ -31,7 +31,7 @@
* <p>
* Example:
* <pre>{@code
- * public void getLocationInWindow(@Size(2) int[] location) {
+ * public void getLocationInWindow(@Size(2) int[] location) {
* ...
* }
* }</pre>
diff --git a/annotations/src/android/support/annotation/StringDef.java b/annotations/src/android/support/annotation/StringDef.java
index cae227e..33f71e9 100644
--- a/annotations/src/android/support/annotation/StringDef.java
+++ b/annotations/src/android/support/annotation/StringDef.java
@@ -29,20 +29,20 @@
* type and that its value should be one of the explicitly named constants.
* <p>
* Example:
- * <pre>{@code
+ * <pre><code>
* @Retention(SOURCE)
- * @StringDef({
+ * @StringDef({
* POWER_SERVICE,
* WINDOW_SERVICE,
* LAYOUT_INFLATER_SERVICE
- * })
- * public @interface ServiceName {}
+ * })
+ * public @interface ServiceName {}
* public static final String POWER_SERVICE = "power";
* public static final String WINDOW_SERVICE = "window";
* public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
* ...
- * public abstract Object getSystemService(@ServiceName String name);
- * }</pre>
+ * public abstract Object getSystemService(@ServiceName String name);
+ * </code></pre>
*/
@Retention(SOURCE)
@Target({ANNOTATION_TYPE})
diff --git a/annotations/src/android/support/annotation/UiThread.java b/annotations/src/android/support/annotation/UiThread.java
index a603740..0f8e9d7 100644
--- a/annotations/src/android/support/annotation/UiThread.java
+++ b/annotations/src/android/support/annotation/UiThread.java
@@ -29,11 +29,11 @@
* on the UI thread.
* <p>
* Example:
- * <pre>{@code
+ * <pre><code>
* @UiThread
*
- * public abstract void setText(@NonNull String text) { ... }
- * }</pre>
+ * public abstract void setText(@NonNull String text) { ... }
+ * </code></pre>
*/
@Retention(CLASS)
@Target({METHOD,CONSTRUCTOR,TYPE})
diff --git a/annotations/src/android/support/annotation/WorkerThread.java b/annotations/src/android/support/annotation/WorkerThread.java
index 31e34b4..c003abc 100644
--- a/annotations/src/android/support/annotation/WorkerThread.java
+++ b/annotations/src/android/support/annotation/WorkerThread.java
@@ -29,10 +29,10 @@
* on a worker thread.
* <p>
* Example:
- * <pre>{@code
- * (@WorkerThread
+ * <pre><code>
+ * @WorkerThread
* protected abstract FilterResults performFiltering(CharSequence constraint);
- * }</pre>
+ * </code></pre>
*/
@Retention(CLASS)
@Target({METHOD,CONSTRUCTOR,TYPE})
diff --git a/apicheck.mk b/apicheck.mk
index 739cfb3..97146cf 100644
--- a/apicheck.mk
+++ b/apicheck.mk
@@ -46,8 +46,9 @@
$(call intermediates-dir-for,$(LOCAL_MODULE_CLASS),$(support_module),,COMMON)/src
LOCAL_SDK_VERSION := current
+LOCAL_DROIDDOC_STUB_OUT_DIR := $(TARGET_OUT_COMMON_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/src
+
LOCAL_DROIDDOC_OPTIONS:= \
- -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/src \
-stubpackages "$(subst $(space),:,$(support_module_java_packages))" \
-api $(support_module_api_file) \
-removedApi $(support_module_removed_file) \
diff --git a/build.gradle b/build.gradle
index 8e89705..30b6bc4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,7 +9,7 @@
}
}
-ext.supportVersion = '23.2.0-SNAPSHOT'
+ext.supportVersion = '23.1.1'
ext.extraVersion = 25
ext.supportRepoOut = ''
ext.buildToolsVersion = '22.1.0'
diff --git a/design/src/android/support/design/widget/CollapsingToolbarLayout.java b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
index 62fef07..984ff44 100644
--- a/design/src/android/support/design/widget/CollapsingToolbarLayout.java
+++ b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
@@ -360,12 +360,20 @@
mDrawCollapsingTitle = mDummyView.isShown();
if (mDrawCollapsingTitle) {
- ViewGroupUtils.getDescendantRect(this, mDummyView, mTmpRect);
- mCollapsingTextHelper.setCollapsedBounds(mTmpRect.left, bottom - mTmpRect.height(),
- mTmpRect.right, bottom);
-
final boolean isRtl = ViewCompat.getLayoutDirection(this)
== ViewCompat.LAYOUT_DIRECTION_RTL;
+
+ // Update the collapsed bounds
+ ViewGroupUtils.getDescendantRect(this, mDummyView, mTmpRect);
+ mCollapsingTextHelper.setCollapsedBounds(
+ mTmpRect.left + (isRtl
+ ? mToolbar.getTitleMarginEnd()
+ : mToolbar.getTitleMarginStart()),
+ bottom + mToolbar.getTitleMarginTop() - mTmpRect.height(),
+ mTmpRect.right + (isRtl
+ ? mToolbar.getTitleMarginStart()
+ : mToolbar.getTitleMarginEnd()),
+ bottom - mToolbar.getTitleMarginBottom());
// Update the expanded bounds
mCollapsingTextHelper.setExpandedBounds(
isRtl ? mExpandedMarginEnd : mExpandedMarginStart,
diff --git a/design/src/android/support/design/widget/CoordinatorLayout.java b/design/src/android/support/design/widget/CoordinatorLayout.java
index 3d16ac9..805076c 100644
--- a/design/src/android/support/design/widget/CoordinatorLayout.java
+++ b/design/src/android/support/design/widget/CoordinatorLayout.java
@@ -356,7 +356,7 @@
// Cancel all behaviors beneath the one that intercepted.
// If the event is "down" then we don't have anything to cancel yet.
if (b != null) {
- if (cancelEvent == null) {
+ if (cancelEvent != null) {
final long now = SystemClock.uptimeMillis();
cancelEvent = MotionEvent.obtain(now, now,
MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
diff --git a/design/src/android/support/design/widget/Snackbar.java b/design/src/android/support/design/widget/Snackbar.java
index a328d98..76e43ec 100644
--- a/design/src/android/support/design/widget/Snackbar.java
+++ b/design/src/android/support/design/widget/Snackbar.java
@@ -25,6 +25,7 @@
import android.os.Message;
import android.support.annotation.ColorInt;
import android.support.annotation.IntDef;
+import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.design.R;
@@ -120,6 +121,7 @@
* @hide
*/
@IntDef({LENGTH_INDEFINITE, LENGTH_SHORT, LENGTH_LONG})
+ @IntRange(from = 1)
@Retention(RetentionPolicy.SOURCE)
public @interface Duration {}
diff --git a/percent/src/android/support/percent/PercentLayoutHelper.java b/percent/src/android/support/percent/PercentLayoutHelper.java
index b670385..cce222f 100644
--- a/percent/src/android/support/percent/PercentLayoutHelper.java
+++ b/percent/src/android/support/percent/PercentLayoutHelper.java
@@ -72,6 +72,9 @@
public class PercentLayoutHelper {
private static final String TAG = "PercentLayout";
+ private static final boolean DEBUG = false;
+ private static final boolean VERBOSE = false;
+
private final ViewGroup mHost;
public PercentLayoutHelper(ViewGroup host) {
@@ -96,7 +99,7 @@
* @param heightMeasureSpec Height MeasureSpec of the parent ViewGroup.
*/
public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(TAG, "adjustChildren: " + mHost + " widthMeasureSpec: "
+ View.MeasureSpec.toString(widthMeasureSpec) + " heightMeasureSpec: "
+ View.MeasureSpec.toString(heightMeasureSpec));
@@ -107,13 +110,13 @@
for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
View view = mHost.getChildAt(i);
ViewGroup.LayoutParams params = view.getLayoutParams();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(TAG, "should adjust " + view + " " + params);
}
if (params instanceof PercentLayoutParams) {
PercentLayoutInfo info =
((PercentLayoutParams) params).getPercentLayoutInfo();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(TAG, "using " + info);
}
if (info != null) {
@@ -139,7 +142,7 @@
float value = array.getFraction(R.styleable.PercentLayout_Layout_layout_widthPercent, 1, 1,
-1f);
if (value != -1f) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ if (VERBOSE) {
Log.v(TAG, "percent width: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
@@ -147,7 +150,7 @@
}
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_heightPercent, 1, 1, -1f);
if (value != -1f) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ if (VERBOSE) {
Log.v(TAG, "percent height: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
@@ -155,7 +158,7 @@
}
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginPercent, 1, 1, -1f);
if (value != -1f) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ if (VERBOSE) {
Log.v(TAG, "percent margin: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
@@ -167,7 +170,7 @@
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginLeftPercent, 1, 1,
-1f);
if (value != -1f) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ if (VERBOSE) {
Log.v(TAG, "percent left margin: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
@@ -176,7 +179,7 @@
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginTopPercent, 1, 1,
-1f);
if (value != -1f) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ if (VERBOSE) {
Log.v(TAG, "percent top margin: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
@@ -185,7 +188,7 @@
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginRightPercent, 1, 1,
-1f);
if (value != -1f) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ if (VERBOSE) {
Log.v(TAG, "percent right margin: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
@@ -194,7 +197,7 @@
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginBottomPercent, 1, 1,
-1f);
if (value != -1f) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ if (VERBOSE) {
Log.v(TAG, "percent bottom margin: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
@@ -203,7 +206,7 @@
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginStartPercent, 1, 1,
-1f);
if (value != -1f) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ if (VERBOSE) {
Log.v(TAG, "percent start margin: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
@@ -212,7 +215,7 @@
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginEndPercent, 1, 1,
-1f);
if (value != -1f) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ if (VERBOSE) {
Log.v(TAG, "percent end margin: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
@@ -221,7 +224,7 @@
value = array.getFraction(R.styleable.PercentLayout_Layout_layout_aspectRatio, 1, 1, -1f);
if (value != -1f) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ if (VERBOSE) {
Log.v(TAG, "aspect ratio: " + value);
}
info = info != null ? info : new PercentLayoutInfo();
@@ -229,7 +232,7 @@
}
array.recycle();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(TAG, "constructed: " + info);
}
return info;
@@ -244,13 +247,13 @@
for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
View view = mHost.getChildAt(i);
ViewGroup.LayoutParams params = view.getLayoutParams();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(TAG, "should restore " + view + " " + params);
}
if (params instanceof PercentLayoutParams) {
PercentLayoutInfo info =
((PercentLayoutParams) params).getPercentLayoutInfo();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(TAG, "using " + info);
}
if (info != null) {
@@ -283,7 +286,7 @@
for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
View view = mHost.getChildAt(i);
ViewGroup.LayoutParams params = view.getLayoutParams();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(TAG, "should handle measured state too small " + view + " " + params);
}
if (params instanceof PercentLayoutParams) {
@@ -301,7 +304,7 @@
}
}
}
- if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(TAG, "should trigger second measure pass: " + needsSecondMeasure);
}
return needsSecondMeasure;
@@ -389,7 +392,7 @@
}
}
- if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(TAG, "after fillLayoutParams: (" + params.width + ", " + params.height + ")");
}
}
@@ -432,7 +435,7 @@
MarginLayoutParamsCompat.setMarginEnd(params,
(int) (widthHint * endMarginPercent));
}
- if (Log.isLoggable(TAG, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(TAG, "after fillMarginLayoutParams: (" + params.width + ", " + params.height
+ ")");
}
diff --git a/tests/Android.mk b/tests/Android.mk
index 7bd3b00..7993eed 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -5,7 +5,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, java)
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target android-support-v4 junit-runner
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := AndroidSupportTests
diff --git a/v14/preference/Android.mk b/v14/preference/Android.mk
index fc7a9aa..6f56bac 100644
--- a/v14/preference/Android.mk
+++ b/v14/preference/Android.mk
@@ -21,7 +21,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE := android-support-v14-preference-res
LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
+LOCAL_SRC_FILES := $(call all-java-files-under, ../../v7/preference/constants)
LOCAL_RESOURCE_DIR := \
frameworks/support/v7/appcompat/res \
frameworks/support/v7/preference/res \
@@ -56,4 +56,4 @@
support_module_src_files := $(LOCAL_SRC_FILES)
support_module_java_libraries := $(LOCAL_JAVA_LIBRARIES)
support_module_java_packages := android.support.v14.preference
-include $(SUPPORT_API_CHECK)
\ No newline at end of file
+include $(SUPPORT_API_CHECK)
diff --git a/v14/preference/res/layout/preference_widget_switch.xml b/v14/preference/res/layout/preference_widget_switch.xml
index ae83afa..afc4351 100644
--- a/v14/preference/res/layout/preference_widget_switch.xml
+++ b/v14/preference/res/layout/preference_widget_switch.xml
@@ -18,7 +18,7 @@
<!-- Layout used by SwitchPreference for the switch widget style. This is inflated
inside android.R.layout.preference. -->
<Switch xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/switchWidget"
+ android:id="@android:id/switch_widget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
diff --git a/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java b/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java
index 2a636fe..8316b0d 100644
--- a/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java
+++ b/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java
@@ -72,7 +72,8 @@
}
public MultiSelectListPreference(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.dialogPreferenceStyle);
+ this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.dialogPreferenceStyle,
+ android.R.attr.dialogPreferenceStyle));
}
public MultiSelectListPreference(Context context) {
diff --git a/v14/preference/src/android/support/v14/preference/PreferenceFragment.java b/v14/preference/src/android/support/v14/preference/PreferenceFragment.java
index b9fc935..d1a0697 100644
--- a/v14/preference/src/android/support/v14/preference/PreferenceFragment.java
+++ b/v14/preference/src/android/support/v14/preference/PreferenceFragment.java
@@ -28,7 +28,9 @@
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.annotation.XmlRes;
+import android.support.v4.content.res.TypedArrayUtils;
import android.support.v4.view.ViewCompat;
+import android.support.v7.preference.AndroidResources;
import android.support.v7.preference.DialogPreference;
import android.support.v7.preference.EditTextPreference;
import android.support.v7.preference.ListPreference;
@@ -242,7 +244,8 @@
TypedArray a = mStyledContext.obtainStyledAttributes(null,
R.styleable.PreferenceFragment,
- R.attr.preferenceFragmentStyle,
+ TypedArrayUtils.getAttr(mStyledContext, R.attr.preferenceFragmentStyle,
+ AndroidResources.ANDROID_R_PREFERENCE_FRAGMENT_STYLE),
0);
mLayoutResId = a.getResourceId(R.styleable.PreferenceFragment_android_layout, mLayoutResId);
@@ -263,10 +266,10 @@
final View view = themedInflater.inflate(mLayoutResId, container, false);
- final View rawListContainer = view.findViewById(R.id.list_container);
+ final View rawListContainer = view.findViewById(AndroidResources.ANDROID_R_LIST_CONTAINER);
if (!(rawListContainer instanceof ViewGroup)) {
- throw new RuntimeException("Content has view with id attribute 'R.id.list_container' "
- + "that is not a ViewGroup class");
+ throw new RuntimeException("Content has view with id attribute "
+ + "'android.R.id.list_container' that is not a ViewGroup class");
}
final ViewGroup listContainer = (ViewGroup) rawListContainer;
diff --git a/v14/preference/src/android/support/v14/preference/SwitchPreference.java b/v14/preference/src/android/support/v14/preference/SwitchPreference.java
index 1a46cc4..ac2a9c6 100644
--- a/v14/preference/src/android/support/v14/preference/SwitchPreference.java
+++ b/v14/preference/src/android/support/v14/preference/SwitchPreference.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v7.preference.AndroidResources;
import android.support.v7.preference.PreferenceViewHolder;
import android.support.v7.preference.TwoStatePreference;
import android.util.AttributeSet;
@@ -121,7 +122,8 @@
* @param attrs Style attributes that differ from the default
*/
public SwitchPreference(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.switchPreferenceStyle);
+ this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.switchPreferenceStyle,
+ android.R.attr.switchPreferenceStyle));
}
/**
@@ -136,7 +138,7 @@
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- View switchView = holder.findViewById(R.id.switchWidget);
+ View switchView = holder.findViewById(AndroidResources.ANDROID_R_SWITCH_WIDGET);
syncSwitchView(switchView);
syncSummaryView(holder);
}
diff --git a/v4/Android.mk b/v4/Android.mk
index 4336e40..446e2c0 100644
--- a/v4/Android.mk
+++ b/v4/Android.mk
@@ -223,7 +223,7 @@
# A helper sub-library that makes direct use of V22 APIs.
include $(CLEAR_VARS)
LOCAL_MODULE := android-support-v4-api22
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 22
LOCAL_SRC_FILES := $(call all-java-files-under, api22)
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-api21
include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -235,7 +235,7 @@
# A helper sub-library that makes direct use of V23 APIs.
include $(CLEAR_VARS)
LOCAL_MODULE := android-support-v4-api23
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 23
LOCAL_SRC_FILES := $(call all-java-files-under, api23)
LOCAL_JAVA_LIBRARIES := android-support-v4-api21-internal
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-api22
@@ -245,6 +245,18 @@
# -----------------------------------------------------------------------
+# A helper sub-library that makes direct use of V24 APIs.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v4-api24
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, api24)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-api23
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+support_module_src_files += $(LOCAL_SRC_FILES)
+
+# -----------------------------------------------------------------------
+
# Here is the final static library that apps can link against.
include $(CLEAR_VARS)
LOCAL_MODULE := android-support-v4
@@ -252,7 +264,7 @@
LOCAL_AIDL_INCLUDES := frameworks/support/v4/java
LOCAL_SRC_FILES := $(call all-java-files-under, java) \
$(call all-Iaidl-files-under, java)
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4-api23
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4-api24
include $(BUILD_STATIC_JAVA_LIBRARY)
support_module_src_files += $(LOCAL_SRC_FILES)
diff --git a/v4/NOTICES.md b/v4/NOTICES.md
new file mode 100644
index 0000000..8665d5d
--- /dev/null
+++ b/v4/NOTICES.md
@@ -0,0 +1,12 @@
+# Change Log
+
+## [23.0.1](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v4/) (2015-09-24)
+
+**Breakage and deprecation notices:**
+
+- ExploreByTouchHelper
+ - Several public methods that are only meant to be called by app developers (and not internally by
+ the helper itself) became final. Any code that depends on overriding these methods should be
+ moved elsewhere.
+ - The concept of keyboard and accessibility focus have been clarified. As a result, the
+ getFocusedVirtualView() method has been deprecated and will be removed in a subsequent release.
diff --git a/v4/api/current.txt b/v4/api/current.txt
index e3c3e6f..603d078 100644
--- a/v4/api/current.txt
+++ b/v4/api/current.txt
@@ -2106,6 +2106,15 @@
method public abstract void onActionProviderVisibilityChanged(boolean);
}
+ public final class AsyncLayoutInflater {
+ ctor public AsyncLayoutInflater(android.content.Context);
+ method public void inflate(int, android.view.ViewGroup, android.support.v4.view.AsyncLayoutInflater.OnInflateFinishedListener);
+ }
+
+ public static abstract interface AsyncLayoutInflater.OnInflateFinishedListener {
+ method public abstract void onInflateFinished(android.view.View, int, android.view.ViewGroup);
+ }
+
public class GestureDetectorCompat {
ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener);
ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler);
@@ -3251,17 +3260,26 @@
public abstract class ExploreByTouchHelper extends android.support.v4.view.AccessibilityDelegateCompat {
ctor public ExploreByTouchHelper(android.view.View);
- method public boolean dispatchHoverEvent(android.view.MotionEvent);
- method public int getFocusedVirtualView();
+ method public final boolean clearKeyboardFocusForVirtualView(int);
+ method public final boolean dispatchHoverEvent(android.view.MotionEvent);
+ method public final boolean dispatchKeyEvent(android.view.KeyEvent);
+ method public final int getAccessibilityFocusedVirtualViewId();
+ method public deprecated int getFocusedVirtualView();
+ method public final int getKeyboardFocusedVirtualViewId();
method protected abstract int getVirtualViewAt(float, float);
method protected abstract void getVisibleVirtualViews(java.util.List<java.lang.Integer>);
- method public void invalidateRoot();
- method public void invalidateVirtualView(int);
+ method public final void invalidateRoot();
+ method public final void invalidateVirtualView(int);
+ method public final void invalidateVirtualView(int, int);
+ method public final void onFocusChanged(boolean, int, android.graphics.Rect);
method protected abstract boolean onPerformActionForVirtualView(int, int, android.os.Bundle);
- method protected abstract void onPopulateEventForVirtualView(int, android.view.accessibility.AccessibilityEvent);
- method public void onPopulateNodeForHost(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+ method protected void onPopulateEventForHost(android.view.accessibility.AccessibilityEvent);
+ method protected void onPopulateEventForVirtualView(int, android.view.accessibility.AccessibilityEvent);
+ method protected void onPopulateNodeForHost(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
method protected abstract void onPopulateNodeForVirtualView(int, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
- method public boolean sendEventForVirtualView(int, int);
+ method protected void onVirtualViewKeyboardFocusChanged(int, boolean);
+ method public final boolean requestKeyboardFocusForVirtualView(int);
+ method public final boolean sendEventForVirtualView(int, int);
field public static final int HOST_ID = -1; // 0xffffffff
field public static final int INVALID_ID = -2147483648; // 0x80000000
}
diff --git a/v4/api21/android/support/v4/app/FragmentTransitionCompat21.java b/v4/api21/android/support/v4/app/FragmentTransitionCompat21.java
index ddcff2e..37c4b97 100644
--- a/v4/api21/android/support/v4/app/FragmentTransitionCompat21.java
+++ b/v4/api21/android/support/v4/app/FragmentTransitionCompat21.java
@@ -92,6 +92,36 @@
return transitionSet;
}
+ private static void excludeViews(Transition transition, Transition fromTransition,
+ ArrayList<View> views, boolean exclude) {
+ if (transition != null) {
+ final int viewCount = fromTransition == null ? 0 : views.size();
+ for (int i = 0; i < viewCount; i++) {
+ transition.excludeTarget(views.get(i), exclude);
+ }
+ }
+ }
+
+ /**
+ * Exclude (or remove the exclude) of shared element views from the enter and exit transitions.
+ *
+ * @param enterTransitionObj The enter transition
+ * @param exitTransitionObj The exit transition
+ * @param sharedElementTransitionObj The shared element transition
+ * @param views The shared element target views.
+ * @param exclude <code>true</code> to exclude or <code>false</code> to remove the excluded
+ * views.
+ */
+ public static void excludeSharedElementViews(Object enterTransitionObj,
+ Object exitTransitionObj, Object sharedElementTransitionObj, ArrayList<View> views,
+ boolean exclude) {
+ Transition enterTransition = (Transition) enterTransitionObj;
+ Transition exitTransition = (Transition) exitTransitionObj;
+ Transition sharedElementTransition = (Transition) sharedElementTransitionObj;
+ excludeViews(enterTransition, sharedElementTransition, views, exclude);
+ excludeViews(exitTransition, sharedElementTransition, views, exclude);
+ }
+
/**
* Prepares the enter transition by adding a non-existent view to the transition's target list
* and setting it epicenter callback. By adding a non-existent view to the target list,
@@ -104,35 +134,42 @@
* capturing the final state of the Transition.</p>
*/
public static void addTransitionTargets(Object enterTransitionObject,
- Object sharedElementTransitionObject, final View container,
+ Object sharedElementTransitionObject, Object exitTransitionObject, final View container,
final ViewRetriever inFragment, final View nonExistentView,
EpicenterView epicenterView, final Map<String, String> nameOverrides,
- final ArrayList<View> enteringViews, final Map<String, View> namedViews,
- final Map<String, View> renamedViews, final ArrayList<View> sharedElementTargets) {
+ final ArrayList<View> enteringViews, final ArrayList<View> exitingViews,
+ final Map<String, View> namedViews, final Map<String, View> renamedViews,
+ final ArrayList<View> sharedElementTargets) {
+ final Transition enterTransition = (Transition) enterTransitionObject;
+ final Transition exitTransition = (Transition) exitTransitionObject;
+ final Transition sharedElementTransition = (Transition) sharedElementTransitionObject;
+ excludeViews(enterTransition, exitTransition, exitingViews, true);
if (enterTransitionObject != null || sharedElementTransitionObject != null) {
- final Transition enterTransition = (Transition) enterTransitionObject;
if (enterTransition != null) {
enterTransition.addTarget(nonExistentView);
}
if (sharedElementTransitionObject != null) {
- setSharedElementTargets(sharedElementTransitionObject, nonExistentView,
+ setSharedElementTargets(sharedElementTransition, nonExistentView,
namedViews, sharedElementTargets);
+ excludeViews(enterTransition, sharedElementTransition, sharedElementTargets, true);
+ excludeViews(exitTransition, sharedElementTransition, sharedElementTargets, true);
}
- if (inFragment != null) {
- container.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- public boolean onPreDraw() {
- container.getViewTreeObserver().removeOnPreDrawListener(this);
- if (enterTransition != null) {
- enterTransition.removeTarget(nonExistentView);
- }
+ container.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ container.getViewTreeObserver().removeOnPreDrawListener(this);
+ if (enterTransition != null) {
+ enterTransition.removeTarget(nonExistentView);
+ }
+ if (inFragment != null) {
View fragmentView = inFragment.getView();
if (fragmentView != null) {
if (!nameOverrides.isEmpty()) {
findNamedViews(renamedViews, fragmentView);
renamedViews.keySet().retainAll(nameOverrides.values());
- for (Map.Entry<String, String> entry : nameOverrides.entrySet()) {
+ for (Map.Entry<String, String> entry : nameOverrides
+ .entrySet()) {
String to = entry.getValue();
View view = renamedViews.get(to);
if (view != null) {
@@ -148,10 +185,12 @@
addTargets(enterTransition, enteringViews);
}
}
- return true;
}
- });
- }
+ excludeViews(exitTransition, enterTransition, enteringViews, true);
+
+ return true;
+ }
+ });
setSharedElementEpicenter(enterTransition, epicenterView);
}
}
@@ -355,9 +394,15 @@
sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
if (enterTransition != null) {
removeTargets(enterTransition, enteringViews);
+ excludeViews(enterTransition, exitTransition, exitingViews, false);
+ excludeViews(enterTransition, sharedElementTransition, sharedElementTargets,
+ false);
}
if (exitTransition != null) {
removeTargets(exitTransition, exitingViews);
+ excludeViews(exitTransition, enterTransition, enteringViews, false);
+ excludeViews(exitTransition, sharedElementTransition, sharedElementTargets,
+ false);
}
if (sharedElementTransition != null) {
removeTargets(sharedElementTransition, sharedElementTargets);
diff --git a/v4/api24/android/support/v4/view/ViewParentCompatApi24.java b/v4/api24/android/support/v4/view/ViewParentCompatApi24.java
new file mode 100644
index 0000000..4026a9d
--- /dev/null
+++ b/v4/api24/android/support/v4/view/ViewParentCompatApi24.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.support.v4.view;
+
+import android.view.View;
+import android.view.ViewParent;
+
+class ViewParentCompatApi24 {
+ public static int findDependentLayoutAxes(ViewParent parent, View child, int axisFilter) {
+ return parent.findDependentLayoutAxes(child, axisFilter);
+ }
+}
diff --git a/v4/build.gradle b/v4/build.gradle
index dce3a15..82b6b16 100644
--- a/v4/build.gradle
+++ b/v4/build.gradle
@@ -30,8 +30,8 @@
def api20SS = createApiSourceset('api20', 'api20', '20', kitkatSS)
def api21SS = createApiSourceset('api21', 'api21', '21', api20SS)
def api22SS = createApiSourceset('api22', 'api22', '22', api21SS)
-def api23SS = createApiSourceset('api23', 'api23', 'current', api22SS)
-
+def api23SS = createApiSourceset('api23', 'api23', '23', api22SS)
+def api24SS = createApiSourceset('api24', 'api24', 'current', api23SS)
def createApiSourceset(String name, String folder, String apiLevel, SourceSet previousSource) {
def sourceSet = sourceSets.create(name)
diff --git a/v4/java/android/support/v4/app/ActivityCompat.java b/v4/java/android/support/v4/app/ActivityCompat.java
index 0926d49..4e699d4 100644
--- a/v4/java/android/support/v4/app/ActivityCompat.java
+++ b/v4/java/android/support/v4/app/ActivityCompat.java
@@ -300,6 +300,12 @@
* When checking whether you have a permission you should use {@link
* #checkSelfPermission(android.content.Context, String)}.
* </p>
+ * <p>
+ * Calling this API for permissions already granted to your app would show UI
+ * to the user to decided whether the app can still hold these permissions. This
+ * can be useful if the way your app uses the data guarded by the permissions
+ * changes significantly.
+ * </p>
*
* @param activity The target activity.
* @param permissions The requested permissions.
diff --git a/v4/java/android/support/v4/app/BackStackRecord.java b/v4/java/android/support/v4/app/BackStackRecord.java
index 5d3f82e..88f7655 100644
--- a/v4/java/android/support/v4/app/BackStackRecord.java
+++ b/v4/java/android/support/v4/app/BackStackRecord.java
@@ -1187,7 +1187,8 @@
callback.onSharedElementStart(names, views, null);
}
prepareSharedElementTransition(state, sceneRoot, sharedElementTransition,
- inFragment, outFragment, isBack, sharedElementTargets);
+ inFragment, outFragment, isBack, sharedElementTargets, enterTransition,
+ exitTransition);
}
}
if (enterTransition == null && sharedElementTransition == null &&
@@ -1234,9 +1235,9 @@
if (transition != null) {
FragmentTransitionCompat21.addTransitionTargets(enterTransition,
- sharedElementTransition, sceneRoot, viewRetriever, state.nonExistentView,
- state.enteringEpicenterView, state.nameOverrides, enteringViews,
- namedViews, renamedViews, sharedElementTargets);
+ sharedElementTransition, exitTransition, sceneRoot, viewRetriever,
+ state.nonExistentView, state.enteringEpicenterView, state.nameOverrides,
+ enteringViews, exitingViews, namedViews, renamedViews, sharedElementTargets);
excludeHiddenFragmentsAfterEnter(sceneRoot, state, containerId, transition);
// We want to exclude hidden views later, so we need a non-null list in the
@@ -1258,16 +1259,22 @@
private void prepareSharedElementTransition(final TransitionState state, final View sceneRoot,
final Object sharedElementTransition, final Fragment inFragment,
final Fragment outFragment, final boolean isBack,
- final ArrayList<View> sharedElementTargets) {
- sceneRoot.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ final ArrayList<View> sharedElementTargets, final Object enterTransition,
+ final Object exitTransition) {
+ if (sharedElementTransition != null) {
+ sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
- if (sharedElementTransition != null) {
+ // Remove the exclude for the shared elements from the exiting fragment.
FragmentTransitionCompat21.removeTargets(sharedElementTransition,
sharedElementTargets);
+ // keep the nonExistentView as excluded so the list doesn't get emptied
+ sharedElementTargets.remove(state.nonExistentView);
+ FragmentTransitionCompat21.excludeSharedElementViews(enterTransition,
+ exitTransition, sharedElementTransition, sharedElementTargets, false);
sharedElementTargets.clear();
ArrayMap<String, View> namedViews = mapSharedElementsIn(
@@ -1279,11 +1286,14 @@
callSharedElementEnd(state, inFragment, outFragment, isBack,
namedViews);
- }
- return true;
- }
- });
+ // Exclude the shared elements from the entering fragment.
+ FragmentTransitionCompat21.excludeSharedElementViews(enterTransition,
+ exitTransition, sharedElementTransition, sharedElementTargets, true);
+ return true;
+ }
+ });
+ }
}
private void callSharedElementEnd(TransitionState state, Fragment inFragment,
diff --git a/v4/java/android/support/v4/app/Fragment.java b/v4/java/android/support/v4/app/Fragment.java
index 48fc495..a5013c6 100644
--- a/v4/java/android/support/v4/app/Fragment.java
+++ b/v4/java/android/support/v4/app/Fragment.java
@@ -59,6 +59,7 @@
final boolean mRetainInstance;
final boolean mDetached;
final Bundle mArguments;
+ final boolean mHidden;
Bundle mSavedFragmentState;
@@ -74,6 +75,7 @@
mRetainInstance = frag.mRetainInstance;
mDetached = frag.mDetached;
mArguments = frag.mArguments;
+ mHidden = frag.mHidden;
}
public FragmentState(Parcel in) {
@@ -86,6 +88,7 @@
mRetainInstance = in.readInt() != 0;
mDetached = in.readInt() != 0;
mArguments = in.readBundle();
+ mHidden = in.readInt() != 0;
mSavedFragmentState = in.readBundle();
}
@@ -113,6 +116,7 @@
mInstance.mTag = mTag;
mInstance.mRetainInstance = mRetainInstance;
mInstance.mDetached = mDetached;
+ mInstance.mHidden = mHidden;
mInstance.mFragmentManager = host.mFragmentManager;
if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
@@ -135,6 +139,7 @@
dest.writeInt(mRetainInstance ? 1 : 0);
dest.writeInt(mDetached ? 1 : 0);
dest.writeBundle(mArguments);
+ dest.writeInt(mHidden? 1 : 0);
dest.writeBundle(mSavedFragmentState);
}
@@ -981,6 +986,12 @@
* android.content.Context#checkSelfPermission(String)}.
* </p>
* <p>
+ * Calling this API for permissions already granted to your app would show UI
+ * to the user to decided whether the app can still hold these permissions. This
+ * can be useful if the way your app uses the data guarded by the permissions
+ * changes significantly.
+ * </p>
+ * <p>
* A sample permissions request looks like this:
* </p>
* <code><pre><p>
diff --git a/v4/java/android/support/v4/app/FragmentTransaction.java b/v4/java/android/support/v4/app/FragmentTransaction.java
index 48f2088..b128efc 100644
--- a/v4/java/android/support/v4/app/FragmentTransaction.java
+++ b/v4/java/android/support/v4/app/FragmentTransaction.java
@@ -162,7 +162,7 @@
public static final int TRANSIT_EXIT_MASK = 0x2000;
/** @hide */
- @IntDef({TRANSIT_NONE, TRANSIT_FRAGMENT_OPEN, TRANSIT_FRAGMENT_CLOSE})
+ @IntDef({TRANSIT_NONE, TRANSIT_FRAGMENT_OPEN, TRANSIT_FRAGMENT_CLOSE, TRANSIT_FRAGMENT_FADE})
@Retention(RetentionPolicy.SOURCE)
private @interface Transit {}
@@ -212,7 +212,7 @@
/**
* Select a standard transition animation for this transaction. May be
* one of {@link #TRANSIT_NONE}, {@link #TRANSIT_FRAGMENT_OPEN},
- * or {@link #TRANSIT_FRAGMENT_CLOSE}
+ * {@link #TRANSIT_FRAGMENT_CLOSE}, or {@link #TRANSIT_FRAGMENT_FADE}.
*/
public abstract FragmentTransaction setTransition(@Transit int transit);
diff --git a/v4/java/android/support/v4/content/FileProvider.java b/v4/java/android/support/v4/content/FileProvider.java
index 515ce1c..9d6d803 100644
--- a/v4/java/android/support/v4/content/FileProvider.java
+++ b/v4/java/android/support/v4/content/FileProvider.java
@@ -365,7 +365,7 @@
*
* @param context A {@link Context} for the current component.
* @param authority The authority of a {@link FileProvider} defined in a
- * {@code <provider>} element in your app's manifest.
+ * {@code <provider>} element in your app's manifest.
* @param file A {@link File} pointing to the filename for which you want a
* <code>content</code> {@link Uri}.
* @return A content URI for the file.
@@ -547,7 +547,7 @@
/**
* Parse and return {@link PathStrategy} for given authority as defined in
- * {@link #META_DATA_FILE_PROVIDER_PATHS} {@code <meta-data>}.
+ * {@link #META_DATA_FILE_PROVIDER_PATHS} {@code <meta-data>}.
*
* @see #getPathStrategy(Context, String)
*/
diff --git a/v4/java/android/support/v4/content/res/TypedArrayUtils.java b/v4/java/android/support/v4/content/res/TypedArrayUtils.java
index 79e4ac8..0cc5885 100644
--- a/v4/java/android/support/v4/content/res/TypedArrayUtils.java
+++ b/v4/java/android/support/v4/content/res/TypedArrayUtils.java
@@ -15,10 +15,12 @@
*/
package android.support.v4.content.res;
+import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.support.annotation.AnyRes;
import android.support.annotation.StyleableRes;
+import android.util.TypedValue;
/**
* Compat methods for accessing TypedArray values.
@@ -70,4 +72,13 @@
}
return val;
}
+
+ public static int getAttr(Context context, int attr, int fallbackAttr) {
+ TypedValue value = new TypedValue();
+ context.getTheme().resolveAttribute(attr, value, true);
+ if (value.resourceId != 0) {
+ return attr;
+ }
+ return fallbackAttr;
+ }
}
diff --git a/v4/java/android/support/v4/util/SimpleArrayMap.java b/v4/java/android/support/v4/util/SimpleArrayMap.java
index 1b1577a..c7d4b5d 100644
--- a/v4/java/android/support/v4/util/SimpleArrayMap.java
+++ b/v4/java/android/support/v4/util/SimpleArrayMap.java
@@ -522,17 +522,42 @@
/**
* {@inheritDoc}
*
- * <p>This implementation returns false if the object is not a map, or
- * if the maps have different sizes. Otherwise, for each key in this map,
- * values of both maps are compared. If the values for any key are not
- * equal, the method returns false, otherwise it returns true.
+ * <p>This implementation returns false if the object is not a Map or
+ * SimpleArrayMap, or if the maps have different sizes. Otherwise, for each
+ * key in this map, values of both maps are compared. If the values for any
+ * key are not equal, the method returns false, otherwise it returns true.
*/
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
- if (object instanceof Map) {
+ if (object instanceof SimpleArrayMap) {
+ SimpleArrayMap<?, ?> map = (SimpleArrayMap<?, ?>) object;
+ if (size() != map.size()) {
+ return false;
+ }
+
+ try {
+ for (int i=0; i<mSize; i++) {
+ K key = keyAt(i);
+ V mine = valueAt(i);
+ Object theirs = map.get(key);
+ if (mine == null) {
+ if (theirs != null || !map.containsKey(key)) {
+ return false;
+ }
+ } else if (!mine.equals(theirs)) {
+ return false;
+ }
+ }
+ } catch (NullPointerException ignored) {
+ return false;
+ } catch (ClassCastException ignored) {
+ return false;
+ }
+ return true;
+ } else if (object instanceof Map) {
Map<?, ?> map = (Map<?, ?>) object;
if (size() != map.size()) {
return false;
diff --git a/v4/java/android/support/v4/view/AccessibilityDelegateCompat.java b/v4/java/android/support/v4/view/AccessibilityDelegateCompat.java
index 0ea755e..4d0281e 100644
--- a/v4/java/android/support/v4/view/AccessibilityDelegateCompat.java
+++ b/v4/java/android/support/v4/view/AccessibilityDelegateCompat.java
@@ -27,6 +27,18 @@
/**
* Helper for accessing {@link View.AccessibilityDelegate} introduced after
* API level 4 in a backwards compatible fashion.
+ * <p>
+ * <strong>Note:</strong> On platform versions prior to
+ * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on
+ * views in the {@code android.widget.*} package are called <i>before</i>
+ * host methods. This prevents certain properties such as class name from
+ * being modified by overriding
+ * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)},
+ * as any changes will be overwritten by the host class.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate
+ * methods are called <i>after</i> host methods, which all properties to be
+ * modified without being overwritten by the host class.
*/
public class AccessibilityDelegateCompat {
diff --git a/v4/java/android/support/v4/view/AsyncLayoutInflater.java b/v4/java/android/support/v4/view/AsyncLayoutInflater.java
new file mode 100644
index 0000000..6e88f59
--- /dev/null
+++ b/v4/java/android/support/v4/view/AsyncLayoutInflater.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.view;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Handler.Callback;
+import android.os.Looper;
+import android.os.Message;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+import android.support.v4.util.Pools.SynchronizedPool;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.concurrent.ArrayBlockingQueue;
+
+/**
+ * <p>Helper class for inflating layouts asynchronously. To use, construct
+ * an instance of {@link AsyncLayoutInflater} on the UI thread and call
+ * {@link #inflate(int, ViewGroup, OnInflateFinishedListener)}. The
+ * {@link OnInflateFinishedListener} will be invoked on the UI thread
+ * when the inflate request has completed.
+ *
+ * <p>This is intended for parts of the UI that are created lazily or in
+ * response to user interactions. This allows the UI thread to continue
+ * to be responsive & animate while the relatively heavy inflate
+ * is being performed.
+ *
+ * <p>For a layout to be inflated asynchronously it needs to have a parent
+ * whose {@link ViewGroup#generateLayoutParams(AttributeSet)} is thread-safe
+ * and all the Views being constructed as part of inflation must not create
+ * any {@link Handler}s or otherwise call {@link Looper#myLooper()}. If the
+ * layout that is trying to be inflated cannot be constructed
+ * asynchronously for whatever reason, {@link AsyncLayoutInflater} will
+ * automatically fall back to inflating on the UI thread.
+ *
+ * <p>NOTE that the inflated View hierarchy is NOT added to the parent. It is
+ * equivalent to calling {@link LayoutInflater#inflate(int, ViewGroup, boolean)}
+ * with attachToRoot set to false. Callers will likely want to call
+ * {@link ViewGroup#addView(View)} in the {@link OnInflateFinishedListener}
+ * callback at a minimum.
+ *
+ * <p>This inflater does not support setting a {@link LayoutInflater.Factory}
+ * nor {@link LayoutInflater.Factory2}. Similarly it does not support inflating
+ * layouts that contain fragments.
+ */
+public final class AsyncLayoutInflater {
+ private static final String TAG = "AsyncLayoutInflater";
+
+ private LayoutInflater mInflater;
+ private Handler mHandler;
+ private InflateThread mInflateThread;
+
+ public AsyncLayoutInflater(@NonNull Context context) {
+ mInflater = new BasicInflater(context);
+ mHandler = new Handler(mHandlerCallback);
+ mInflateThread = InflateThread.getInstance();
+ }
+
+ @UiThread
+ public void inflate(@LayoutRes int resid, @Nullable ViewGroup parent,
+ @NonNull OnInflateFinishedListener callback) {
+ if (callback == null) {
+ throw new NullPointerException("callback argument may not be null!");
+ }
+ InflateRequest request = mInflateThread.obtainRequest();
+ request.inflater = this;
+ request.resid = resid;
+ request.parent = parent;
+ request.callback = callback;
+ mInflateThread.enqueue(request);
+ }
+
+ private Callback mHandlerCallback = new Callback() {
+ @Override
+ public boolean handleMessage(Message msg) {
+ InflateRequest request = (InflateRequest) msg.obj;
+ if (request.view == null) {
+ request.view = mInflater.inflate(
+ request.resid, request.parent, false);
+ }
+ request.callback.onInflateFinished(
+ request.view, request.resid, request.parent);
+ mInflateThread.releaseRequest(request);
+ return true;
+ }
+ };
+
+ public interface OnInflateFinishedListener {
+ public void onInflateFinished(View view, int resid, ViewGroup parent);
+ }
+
+ private static class InflateRequest {
+ AsyncLayoutInflater inflater;
+ ViewGroup parent;
+ int resid;
+ View view;
+ OnInflateFinishedListener callback;
+ }
+
+ private static class BasicInflater extends LayoutInflater {
+ private static final String[] sClassPrefixList = {
+ "android.widget.",
+ "android.webkit.",
+ "android.app."
+ };
+
+ public BasicInflater(Context context) {
+ super(context);
+ }
+
+ @Override
+ public LayoutInflater cloneInContext(Context newContext) {
+ return new BasicInflater(newContext);
+ }
+
+ @Override
+ protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
+ for (String prefix : sClassPrefixList) {
+ try {
+ View view = createView(name, prefix, attrs);
+ if (view != null) {
+ return view;
+ }
+ } catch (ClassNotFoundException e) {
+ // In this case we want to let the base class take a crack
+ // at it.
+ }
+ }
+
+ return super.onCreateView(name, attrs);
+ }
+ }
+
+ private static class InflateThread extends Thread {
+ private static final InflateThread sInstance;
+ static {
+ sInstance = new InflateThread();
+ sInstance.start();
+ }
+
+ public static InflateThread getInstance() {
+ return sInstance;
+ }
+
+ private ArrayBlockingQueue<InflateRequest> mQueue
+ = new ArrayBlockingQueue<>(10);
+ private SynchronizedPool<InflateRequest> mRequestPool
+ = new SynchronizedPool<>(10);
+
+ @Override
+ public void run() {
+ while (true) {
+ InflateRequest request;
+ try {
+ request = mQueue.take();
+ } catch (InterruptedException ex) {
+ // Odd, just continue
+ Log.w(TAG, ex);
+ continue;
+ }
+
+ try {
+ request.view = request.inflater.mInflater.inflate(
+ request.resid, request.parent, false);
+ } catch (RuntimeException ex) {
+ // Probably a Looper failure, retry on the UI thread
+ Log.w(TAG, "Failed to inflate resource in the background! Retrying on the UI thread",
+ ex);
+ }
+ Message.obtain(request.inflater.mHandler, 0, request)
+ .sendToTarget();
+ }
+ }
+
+ public InflateRequest obtainRequest() {
+ InflateRequest obj = mRequestPool.acquire();
+ if (obj == null) {
+ obj = new InflateRequest();
+ }
+ return obj;
+ }
+
+ public void releaseRequest(InflateRequest obj) {
+ obj.callback = null;
+ obj.inflater = null;
+ obj.parent = null;
+ obj.resid = 0;
+ obj.view = null;
+ mRequestPool.release(obj);
+ }
+
+ public void enqueue(InflateRequest request) {
+ try {
+ mQueue.put(request);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(
+ "Failed to enqueue async inflate request", e);
+ }
+ }
+ }
+}
diff --git a/v4/java/android/support/v4/view/ViewCompat.java b/v4/java/android/support/v4/view/ViewCompat.java
index b88d7e5..284a8b6 100644
--- a/v4/java/android/support/v4/view/ViewCompat.java
+++ b/v4/java/android/support/v4/view/ViewCompat.java
@@ -53,6 +53,21 @@
public class ViewCompat {
private static final String TAG = "ViewCompat";
+ /** @hide */
+ @IntDef({View.FOCUS_LEFT, View.FOCUS_UP, View.FOCUS_RIGHT, View.FOCUS_DOWN,
+ View.FOCUS_FORWARD, View.FOCUS_BACKWARD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FocusDirection {}
+
+ /** @hide */
+ @IntDef({View.FOCUS_LEFT, View.FOCUS_UP, View.FOCUS_RIGHT, View.FOCUS_DOWN})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FocusRealDirection {}
+
+ /** @hide */
+ @IntDef({View.FOCUS_FORWARD, View.FOCUS_BACKWARD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FocusRelativeDirection {}
/** @hide */
@IntDef({OVER_SCROLL_ALWAYS, OVER_SCROLL_IF_CONTENT_SCROLLS, OVER_SCROLL_NEVER})
@@ -293,36 +308,36 @@
/**
* Scroll indicator direction for the top edge of the view.
*
- * @see #setScrollIndicators(int)
- * @see #setScrollIndicators(int, int)
- * @see #getScrollIndicators()
+ * @see #setScrollIndicators(View, int)
+ * @see #setScrollIndicators(View, int, int)
+ * @see #getScrollIndicators(View)
*/
public static final int SCROLL_INDICATOR_TOP = 0x1;
/**
* Scroll indicator direction for the bottom edge of the view.
*
- * @see #setScrollIndicators(int)
- * @see #setScrollIndicators(int, int)
- * @see #getScrollIndicators()
+ * @see #setScrollIndicators(View, int)
+ * @see #setScrollIndicators(View, int, int)
+ * @see #getScrollIndicators(View)
*/
public static final int SCROLL_INDICATOR_BOTTOM = 0x2;
/**
* Scroll indicator direction for the left edge of the view.
*
- * @see #setScrollIndicators(int)
- * @see #setScrollIndicators(int, int)
- * @see #getScrollIndicators()
+ * @see #setScrollIndicators(View, int)
+ * @see #setScrollIndicators(View, int, int)
+ * @see #getScrollIndicators(View)
*/
public static final int SCROLL_INDICATOR_LEFT = 0x4;
/**
* Scroll indicator direction for the right edge of the view.
*
- * @see #setScrollIndicators(int)
- * @see #setScrollIndicators(int, int)
- * @see #getScrollIndicators()
+ * @see #setScrollIndicators(View, int)
+ * @see #setScrollIndicators(View, int, int)
+ * @see #getScrollIndicators(View)
*/
public static final int SCROLL_INDICATOR_RIGHT = 0x8;
@@ -338,93 +353,93 @@
/**
* Scroll indicator direction for the ending edge of the view.
*
- * @see #setScrollIndicators(int)
- * @see #setScrollIndicators(int, int)
- * @see #getScrollIndicators()
+ * @see #setScrollIndicators(View, int)
+ * @see #setScrollIndicators(View, int, int)
+ * @see #getScrollIndicators(View)
*/
public static final int SCROLL_INDICATOR_END = 0x20;
interface ViewCompatImpl {
- public boolean canScrollHorizontally(View v, int direction);
- public boolean canScrollVertically(View v, int direction);
- public int getOverScrollMode(View v);
- public void setOverScrollMode(View v, int mode);
- public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event);
- public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event);
- public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info);
- public void setAccessibilityDelegate(View v, @Nullable AccessibilityDelegateCompat delegate);
- public boolean hasAccessibilityDelegate(View v);
- public boolean hasTransientState(View view);
- public void setHasTransientState(View view, boolean hasTransientState);
- public void postInvalidateOnAnimation(View view);
- public void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom);
- public void postOnAnimation(View view, Runnable action);
- public void postOnAnimationDelayed(View view, Runnable action, long delayMillis);
- public int getImportantForAccessibility(View view);
- public void setImportantForAccessibility(View view, int mode);
- public boolean isImportantForAccessibility(View view);
- public boolean performAccessibilityAction(View view, int action, Bundle arguments);
- public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view);
- public float getAlpha(View view);
- public void setLayerType(View view, int layerType, Paint paint);
- public int getLayerType(View view);
- public int getLabelFor(View view);
- public void setLabelFor(View view, int id);
- public void setLayerPaint(View view, Paint paint);
- public int getLayoutDirection(View view);
- public void setLayoutDirection(View view, int layoutDirection);
- public ViewParent getParentForAccessibility(View view);
- public boolean isOpaque(View view);
- public int resolveSizeAndState(int size, int measureSpec, int childMeasuredState);
- public int getMeasuredWidthAndState(View view);
- public int getMeasuredHeightAndState(View view);
- public int getMeasuredState(View view);
- public int getAccessibilityLiveRegion(View view);
- public void setAccessibilityLiveRegion(View view, int mode);
- public int getPaddingStart(View view);
- public int getPaddingEnd(View view);
- public void setPaddingRelative(View view, int start, int top, int end, int bottom);
- public void dispatchStartTemporaryDetach(View view);
- public void dispatchFinishTemporaryDetach(View view);
- public float getX(View view);
- public float getY(View view);
- public float getRotation(View view);
- public float getRotationX(View view);
- public float getRotationY(View view);
- public float getScaleX(View view);
- public float getScaleY(View view);
- public float getTranslationX(View view);
- public float getTranslationY(View view);
- public int getMinimumWidth(View view);
- public int getMinimumHeight(View view);
- public ViewPropertyAnimatorCompat animate(View view);
- public void setRotation(View view, float value);
- public void setRotationX(View view, float value);
- public void setRotationY(View view, float value);
- public void setScaleX(View view, float value);
- public void setScaleY(View view, float value);
- public void setTranslationX(View view, float value);
- public void setTranslationY(View view, float value);
- public void setX(View view, float value);
- public void setY(View view, float value);
- public void setAlpha(View view, float value);
- public void setPivotX(View view, float value);
- public void setPivotY(View view, float value);
- public float getPivotX(View view);
- public float getPivotY(View view);
- public void setElevation(View view, float elevation);
- public float getElevation(View view);
- public void setTranslationZ(View view, float translationZ);
- public float getTranslationZ(View view);
- public void setClipBounds(View view, Rect clipBounds);
- public Rect getClipBounds(View view);
- public void setTransitionName(View view, String transitionName);
- public String getTransitionName(View view);
- public int getWindowSystemUiVisibility(View view);
- public void requestApplyInsets(View view);
- public void setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled);
- public boolean getFitsSystemWindows(View view);
- public boolean hasOverlappingRendering(View view);
+ boolean canScrollHorizontally(View v, int direction);
+ boolean canScrollVertically(View v, int direction);
+ int getOverScrollMode(View v);
+ void setOverScrollMode(View v, int mode);
+ void onInitializeAccessibilityEvent(View v, AccessibilityEvent event);
+ void onPopulateAccessibilityEvent(View v, AccessibilityEvent event);
+ void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info);
+ void setAccessibilityDelegate(View v, @Nullable AccessibilityDelegateCompat delegate);
+ boolean hasAccessibilityDelegate(View v);
+ boolean hasTransientState(View view);
+ void setHasTransientState(View view, boolean hasTransientState);
+ void postInvalidateOnAnimation(View view);
+ void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom);
+ void postOnAnimation(View view, Runnable action);
+ void postOnAnimationDelayed(View view, Runnable action, long delayMillis);
+ int getImportantForAccessibility(View view);
+ void setImportantForAccessibility(View view, int mode);
+ boolean isImportantForAccessibility(View view);
+ boolean performAccessibilityAction(View view, int action, Bundle arguments);
+ AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view);
+ float getAlpha(View view);
+ void setLayerType(View view, int layerType, Paint paint);
+ int getLayerType(View view);
+ int getLabelFor(View view);
+ void setLabelFor(View view, int id);
+ void setLayerPaint(View view, Paint paint);
+ int getLayoutDirection(View view);
+ void setLayoutDirection(View view, int layoutDirection);
+ ViewParent getParentForAccessibility(View view);
+ boolean isOpaque(View view);
+ int resolveSizeAndState(int size, int measureSpec, int childMeasuredState);
+ int getMeasuredWidthAndState(View view);
+ int getMeasuredHeightAndState(View view);
+ int getMeasuredState(View view);
+ int getAccessibilityLiveRegion(View view);
+ void setAccessibilityLiveRegion(View view, int mode);
+ int getPaddingStart(View view);
+ int getPaddingEnd(View view);
+ void setPaddingRelative(View view, int start, int top, int end, int bottom);
+ void dispatchStartTemporaryDetach(View view);
+ void dispatchFinishTemporaryDetach(View view);
+ float getX(View view);
+ float getY(View view);
+ float getRotation(View view);
+ float getRotationX(View view);
+ float getRotationY(View view);
+ float getScaleX(View view);
+ float getScaleY(View view);
+ float getTranslationX(View view);
+ float getTranslationY(View view);
+ int getMinimumWidth(View view);
+ int getMinimumHeight(View view);
+ ViewPropertyAnimatorCompat animate(View view);
+ void setRotation(View view, float value);
+ void setRotationX(View view, float value);
+ void setRotationY(View view, float value);
+ void setScaleX(View view, float value);
+ void setScaleY(View view, float value);
+ void setTranslationX(View view, float value);
+ void setTranslationY(View view, float value);
+ void setX(View view, float value);
+ void setY(View view, float value);
+ void setAlpha(View view, float value);
+ void setPivotX(View view, float value);
+ void setPivotY(View view, float value);
+ float getPivotX(View view);
+ float getPivotY(View view);
+ void setElevation(View view, float elevation);
+ float getElevation(View view);
+ void setTranslationZ(View view, float translationZ);
+ float getTranslationZ(View view);
+ void setClipBounds(View view, Rect clipBounds);
+ Rect getClipBounds(View view);
+ void setTransitionName(View view, String transitionName);
+ String getTransitionName(View view);
+ int getWindowSystemUiVisibility(View view);
+ void requestApplyInsets(View view);
+ void setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled);
+ boolean getFitsSystemWindows(View view);
+ boolean hasOverlappingRendering(View view);
void setFitsSystemWindows(View view, boolean fitSystemWindows);
void jumpDrawablesToCurrentState(View v);
void setOnApplyWindowInsetsListener(View view, OnApplyWindowInsetsListener listener);
@@ -450,12 +465,12 @@
boolean dispatchNestedPreFling(View view, float velocityX, float velocityY);
boolean isLaidOut(View view);
int combineMeasuredStates(int curState, int newState);
- public float getZ(View view);
- public boolean isAttachedToWindow(View view);
- public boolean hasOnClickListeners(View view);
- public void setScrollIndicators(View view, int indicators);
- public void setScrollIndicators(View view, int indicators, int mask);
- public int getScrollIndicators(View view);
+ float getZ(View view);
+ boolean isAttachedToWindow(View view);
+ boolean hasOnClickListeners(View view);
+ void setScrollIndicators(View view, int indicators);
+ void setScrollIndicators(View view, int indicators, int mask);
+ int getScrollIndicators(View view);
}
static class BaseViewCompatImpl implements ViewCompatImpl {
@@ -1297,8 +1312,7 @@
@Override
public ViewPropertyAnimatorCompat animate(View view) {
if (mViewPropertyAnimatorCompatMap == null) {
- mViewPropertyAnimatorCompatMap =
- new WeakHashMap<View, ViewPropertyAnimatorCompat>();
+ mViewPropertyAnimatorCompatMap = new WeakHashMap<>();
}
ViewPropertyAnimatorCompat vpa = mViewPropertyAnimatorCompatMap.get(view);
if (vpa == null) {
@@ -1706,6 +1720,7 @@
*/
@OverScroll
public static int getOverScrollMode(View v) {
+ //noinspection ResourceType
return IMPL.getOverScrollMode(v);
}
@@ -1742,10 +1757,9 @@
* event.getText().add(selectedDateUtterance);
* }</pre>
* <p>
- * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
- * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
- * {@link android.view.View.AccessibilityDelegate#onPopulateAccessibilityEvent(View,
- * AccessibilityEvent)}
+ * If an {@link AccessibilityDelegateCompat} has been specified via calling
+ * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its
+ * {@link AccessibilityDelegateCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)}
* is responsible for handling this call.
* </p>
* <p class="note"><strong>Note:</strong> Always call the super implementation before adding
@@ -1775,15 +1789,10 @@
* event.setPassword(true);
* }</pre>
* <p>
- * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
- * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
- * {@link android.view.View.AccessibilityDelegate#onInitializeAccessibilityEvent(View,
- * AccessibilityEvent)}
+ * If an {@link AccessibilityDelegateCompat} has been specified via calling
+ * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)}, its
+ * {@link AccessibilityDelegateCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)}
* is responsible for handling this call.
- * </p>
- * <p class="note"><strong>Note:</strong> Always call the super implementation before adding
- * information to the event, in case the default implementation has basic information to add.
- * </p>
*
* @param v The View against which to invoke the method.
* @param event The event to initialize.
@@ -1796,33 +1805,27 @@
}
/**
- * Initializes an {@link android.view.accessibility.AccessibilityNodeInfo} with information
+ * Initializes an {@link AccessibilityNodeInfoCompat} with information
* about this view. The base implementation sets:
* <ul>
- * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setParent(View)},</li>
- * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInParent(Rect)},</li>
- * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInScreen(Rect)},</li>
- * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setPackageName(CharSequence)},</li>
- * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setClassName(CharSequence)},</li>
- * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setContentDescription(CharSequence)},</li>
- * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setEnabled(boolean)},</li>
- * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setClickable(boolean)},</li>
- * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setFocusable(boolean)},</li>
- * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setFocused(boolean)},</li>
- * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setLongClickable(boolean)},</li>
- * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setSelected(boolean)},</li>
+ * <li>{@link AccessibilityNodeInfoCompat#setParent(View)},</li>
+ * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent(Rect)},</li>
+ * <li>{@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)},</li>
+ * <li>{@link AccessibilityNodeInfoCompat#setPackageName(CharSequence)},</li>
+ * <li>{@link AccessibilityNodeInfoCompat#setClassName(CharSequence)},</li>
+ * <li>{@link AccessibilityNodeInfoCompat#setContentDescription(CharSequence)},</li>
+ * <li>{@link AccessibilityNodeInfoCompat#setEnabled(boolean)},</li>
+ * <li>{@link AccessibilityNodeInfoCompat#setClickable(boolean)},</li>
+ * <li>{@link AccessibilityNodeInfoCompat#setFocusable(boolean)},</li>
+ * <li>{@link AccessibilityNodeInfoCompat#setFocused(boolean)},</li>
+ * <li>{@link AccessibilityNodeInfoCompat#setLongClickable(boolean)},</li>
+ * <li>{@link AccessibilityNodeInfoCompat#setSelected(boolean)},</li>
* </ul>
* <p>
- * Subclasses should override this method, call the super implementation,
- * and set additional attributes.
- * </p>
- * <p>
- * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
- * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
- * {@link android.view.View.AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View,
- * android.view.accessibility.AccessibilityNodeInfo)}
- * is responsible for handling this call.
- * </p>
+ * If an {@link AccessibilityDelegateCompat} has been specified via calling
+ * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)}, its
+ * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)}
+ * method is responsible for handling this call.
*
* @param v The View against which to invoke the method.
* @param info The instance to initialize.
@@ -1832,15 +1835,27 @@
}
/**
- * Sets a delegate for implementing accessibility support via compositon as
- * opposed to inheritance. The delegate's primary use is for implementing
- * backwards compatible widgets. For more details see
- * {@link android.view.View.AccessibilityDelegate}.
+ * Sets a delegate for implementing accessibility support via composition
+ * (as opposed to inheritance). For more details, see
+ * {@link AccessibilityDelegateCompat}.
+ * <p>
+ * On platform versions prior to API 14, this method is a no-op.
+ * <p>
+ * <strong>Note:</strong> On platform versions prior to
+ * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on
+ * views in the {@code android.widget.*} package are called <i>before</i>
+ * host methods. This prevents certain properties such as class name from
+ * being modified by overriding
+ * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)},
+ * as any changes will be overwritten by the host class.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate
+ * methods are called <i>after</i> host methods, which all properties to be
+ * modified without being overwritten by the host class.
*
- * @param v The View against which to invoke the method.
- * @param delegate The delegate instance.
- *
- * @see android.view.View.AccessibilityDelegate
+ * @param delegate the object to which accessibility method calls should be
+ * delegated
+ * @see AccessibilityDelegateCompat
*/
public static void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) {
IMPL.setAccessibilityDelegate(v, delegate);
@@ -1956,6 +1971,7 @@
*/
@ImportantForAccessibility
public static int getImportantForAccessibility(View view) {
+ //noinspection ResourceType
return IMPL.getImportantForAccessibility(view);
}
@@ -2063,13 +2079,6 @@
* {@link #LAYER_TYPE_SOFTWARE software} and {@link #LAYER_TYPE_HARDWARE hardware}
* for more information on when and how to use layers.</p>
*
- * @param layerType The ype of layer to use with this view, must be one of
- * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
- * {@link #LAYER_TYPE_HARDWARE}
- * @param paint The paint used to compose the layer. This argument is optional
- * and can be null. It is ignored when the layer type is
- * {@link #LAYER_TYPE_NONE}
- *
* @param view View to set the layer type for
* @param layerType The type of layer to use with this view, must be one of
* {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
@@ -2100,6 +2109,7 @@
*/
@LayerType
public static int getLayerType(View view) {
+ //noinspection ResourceType
return IMPL.getLayerType(view);
}
@@ -2171,6 +2181,7 @@
*/
@ResolvedLayoutDirectionMode
public static int getLayoutDirection(View view) {
+ //noinspection ResourceType
return IMPL.getLayoutDirection(view);
}
@@ -2297,6 +2308,7 @@
*/
@AccessibilityLiveRegion
public static int getAccessibilityLiveRegion(View view) {
+ //noinspection ResourceType
return IMPL.getAccessibilityLiveRegion(view);
}
diff --git a/v4/java/android/support/v4/view/ViewParentCompat.java b/v4/java/android/support/v4/view/ViewParentCompat.java
index fb107fa..6b9c6f0 100644
--- a/v4/java/android/support/v4/view/ViewParentCompat.java
+++ b/v4/java/android/support/v4/view/ViewParentCompat.java
@@ -224,6 +224,13 @@
}
}
+ static class ViewParentCompatApi24Impl extends ViewParentCompatLollipopImpl {
+ @Override
+ public int findDependentLayoutAxes(ViewParent parent, View child, int axisFilter) {
+ return ViewParentCompatApi24.findDependentLayoutAxes(parent, child, axisFilter);
+ }
+ }
+
static final ViewParentCompatImpl IMPL;
static {
final int version = Build.VERSION.SDK_INT;
diff --git a/v4/java/android/support/v4/widget/ExploreByTouchHelper.java b/v4/java/android/support/v4/widget/ExploreByTouchHelper.java
index 64f6634..ddfc5d6 100644
--- a/v4/java/android/support/v4/widget/ExploreByTouchHelper.java
+++ b/v4/java/android/support/v4/widget/ExploreByTouchHelper.java
@@ -19,7 +19,11 @@
import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.util.SparseArrayCompat;
import android.support.v4.view.AccessibilityDelegateCompat;
+import android.support.v4.view.KeyEventCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewParentCompat;
@@ -28,15 +32,22 @@
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
-import java.util.LinkedList;
+import java.util.ArrayList;
import java.util.List;
+import android.support.v4.view.ViewCompat.FocusDirection;
+import android.support.v4.view.ViewCompat.FocusRealDirection;
+
+import static android.support.v4.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+import static android.support.v4.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES;
+
/**
* ExploreByTouchHelper is a utility class for implementing accessibility
* support in custom {@link View}s that represent a collection of View-like
@@ -47,8 +58,36 @@
* <p>
* Clients should override abstract methods on this class and attach it to the
* host view using {@link ViewCompat#setAccessibilityDelegate}:
- *
+ * <p>
* <pre>
+ * class MyCustomView extends View {
+ * private MyVirtualViewHelper mVirtualViewHelper;
+ *
+ * public MyCustomView(Context context, ...) {
+ * ...
+ * mVirtualViewHelper = new MyVirtualViewHelper(this);
+ * ViewCompat.setAccessibilityDelegate(this, mVirtualViewHelper);
+ * }
+ *
+ * @Override
+ * public boolean dispatchHoverEvent(MotionEvent event) {
+ * return mHelper.dispatchHoverEvent(this, event)
+ * || super.dispatchHoverEvent(event);
+ * }
+ *
+ * @Override
+ * public boolean dispatchKeyEvent(KeyEvent event) {
+ * return mHelper.dispatchKeyEvent(event)
+ * || super.dispatchKeyEvent(event);
+ * }
+ *
+ * @Override
+ * public boolean onFocusChanged(boolean gainFocus, int direction,
+ * Rect previouslyFocusedRect) {
+ * super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ * mHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ * }
+ * }
* mAccessHelper = new MyExploreByTouchHelper(someView);
* ViewCompat.setAccessibilityDelegate(someView, mAccessHelper);
* </pre>
@@ -61,7 +100,7 @@
public static final int HOST_ID = View.NO_ID;
/** Default class name used for virtual views. */
- private static final String DEFAULT_CLASS_NAME = View.class.getName();
+ private static final String DEFAULT_CLASS_NAME = "android.view.View";
// Temporary, reusable data structures.
private final Rect mTempScreenRect = new Rect();
@@ -69,70 +108,79 @@
private final Rect mTempVisibleRect = new Rect();
private final int[] mTempGlobalRect = new int[2];
+ /** Cache of accessibility nodes. This is populated on-demand. */
+ private final SparseArrayCompat<AccessibilityNodeInfoCompat> mCachedNodes =
+ new SparseArrayCompat<>();
+
/** System accessibility manager, used to check state and send events. */
private final AccessibilityManager mManager;
/** View whose internal structure is exposed through this helper. */
- private final View mView;
+ private final View mHost;
- /** Node provider that handles creating nodes and performing actions. */
- private ExploreByTouchNodeProvider mNodeProvider;
+ /** Virtual node provider used to expose logical structure to services. */
+ private MyNodeProvider mNodeProvider;
- /** Virtual view id for the currently focused logical item. */
- private int mFocusedVirtualViewId = INVALID_ID;
+ /** Identifier for the virtual view that holds accessibility focus. */
+ private int mAccessibilityFocusedVirtualViewId = INVALID_ID;
- /** Virtual view id for the currently hovered logical item. */
+ /** Identifier for the virtual view that holds keyboard focus. */
+ private int mKeyboardFocusedVirtualViewId = INVALID_ID;
+
+ /** Identifier for the virtual view that is currently hovered. */
private int mHoveredVirtualViewId = INVALID_ID;
/**
- * Factory method to create a new {@link ExploreByTouchHelper}.
+ * Constructs a new helper that can expose a virtual view hierarchy for the
+ * specified host view.
*
- * @param forView View whose logical children are exposed by this helper.
+ * @param host view whose virtual view hierarchy is exposed by this helper
*/
- public ExploreByTouchHelper(View forView) {
- if (forView == null) {
+ public ExploreByTouchHelper(View host) {
+ if (host == null) {
throw new IllegalArgumentException("View may not be null");
}
- mView = forView;
- final Context context = forView.getContext();
+ mHost = host;
+
+ final Context context = host.getContext();
mManager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+
+ // Host view must be focusable so that we can delegate to virtual
+ // views.
+ host.setFocusable(true);
+ if (ViewCompat.getImportantForAccessibility(host) == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ ViewCompat.setImportantForAccessibility(host, IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
}
- /**
- * Returns the {@link AccessibilityNodeProviderCompat} for this helper.
- *
- * @param host View whose logical children are exposed by this helper.
- * @return The accessibility node provider for this helper.
- */
@Override
public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
if (mNodeProvider == null) {
- mNodeProvider = new ExploreByTouchNodeProvider();
+ mNodeProvider = new MyNodeProvider();
}
return mNodeProvider;
}
/**
+ * Delegates hover events from the host view.
+ * <p>
* Dispatches hover {@link MotionEvent}s to the virtual view hierarchy when
* the Explore by Touch feature is enabled.
* <p>
- * This method should be called by overriding
- * {@link View#dispatchHoverEvent}:
- *
+ * This method should be called by overriding the host view's
+ * {@link View#dispatchHoverEvent(MotionEvent)} method:
* <pre>@Override
* public boolean dispatchHoverEvent(MotionEvent event) {
- * if (mHelper.dispatchHoverEvent(this, event) {
- * return true;
- * }
- * return super.dispatchHoverEvent(event);
+ * return mHelper.dispatchHoverEvent(this, event)
+ * || super.dispatchHoverEvent(event);
* }
* </pre>
*
* @param event The hover event to dispatch to the virtual view hierarchy.
* @return Whether the hover event was handled.
*/
- public boolean dispatchHoverEvent(MotionEvent event) {
+ public final boolean dispatchHoverEvent(@NonNull MotionEvent event) {
if (!mManager.isEnabled()
|| !AccessibilityManagerCompat.isTouchExplorationEnabled(mManager)) {
return false;
@@ -145,7 +193,7 @@
updateHoveredVirtualView(virtualViewId);
return (virtualViewId != INVALID_ID);
case MotionEventCompat.ACTION_HOVER_EXIT:
- if (mFocusedVirtualViewId != INVALID_ID) {
+ if (mAccessibilityFocusedVirtualViewId != INVALID_ID) {
updateHoveredVirtualView(INVALID_ID);
return true;
}
@@ -156,78 +204,412 @@
}
/**
+ * Delegates key events from the host view.
+ * <p>
+ * This method should be called by overriding the host view's
+ * {@link View#dispatchKeyEvent(KeyEvent)} method:
+ * <pre>@Override
+ * public boolean dispatchKeyEvent(KeyEvent event) {
+ * return mHelper.dispatchKeyEvent(event)
+ * || super.dispatchKeyEvent(event);
+ * }
+ * </pre>
+ */
+ public final boolean dispatchKeyEvent(@NonNull KeyEvent event) {
+ boolean handled = false;
+
+ final int action = event.getAction();
+ if (action != KeyEvent.ACTION_UP) {
+ final int keyCode = event.getKeyCode();
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (KeyEventCompat.hasNoModifiers(event)) {
+ final int direction = keyToDirection(keyCode);
+ final int count = 1 + event.getRepeatCount();
+ for (int i = 0; i < count; i++) {
+ if (moveFocus(direction, null)) {
+ handled = true;
+ } else {
+ break;
+ }
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ if (KeyEventCompat.hasNoModifiers(event)) {
+ if (event.getRepeatCount() == 0) {
+ clickKeyboardFocusedVirtualView();
+ handled = true;
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_TAB:
+ if (KeyEventCompat.hasNoModifiers(event)) {
+ handled = moveFocus(View.FOCUS_FORWARD, null);
+ } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) {
+ handled = moveFocus(View.FOCUS_BACKWARD, null);
+ }
+ break;
+ }
+ }
+
+ return handled;
+ }
+
+ /**
+ * Delegates focus changes from the host view.
+ * <p>
+ * This method should be called by overriding the host view's
+ * {@link View#onFocusChanged(boolean, int, Rect)} method:
+ * <pre>@Override
+ * public boolean onFocusChanged(boolean gainFocus, int direction,
+ * Rect previouslyFocusedRect) {
+ * super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ * mHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ * }
+ * </pre>
+ */
+ public final void onFocusChanged(boolean gainFocus, int direction,
+ @Nullable Rect previouslyFocusedRect) {
+ if (mKeyboardFocusedVirtualViewId != INVALID_ID) {
+ clearKeyboardFocusForVirtualView(mKeyboardFocusedVirtualViewId);
+ }
+
+ if (gainFocus) {
+ moveFocus(direction, previouslyFocusedRect);
+ }
+ }
+
+ /**
+ * @return the identifier of the virtual view that has accessibility focus
+ * or {@link #INVALID_ID} if no virtual view has accessibility
+ * focus
+ */
+ public final int getAccessibilityFocusedVirtualViewId() {
+ return mAccessibilityFocusedVirtualViewId;
+ }
+
+ /**
+ * @return the identifier of the virtual view that has keyboard focus
+ * or {@link #INVALID_ID} if no virtual view has keyboard focus
+ */
+ public final int getKeyboardFocusedVirtualViewId() {
+ return mKeyboardFocusedVirtualViewId;
+ }
+
+ /**
+ * Maps key event codes to focus directions.
+ *
+ * @param keyCode the key event code
+ * @return the corresponding focus direction
+ */
+ @FocusRealDirection
+ private static int keyToDirection(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ return View.FOCUS_LEFT;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ return View.FOCUS_UP;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ return View.FOCUS_RIGHT;
+ default:
+ return View.FOCUS_DOWN;
+ }
+ }
+
+ /**
+ * Obtains the bounds for the specified virtual view.
+ *
+ * @param virtualViewId the identifier of the virtual view
+ * @param outBounds the rect to populate with virtual view bounds
+ */
+ private void getBoundsInParent(int virtualViewId, Rect outBounds) {
+ final AccessibilityNodeInfoCompat node = obtainAccessibilityNodeInfo(virtualViewId);
+ node.getBoundsInParent(outBounds);
+ }
+
+ /**
+ * Adapts AccessibilityNodeInfoCompat for obtaining bounds.
+ */
+ private static final FocusStrategy.BoundsAdapter<AccessibilityNodeInfoCompat> NODE_ADAPTER =
+ new FocusStrategy.BoundsAdapter<AccessibilityNodeInfoCompat>() {
+ @Override
+ public void obtainBounds(AccessibilityNodeInfoCompat node, Rect outBounds) {
+ node.getBoundsInParent(outBounds);
+ }
+ };
+
+ /**
+ * Adapts SparseArrayCompat for iterating through values.
+ */
+ private static final FocusStrategy.CollectionAdapter<SparseArrayCompat<
+ AccessibilityNodeInfoCompat>, AccessibilityNodeInfoCompat> SPARSE_VALUES_ADAPTER =
+ new FocusStrategy.CollectionAdapter<SparseArrayCompat<
+ AccessibilityNodeInfoCompat>, AccessibilityNodeInfoCompat>() {
+ @Override
+ public AccessibilityNodeInfoCompat get(
+ SparseArrayCompat<AccessibilityNodeInfoCompat> collection, int index) {
+ return collection.valueAt(index);
+ }
+
+ @Override
+ public int size(SparseArrayCompat<AccessibilityNodeInfoCompat> collection) {
+ return collection.size();
+ }
+ };
+
+ /**
+ * Attempts to move keyboard focus in the specified direction.
+ *
+ * @param direction the direction in which to move keyboard focus
+ * @param previouslyFocusedRect the bounds of the previously focused item,
+ * or {@code null} if not available
+ * @return {@code true} if keyboard focus moved to a virtual view managed
+ * by this helper, or {@code false} otherwise
+ */
+ private boolean moveFocus(@FocusDirection int direction, @Nullable Rect previouslyFocusedRect) {
+ final int focusedNodeId = mKeyboardFocusedVirtualViewId;
+ final AccessibilityNodeInfoCompat focusedNode =
+ focusedNodeId == INVALID_ID ? null : mCachedNodes.get(focusedNodeId);
+
+ final AccessibilityNodeInfoCompat nextFocusedNode;
+ switch (direction) {
+ case View.FOCUS_FORWARD:
+ case View.FOCUS_BACKWARD:
+ final boolean isLayoutRtl =
+ ViewCompat.getLayoutDirection(mHost) == ViewCompat.LAYOUT_DIRECTION_RTL;
+ nextFocusedNode = FocusStrategy.findNextFocusInRelativeDirection(mCachedNodes,
+ SPARSE_VALUES_ADAPTER, NODE_ADAPTER, focusedNode, direction, isLayoutRtl,
+ false);
+ break;
+ case View.FOCUS_LEFT:
+ case View.FOCUS_UP:
+ case View.FOCUS_RIGHT:
+ case View.FOCUS_DOWN:
+ final Rect selectedRect = new Rect();
+ if (mKeyboardFocusedVirtualViewId != INVALID_ID) {
+ // Focus is moving from a virtual view within the host.
+ getBoundsInParent(mKeyboardFocusedVirtualViewId, selectedRect);
+ } else if (previouslyFocusedRect != null) {
+ // Focus is moving from a real view outside the host.
+ selectedRect.set(previouslyFocusedRect);
+ } else {
+ // Focus is moving from... somewhere? Make a guess.
+ // Usually this happens when another view was too lazy
+ // to pass the previously focused rect (ex. ScrollView
+ // when moving UP or DOWN).
+ guessPreviouslyFocusedRect(mHost, direction, selectedRect);
+ }
+ nextFocusedNode = FocusStrategy.findNextFocusInAbsoluteDirection(mCachedNodes,
+ SPARSE_VALUES_ADAPTER, NODE_ADAPTER, focusedNode, selectedRect, direction);
+ break;
+ default:
+ throw new IllegalArgumentException("direction must be one of "
+ + "{FOCUS_FORWARD, FOCUS_BACKWARD, FOCUS_UP, FOCUS_DOWN, "
+ + "FOCUS_LEFT, FOCUS_RIGHT}.");
+ }
+
+ final int nextFocusedNodeId;
+ if (nextFocusedNode == null) {
+ nextFocusedNodeId = INVALID_ID;
+ } else {
+ final int index = mCachedNodes.indexOfValue(nextFocusedNode);
+ nextFocusedNodeId = mCachedNodes.keyAt(index);
+ }
+
+ return requestKeyboardFocusForVirtualView(nextFocusedNodeId);
+ }
+
+ /**
+ * Obtains a best guess for the previously focused rect for keyboard focus
+ * moving in the specified direction.
+ *
+ * @param host the view into which focus is moving
+ * @param direction the absolute direction in which focus is moving
+ * @param outBounds the rect to populate with the best-guess bounds for the
+ * previous focus rect
+ */
+ private static Rect guessPreviouslyFocusedRect(@NonNull View host,
+ @FocusRealDirection int direction, @NonNull Rect outBounds) {
+ final int w = host.getWidth();
+ final int h = host.getHeight();
+
+ switch (direction) {
+ case View.FOCUS_LEFT:
+ outBounds.set(w, 0, w, h);
+ break;
+ case View.FOCUS_UP:
+ outBounds.set(0, h, w, h);
+ break;
+ case View.FOCUS_RIGHT:
+ outBounds.set(-1, 0, -1, h);
+ break;
+ case View.FOCUS_DOWN:
+ outBounds.set(0, -1, w, -1);
+ break;
+ default:
+ throw new IllegalArgumentException("direction must be one of "
+ + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+ }
+
+ return outBounds;
+ }
+
+ /**
+ * Performs a click action on the keyboard focused virtual view, if any.
+ *
+ * @return {@code true} if the click action was performed successfully or
+ * {@code false} otherwise
+ */
+ private boolean clickKeyboardFocusedVirtualView() {
+ return mKeyboardFocusedVirtualViewId != INVALID_ID && onPerformActionForVirtualView(
+ mKeyboardFocusedVirtualViewId, AccessibilityNodeInfoCompat.ACTION_CLICK, null);
+ }
+
+ /**
* Populates an event of the specified type with information about an item
* and attempts to send it up through the view hierarchy.
* <p>
* You should call this method after performing a user action that normally
* fires an accessibility event, such as clicking on an item.
- *
+ * <p>
* <pre>public void performItemClick(T item) {
* ...
* sendEventForVirtualViewId(item.id, AccessibilityEvent.TYPE_VIEW_CLICKED);
* }
* </pre>
*
- * @param virtualViewId The virtual view id for which to send an event.
- * @param eventType The type of event to send.
- * @return true if the event was sent successfully.
+ * @param virtualViewId the identifier of the virtual view for which to
+ * send an event
+ * @param eventType the type of event to send
+ * @return {@code true} if the event was sent successfully, {@code false}
+ * otherwise
*/
- public boolean sendEventForVirtualView(int virtualViewId, int eventType) {
+ public final boolean sendEventForVirtualView(int virtualViewId, int eventType) {
if ((virtualViewId == INVALID_ID) || !mManager.isEnabled()) {
return false;
}
- final ViewParent parent = mView.getParent();
+ final ViewParent parent = mHost.getParent();
if (parent == null) {
return false;
}
final AccessibilityEvent event = createEvent(virtualViewId, eventType);
- return ViewParentCompat.requestSendAccessibilityEvent(parent, mView, event);
+ return ViewParentCompat.requestSendAccessibilityEvent(parent, mHost, event);
}
/**
* Notifies the accessibility framework that the properties of the parent
* view have changed.
* <p>
- * You <b>must</b> call this method after adding or removing items from the
- * parent view.
+ * You <strong>must</strong> call this method after adding or removing
+ * items from the parent view.
*/
- public void invalidateRoot() {
- invalidateVirtualView(HOST_ID);
+ public final void invalidateRoot() {
+ invalidateVirtualView(HOST_ID, AccessibilityEventCompat.CONTENT_CHANGE_TYPE_SUBTREE);
}
/**
* Notifies the accessibility framework that the properties of a particular
* item have changed.
* <p>
- * You <b>must</b> call this method after changing any of the properties set
- * in {@link #onPopulateNodeForVirtualView}.
+ * You <strong>must</strong> call this method after changing any of the
+ * properties set in
+ * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}.
*
- * @param virtualViewId The virtual view id to invalidate.
+ * @param virtualViewId the virtual view id to invalidate, or
+ * {@link #HOST_ID} to invalidate the root view
+ * @see #invalidateVirtualView(int, int)
*/
- public void invalidateVirtualView(int virtualViewId) {
- sendEventForVirtualView(
- virtualViewId, AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
+ public final void invalidateVirtualView(int virtualViewId) {
+ invalidateVirtualView(virtualViewId,
+ AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED);
}
/**
- * Returns the virtual view id for the currently focused item,
+ * Notifies the accessibility framework that the properties of a particular
+ * item have changed.
+ * <p>
+ * You <strong>must</strong> call this method after changing any of the
+ * properties set in
+ * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}.
*
- * @return A virtual view id, or {@link #INVALID_ID} if no item is
- * currently focused.
+ * @param virtualViewId the virtual view id to invalidate, or
+ * {@link #HOST_ID} to invalidate the root view
+ * @param changeTypes the bit mask of change types. May be {@code 0} for the
+ * default (undefined) change type or one or more of:
+ * <ul>
+ * <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
+ * <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_SUBTREE}
+ * <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_TEXT}
+ * <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_UNDEFINED}
+ * </ul>
*/
+ public final void invalidateVirtualView(int virtualViewId, int changeTypes) {
+ final SparseArrayCompat<AccessibilityNodeInfoCompat> cachedNodes = mCachedNodes;
+ if (virtualViewId == HOST_ID
+ && (changeTypes & AccessibilityEventCompat.CONTENT_CHANGE_TYPE_SUBTREE) != 0) {
+ for (int i = 0, count = cachedNodes.size(); i < count; i++) {
+ cachedNodes.valueAt(i).recycle();
+ }
+ cachedNodes.clear();
+ } else {
+ final int index = cachedNodes.indexOfKey(virtualViewId);
+ if (index >= 0) {
+ cachedNodes.valueAt(index).recycle();
+ cachedNodes.removeAt(index);
+ }
+ }
+
+ if (virtualViewId != INVALID_ID && mManager.isEnabled()) {
+ final ViewParent parent = mHost.getParent();
+ if (parent != null) {
+ // Send events up the hierarchy so they can be coalesced.
+ final AccessibilityEvent event = createEvent(virtualViewId,
+ AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
+ AccessibilityEventCompat.setContentChangeTypes(event, changeTypes);
+ ViewParentCompat.requestSendAccessibilityEvent(parent, mHost, event);
+ }
+ }
+ }
+
+ /**
+ * Returns the virtual view ID for the currently accessibility focused
+ * item.
+ *
+ * @return the identifier of the virtual view that has accessibility focus
+ * or {@link #INVALID_ID} if no virtual view has accessibility
+ * focus
+ * @deprecated Use {@link #getAccessibilityFocusedVirtualViewId()}.
+ */
+ @Deprecated
public int getFocusedVirtualView() {
- return mFocusedVirtualViewId;
+ return getAccessibilityFocusedVirtualViewId();
+ }
+
+ /**
+ * Called when the focus state of a virtual view changes.
+ *
+ * @param virtualViewId the virtual view identifier
+ * @param hasFocus {@code true} if the view has focus, {@code false}
+ * otherwise
+ */
+ protected void onVirtualViewKeyboardFocusChanged(int virtualViewId, boolean hasFocus) {
+ // Stub method.
}
/**
* Sets the currently hovered item, sending hover accessibility events as
* necessary to maintain the correct state.
*
- * @param virtualViewId The virtual view id for the item currently being
- * hovered, or {@link #INVALID_ID} if no item is hovered within
- * the parent view.
+ * @param virtualViewId the virtual view id for the item currently being
+ * hovered, or {@link #INVALID_ID} if no item is
+ * hovered within the parent view
*/
private void updateHoveredVirtualView(int virtualViewId) {
if (mHoveredVirtualViewId == virtualViewId) {
@@ -248,11 +630,11 @@
* Constructs and returns an {@link AccessibilityEvent} for the specified
* virtual view id, which includes the host view ({@link #HOST_ID}).
*
- * @param virtualViewId The virtual view id for the item for which to
- * construct an event.
- * @param eventType The type of event to construct.
- * @return An {@link AccessibilityEvent} populated with information about
- * the specified item.
+ * @param virtualViewId the virtual view id for the item for which to
+ * construct an event
+ * @param eventType the type of event to construct
+ * @return an {@link AccessibilityEvent} populated with information about
+ * the specified item
*/
private AccessibilityEvent createEvent(int virtualViewId, int eventType) {
switch (virtualViewId) {
@@ -266,13 +648,17 @@
/**
* Constructs and returns an {@link AccessibilityEvent} for the host node.
*
- * @param eventType The type of event to construct.
- * @return An {@link AccessibilityEvent} populated with information about
- * the specified item.
+ * @param eventType the type of event to construct
+ * @return an {@link AccessibilityEvent} populated with information about
+ * the specified item
*/
private AccessibilityEvent createEventForHost(int eventType) {
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
- ViewCompat.onInitializeAccessibilityEvent(mView, event);
+ ViewCompat.onInitializeAccessibilityEvent(mHost, event);
+
+ // Allow the client to populate the event.
+ onPopulateEventForHost(event);
+
return event;
}
@@ -280,16 +666,24 @@
* Constructs and returns an {@link AccessibilityEvent} populated with
* information about the specified item.
*
- * @param virtualViewId The virtual view id for the item for which to
- * construct an event.
- * @param eventType The type of event to construct.
- * @return An {@link AccessibilityEvent} populated with information about
- * the specified item.
+ * @param virtualViewId the virtual view id for the item for which to
+ * construct an event
+ * @param eventType the type of event to construct
+ * @return an {@link AccessibilityEvent} populated with information about
+ * the specified item
*/
private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) {
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
- event.setEnabled(true);
- event.setClassName(DEFAULT_CLASS_NAME);
+ final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
+ final AccessibilityNodeInfoCompat node = obtainAccessibilityNodeInfo(virtualViewId);
+
+ // Allow the client to override these properties,
+ record.getText().add(node.getText());
+ record.setContentDescription(node.getContentDescription());
+ record.setScrollable(node.isScrollable());
+ record.setPassword(node.isPassword());
+ record.setEnabled(node.isEnabled());
+ record.setChecked(node.isChecked());
// Allow the client to populate the event.
onPopulateEventForVirtualView(virtualViewId, event);
@@ -301,52 +695,66 @@
}
// Don't allow the client to override these properties.
- event.setPackageName(mView.getContext().getPackageName());
-
- final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
- record.setSource(mView, virtualViewId);
+ record.setClassName(node.getClassName());
+ record.setSource(mHost, virtualViewId);
+ event.setPackageName(mHost.getContext().getPackageName());
return event;
}
/**
- * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
- * specified virtual view id, which includes the host view
- * ({@link #HOST_ID}).
+ * Obtains a populated {@link AccessibilityNodeInfoCompat} for the
+ * virtual view with the specified identifier.
+ * <p>
+ * This method may be called with identifier {@link #HOST_ID} to obtain a
+ * node for the host view.
*
- * @param virtualViewId The virtual view id for the item for which to
- * construct a node.
- * @return An {@link AccessibilityNodeInfoCompat} populated with information
- * about the specified item.
+ * @param virtualViewId the identifier of the virtual view for which to
+ * construct a node
+ * @return an {@link AccessibilityNodeInfoCompat} populated with information
+ * about the specified item
*/
- private AccessibilityNodeInfoCompat createNode(int virtualViewId) {
- switch (virtualViewId) {
- case HOST_ID:
- return createNodeForHost();
- default:
- return createNodeForChild(virtualViewId);
+ @NonNull
+ private AccessibilityNodeInfoCompat obtainAccessibilityNodeInfo(int virtualViewId) {
+ final AccessibilityNodeInfoCompat node;
+ final int cacheIndex = mCachedNodes.indexOfKey(virtualViewId);
+ if (cacheIndex >= 0) {
+ node = mCachedNodes.valueAt(cacheIndex);
+ } else if (virtualViewId == HOST_ID) {
+ node = createNodeForHost();
+ } else {
+ node = createNodeForChild(virtualViewId);
}
+
+ mCachedNodes.put(virtualViewId, node);
+
+ return node;
}
/**
* Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
* host view populated with its virtual descendants.
*
- * @return An {@link AccessibilityNodeInfoCompat} for the parent node.
+ * @return an {@link AccessibilityNodeInfoCompat} for the parent node
*/
+ @NonNull
private AccessibilityNodeInfoCompat createNodeForHost() {
- final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain(mView);
- ViewCompat.onInitializeAccessibilityNodeInfo(mView, node);
+ final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain(mHost);
+ ViewCompat.onInitializeAccessibilityNodeInfo(mHost, node);
+ final int realNodeCount = node.getChildCount();
// Allow the client to populate the host node.
onPopulateNodeForHost(node);
// Add the virtual descendants.
- final LinkedList<Integer> virtualViewIds = new LinkedList<Integer>();
+ final ArrayList<Integer> virtualViewIds = new ArrayList<>();
getVisibleVirtualViews(virtualViewIds);
+ if (realNodeCount > 0 && virtualViewIds.size() > 0) {
+ throw new RuntimeException("Views cannot have both real and virtual children");
+ }
- for (Integer childVirtualViewId : virtualViewIds) {
- node.addChild(mView, childVirtualViewId);
+ for (int i = 0, count = virtualViewIds.size(); i < count; i++) {
+ node.addChild(mHost, virtualViewIds.get(i));
}
return node;
@@ -376,15 +784,17 @@
* <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent}
* </ul>
*
- * @param virtualViewId The virtual view id for item for which to construct
- * a node.
- * @return An {@link AccessibilityNodeInfoCompat} for the specified item.
+ * @param virtualViewId the virtual view id for item for which to construct
+ * a node
+ * @return an {@link AccessibilityNodeInfoCompat} for the specified item
*/
+ @NonNull
private AccessibilityNodeInfoCompat createNodeForChild(int virtualViewId) {
final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain();
// Ensure the client has good defaults.
node.setEnabled(true);
+ node.setFocusable(true);
node.setClassName(DEFAULT_CLASS_NAME);
// Allow the client to populate the node.
@@ -413,12 +823,12 @@
}
// Don't allow the client to override these properties.
- node.setPackageName(mView.getContext().getPackageName());
- node.setSource(mView, virtualViewId);
- node.setParent(mView);
+ node.setPackageName(mHost.getContext().getPackageName());
+ node.setSource(mHost, virtualViewId);
+ node.setParent(mHost);
// Manage internal accessibility focus state.
- if (mFocusedVirtualViewId == virtualViewId) {
+ if (mAccessibilityFocusedVirtualViewId == virtualViewId) {
node.setAccessibilityFocused(true);
node.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
} else {
@@ -426,6 +836,15 @@
node.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
}
+ // Manage internal keyboard focus state.
+ final boolean isFocused = mKeyboardFocusedVirtualViewId == virtualViewId;
+ if (isFocused) {
+ node.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS);
+ } else if (node.isFocusable()) {
+ node.addAction(AccessibilityNodeInfoCompat.ACTION_FOCUS);
+ }
+ node.setFocused(isFocused);
+
// Set the visibility based on the parent bound.
if (intersectVisibleToUser(mTempParentRect)) {
node.setVisibleToUser(true);
@@ -433,7 +852,7 @@
}
// Calculate screen-relative bound.
- mView.getLocationOnScreen(mTempGlobalRect);
+ mHost.getLocationOnScreen(mTempGlobalRect);
final int offsetX = mTempGlobalRect[0];
final int offsetY = mTempGlobalRect[1];
mTempScreenRect.set(mTempParentRect);
@@ -453,27 +872,21 @@
}
private boolean performActionForHost(int action, Bundle arguments) {
- return ViewCompat.performAccessibilityAction(mView, action, arguments);
+ return ViewCompat.performAccessibilityAction(mHost, action, arguments);
}
private boolean performActionForChild(int virtualViewId, int action, Bundle arguments) {
switch (action) {
case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS:
- case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
- return manageFocusForChild(virtualViewId, action, arguments);
- default:
- return onPerformActionForVirtualView(virtualViewId, action, arguments);
- }
- }
-
- private boolean manageFocusForChild(int virtualViewId, int action, Bundle arguments) {
- switch (action) {
- case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS:
return requestAccessibilityFocus(virtualViewId);
case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
return clearAccessibilityFocus(virtualViewId);
+ case AccessibilityNodeInfoCompat.ACTION_FOCUS:
+ return requestKeyboardFocusForVirtualView(virtualViewId);
+ case AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS:
+ return clearKeyboardFocusForVirtualView(virtualViewId);
default:
- return false;
+ return onPerformActionForVirtualView(virtualViewId, action, arguments);
}
}
@@ -482,8 +895,8 @@
* portion of its parent {@link View}. Modifies {@code localRect} to contain
* only the visible portion.
*
- * @param localRect A rectangle in local (parent) coordinates.
- * @return Whether the specified {@link Rect} is visible on the screen.
+ * @param localRect a rectangle in local (parent) coordinates
+ * @return whether the specified {@link Rect} is visible on the screen
*/
private boolean intersectVisibleToUser(Rect localRect) {
// Missing or empty bounds mean this view is not visible.
@@ -492,12 +905,12 @@
}
// Attached to invisible window means this view is not visible.
- if (mView.getWindowVisibility() != View.VISIBLE) {
+ if (mHost.getWindowVisibility() != View.VISIBLE) {
return false;
}
// An invisible predecessor means that this view is not visible.
- ViewParent viewParent = mView.getParent();
+ ViewParent viewParent = mHost.getParent();
while (viewParent instanceof View) {
final View view = (View) viewParent;
if ((ViewCompat.getAlpha(view) <= 0) || (view.getVisibility() != View.VISIBLE)) {
@@ -512,7 +925,7 @@
}
// If no portion of the parent is visible, this view is not visible.
- if (!mView.getLocalVisibleRect(mTempVisibleRect)) {
+ if (!mHost.getLocalVisibleRect(mTempVisibleRect)) {
return false;
}
@@ -521,15 +934,6 @@
}
/**
- * Returns whether this virtual view is accessibility focused.
- *
- * @return True if the view is accessibility focused.
- */
- private boolean isAccessibilityFocused(int virtualViewId) {
- return (mFocusedVirtualViewId == virtualViewId);
- }
-
- /**
* Attempts to give accessibility focus to a virtual view.
* <p>
* A virtual view will not actually take focus if
@@ -537,9 +941,9 @@
* {@link AccessibilityManager#isTouchExplorationEnabled()} returns false,
* or the view already has accessibility focus.
*
- * @param virtualViewId The id of the virtual view on which to place
- * accessibility focus.
- * @return Whether this virtual view actually took accessibility focus.
+ * @param virtualViewId the identifier of the virtual view on which to
+ * place accessibility focus
+ * @return whether this virtual view actually took accessibility focus
*/
private boolean requestAccessibilityFocus(int virtualViewId) {
if (!mManager.isEnabled()
@@ -547,18 +951,17 @@
return false;
}
// TODO: Check virtual view visibility.
- if (!isAccessibilityFocused(virtualViewId)) {
+ if (mAccessibilityFocusedVirtualViewId != virtualViewId) {
// Clear focus from the previously focused view, if applicable.
- if (mFocusedVirtualViewId != INVALID_ID) {
- sendEventForVirtualView(mFocusedVirtualViewId,
- AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+ if (mAccessibilityFocusedVirtualViewId != INVALID_ID) {
+ clearAccessibilityFocus(mAccessibilityFocusedVirtualViewId);
}
// Set focus on the new view.
- mFocusedVirtualViewId = virtualViewId;
+ mAccessibilityFocusedVirtualViewId = virtualViewId;
// TODO: Only invalidate virtual view bounds.
- mView.invalidate();
+ mHost.invalidate();
sendEventForVirtualView(virtualViewId,
AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
return true;
@@ -569,14 +972,14 @@
/**
* Attempts to clear accessibility focus from a virtual view.
*
- * @param virtualViewId The id of the virtual view from which to clear
- * accessibility focus.
- * @return Whether this virtual view actually cleared accessibility focus.
+ * @param virtualViewId the identifier of the virtual view from which to
+ * clear accessibility focus
+ * @return whether this virtual view actually cleared accessibility focus
*/
private boolean clearAccessibilityFocus(int virtualViewId) {
- if (isAccessibilityFocused(virtualViewId)) {
- mFocusedVirtualViewId = INVALID_ID;
- mView.invalidate();
+ if (mAccessibilityFocusedVirtualViewId == virtualViewId) {
+ mAccessibilityFocusedVirtualViewId = INVALID_ID;
+ mHost.invalidate();
sendEventForVirtualView(virtualViewId,
AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
return true;
@@ -585,6 +988,57 @@
}
/**
+ * Attempts to give keyboard focus to a virtual view.
+ *
+ * @param virtualViewId the identifier of the virtual view on which to
+ * place keyboard focus
+ * @return whether this virtual view actually took keyboard focus
+ */
+ public final boolean requestKeyboardFocusForVirtualView(int virtualViewId) {
+ if (!mHost.isFocused() && !mHost.requestFocus()) {
+ // Host must have real keyboard focus.
+ return false;
+ }
+
+ if (mKeyboardFocusedVirtualViewId == virtualViewId) {
+ // The virtual view already has focus.
+ return false;
+ }
+
+ if (mKeyboardFocusedVirtualViewId != INVALID_ID) {
+ clearKeyboardFocusForVirtualView(mKeyboardFocusedVirtualViewId);
+ }
+
+ mKeyboardFocusedVirtualViewId = virtualViewId;
+
+ onVirtualViewKeyboardFocusChanged(virtualViewId, true);
+ sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_FOCUSED);
+
+ return true;
+ }
+
+ /**
+ * Attempts to clear keyboard focus from a virtual view.
+ *
+ * @param virtualViewId the identifier of the virtual view from which to
+ * clear keyboard focus
+ * @return whether this virtual view actually cleared keyboard focus
+ */
+ public final boolean clearKeyboardFocusForVirtualView(int virtualViewId) {
+ if (mKeyboardFocusedVirtualViewId != virtualViewId) {
+ // The virtual view is not focused.
+ return false;
+ }
+
+ mKeyboardFocusedVirtualViewId = INVALID_ID;
+
+ onVirtualViewKeyboardFocusChanged(virtualViewId, false);
+ sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_FOCUSED);
+
+ return true;
+ }
+
+ /**
* Provides a mapping between view-relative coordinates and logical
* items.
*
@@ -609,22 +1063,25 @@
* Populates an {@link AccessibilityEvent} with information about the
* specified item.
* <p>
- * Implementations <b>must</b> populate the following required fields:
+ * The helper class automatically populates the following fields based on
+ * the values set by
+ * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)},
+ * but implementations may optionally override them:
* <ul>
- * <li>event text, see {@link AccessibilityEvent#getText} or
- * {@link AccessibilityEvent#setContentDescription}
- * </ul>
- * <p>
- * The helper class automatically populates the following fields with
- * default values, but implementations may optionally override them:
- * <ul>
- * <li>item class name, set to android.view.View, see
- * {@link AccessibilityEvent#setClassName}
+ * <li>event text, see {@link AccessibilityEvent#getText()}
+ * <li>content description, see
+ * {@link AccessibilityEvent#setContentDescription(CharSequence)}
+ * <li>scrollability, see {@link AccessibilityEvent#setScrollable(boolean)}
+ * <li>password state, see {@link AccessibilityEvent#setPassword(boolean)}
+ * <li>enabled state, see {@link AccessibilityEvent#setEnabled(boolean)}
+ * <li>checked state, see {@link AccessibilityEvent#setChecked(boolean)}
* </ul>
* <p>
* The following required fields are automatically populated by the
* helper class and may not be overridden:
* <ul>
+ * <li>item class name, set to the value used in
+ * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}
* <li>package name, set to the package of the host view's
* {@link Context}, see {@link AccessibilityEvent#setPackageName}
* <li>event source, set to the host view and virtual view identifier,
@@ -635,55 +1092,76 @@
* populate the event
* @param event The event to populate
*/
- protected abstract void onPopulateEventForVirtualView(
- int virtualViewId, AccessibilityEvent event);
+ protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
+ // Default implementation is no-op.
+ }
+
+ /**
+ * Populates an {@link AccessibilityEvent} with information about the host
+ * view.
+ * <p>
+ * The default implementation is a no-op.
+ *
+ * @param event the event to populate with information about the host view
+ */
+ protected void onPopulateEventForHost(AccessibilityEvent event) {
+ // Default implementation is no-op.
+ }
/**
* Populates an {@link AccessibilityNodeInfoCompat} with information
* about the specified item.
* <p>
- * Implementations <b>must</b> populate the following required fields:
+ * Implementations <strong>must</strong> populate the following required
+ * fields:
* <ul>
- * <li>event text, see {@link AccessibilityNodeInfoCompat#setText} or
- * {@link AccessibilityNodeInfoCompat#setContentDescription}
+ * <li>event text, see
+ * {@link AccessibilityNodeInfoCompat#setText(CharSequence)} or
+ * {@link AccessibilityNodeInfoCompat#setContentDescription(CharSequence)}
* <li>bounds in parent coordinates, see
- * {@link AccessibilityNodeInfoCompat#setBoundsInParent}
+ * {@link AccessibilityNodeInfoCompat#setBoundsInParent(Rect)}
* </ul>
* <p>
* The helper class automatically populates the following fields with
* default values, but implementations may optionally override them:
* <ul>
- * <li>enabled state, set to true, see
- * {@link AccessibilityNodeInfoCompat#setEnabled}
- * <li>item class name, identical to the class name set by
- * {@link #onPopulateEventForVirtualView}, see
- * {@link AccessibilityNodeInfoCompat#setClassName}
+ * <li>enabled state, set to {@code true}, see
+ * {@link AccessibilityNodeInfoCompat#setEnabled(boolean)}
+ * <li>keyboard focusability, set to {@code true}, see
+ * {@link AccessibilityNodeInfoCompat#setFocusable(boolean)}
+ * <li>item class name, set to {@code android.view.View}, see
+ * {@link AccessibilityNodeInfoCompat#setClassName(CharSequence)}
* </ul>
* <p>
* The following required fields are automatically populated by the
* helper class and may not be overridden:
* <ul>
* <li>package name, identical to the package name set by
- * {@link #onPopulateEventForVirtualView}, see
+ * {@link #onPopulateEventForVirtualView(int, AccessibilityEvent)}, see
* {@link AccessibilityNodeInfoCompat#setPackageName}
* <li>node source, identical to the event source set in
- * {@link #onPopulateEventForVirtualView}, see
+ * {@link #onPopulateEventForVirtualView(int, AccessibilityEvent)}, see
* {@link AccessibilityNodeInfoCompat#setSource(View, int)}
* <li>parent view, set to the host view, see
* {@link AccessibilityNodeInfoCompat#setParent(View)}
* <li>visibility, computed based on parent-relative bounds, see
- * {@link AccessibilityNodeInfoCompat#setVisibleToUser}
+ * {@link AccessibilityNodeInfoCompat#setVisibleToUser(boolean)}
* <li>accessibility focus, computed based on internal helper state, see
- * {@link AccessibilityNodeInfoCompat#setAccessibilityFocused}
+ * {@link AccessibilityNodeInfoCompat#setAccessibilityFocused(boolean)}
+ * <li>keyboard focus, computed based on internal helper state, see
+ * {@link AccessibilityNodeInfoCompat#setFocused(boolean)}
* <li>bounds in screen coordinates, computed based on host view bounds,
- * see {@link AccessibilityNodeInfoCompat#setBoundsInScreen}
+ * see {@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)}
* </ul>
* <p>
- * Additionally, the helper class automatically handles accessibility
- * focus management by adding the appropriate
- * {@link AccessibilityNodeInfoCompat#ACTION_ACCESSIBILITY_FOCUS} or
+ * Additionally, the helper class automatically handles keyboard focus and
+ * accessibility focus management by adding the appropriate
+ * {@link AccessibilityNodeInfoCompat#ACTION_FOCUS},
+ * {@link AccessibilityNodeInfoCompat#ACTION_CLEAR_FOCUS},
+ * {@link AccessibilityNodeInfoCompat#ACTION_ACCESSIBILITY_FOCUS}, or
* {@link AccessibilityNodeInfoCompat#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
- * action. Implementations must <b>never</b> manually add these actions.
+ * actions. Implementations must <strong>never</strong> manually add these
+ * actions.
* <p>
* The helper class also automatically modifies parent- and
* screen-relative bounds to reflect the portion of the item visible
@@ -700,10 +1178,11 @@
* Populates an {@link AccessibilityNodeInfoCompat} with information
* about the host view.
* <p>
- * The following required fields are automatically populated by the
- * helper class and may not be overridden:
+ * The default implementation is a no-op.
+ *
+ * @param node the node to populate with information about the host view
*/
- public void onPopulateNodeForHost(AccessibilityNodeInfoCompat node) {
+ protected void onPopulateNodeForHost(AccessibilityNodeInfoCompat node) {
// Default implementation is no-op.
}
@@ -713,8 +1192,9 @@
* {@link AccessibilityNodeInfoCompat#performAction(int, Bundle)} for
* more information.
* <p>
- * Implementations <b>must</b> handle any actions added manually in
- * {@link #onPopulateNodeForVirtualView}.
+ * Implementations <strong>must</strong> handle any actions added manually
+ * in
+ * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}.
* <p>
* The helper class automatically handles focus management resulting
* from {@link AccessibilityNodeInfoCompat#ACTION_ACCESSIBILITY_FOCUS}
@@ -733,13 +1213,16 @@
int virtualViewId, int action, Bundle arguments);
/**
- * Exposes a virtual view hierarchy to the accessibility framework. Only
- * used in API 16+.
+ * Exposes a virtual view hierarchy to the accessibility framework.
*/
- private class ExploreByTouchNodeProvider extends AccessibilityNodeProviderCompat {
+ private class MyNodeProvider extends AccessibilityNodeProviderCompat {
@Override
public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) {
- return ExploreByTouchHelper.this.createNode(virtualViewId);
+ // The caller takes ownership of the node and is expected to
+ // recycle it when done, so always return a copy.
+ final AccessibilityNodeInfoCompat node =
+ ExploreByTouchHelper.this.obtainAccessibilityNodeInfo(virtualViewId);
+ return AccessibilityNodeInfoCompat.obtain(node);
}
@Override
diff --git a/v4/java/android/support/v4/widget/FocusStrategy.java b/v4/java/android/support/v4/widget/FocusStrategy.java
new file mode 100644
index 0000000..8be9f1a
--- /dev/null
+++ b/v4/java/android/support/v4/widget/FocusStrategy.java
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.widget;
+
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+
+import android.support.v4.view.ViewCompat.FocusRealDirection;
+import android.support.v4.view.ViewCompat.FocusRelativeDirection;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+/**
+ * Implements absolute and relative focus movement strategies. Adapted from
+ * android.view.FocusFinder to work with generic collections of bounded items.
+ */
+class FocusStrategy {
+ public static <L,T> T findNextFocusInRelativeDirection(@NonNull L focusables,
+ @NonNull CollectionAdapter<L,T> collectionAdapter, @NonNull BoundsAdapter<T> adapter,
+ @Nullable T focused, @FocusRelativeDirection int direction, boolean isLayoutRtl,
+ boolean wrap) {
+ final int count = collectionAdapter.size(focusables);
+ final ArrayList<T> sortedFocusables = new ArrayList<>(count);
+ for (int i = 0; i < count; i++) {
+ sortedFocusables.add(collectionAdapter.get(focusables, i));
+ }
+
+ final SequentialComparator<T> comparator = new SequentialComparator<>(isLayoutRtl, adapter);
+ Collections.sort(sortedFocusables, comparator);
+
+ switch (direction) {
+ case View.FOCUS_FORWARD:
+ return getNextFocusable(focused, sortedFocusables, wrap);
+ case View.FOCUS_BACKWARD:
+ return getPreviousFocusable(focused, sortedFocusables, wrap);
+ default:
+ throw new IllegalArgumentException("direction must be one of "
+ + "{FOCUS_FORWARD, FOCUS_BACKWARD}.");
+ }
+ }
+
+ private static <T> T getNextFocusable(T focused, ArrayList<T> focusables, boolean wrap) {
+ final int count = focusables.size();
+
+ // The position of the next focusable item, which is the first item if
+ // no item is currently focused.
+ final int position = (focused == null ? -1 : focusables.lastIndexOf(focused)) + 1;
+ if (position < count) {
+ return focusables.get(position);
+ } else if (wrap && count > 0) {
+ return focusables.get(0);
+ } else {
+ return null;
+ }
+ }
+
+ private static <T> T getPreviousFocusable(T focused, ArrayList<T> focusables, boolean wrap) {
+ final int count = focusables.size();
+
+ // The position of the previous focusable item, which is the last item
+ // if no item is currently focused.
+ final int position = (focused == null ? count : focusables.indexOf(focused)) - 1;
+ if (position >= 0) {
+ return focusables.get(position);
+ } else if (wrap && count > 0) {
+ return focusables.get(count - 1);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Sorts views according to their visual layout and geometry for default tab order.
+ * This is used for sequential focus traversal.
+ */
+ private static class SequentialComparator<T> implements Comparator<T> {
+ private final Rect mTemp1 = new Rect();
+ private final Rect mTemp2 = new Rect();
+
+ private final boolean mIsLayoutRtl;
+ private final BoundsAdapter<T> mAdapter;
+
+ public SequentialComparator(boolean isLayoutRtl, BoundsAdapter<T> adapter) {
+ mIsLayoutRtl = isLayoutRtl;
+ mAdapter = adapter;
+ }
+
+ public int compare(T first, T second) {
+ final Rect firstRect = mTemp1;
+ final Rect secondRect = mTemp2;
+
+ mAdapter.obtainBounds(first, firstRect);
+ mAdapter.obtainBounds(second, secondRect);
+
+ if (firstRect.top < secondRect.top) {
+ return -1;
+ } else if (firstRect.top > secondRect.top) {
+ return 1;
+ } else if (firstRect.left < secondRect.left) {
+ return mIsLayoutRtl ? 1 : -1;
+ } else if (firstRect.left > secondRect.left) {
+ return mIsLayoutRtl ? -1 : 1;
+ } else if (firstRect.bottom < secondRect.bottom) {
+ return -1;
+ } else if (firstRect.bottom > secondRect.bottom) {
+ return 1;
+ } else if (firstRect.right < secondRect.right) {
+ return mIsLayoutRtl ? 1 : -1;
+ } else if (firstRect.right > secondRect.right) {
+ return mIsLayoutRtl ? -1 : 1;
+ } else {
+ // The view are distinct but completely coincident so we
+ // consider them equal for our purposes. Since the sort is
+ // stable, this means that the views will retain their
+ // layout order relative to one another.
+ return 0;
+ }
+ }
+ }
+
+ public static <L,T> T findNextFocusInAbsoluteDirection(@NonNull L focusables,
+ @NonNull CollectionAdapter<L,T> collectionAdapter, @NonNull BoundsAdapter<T> adapter,
+ @Nullable T focused, @NonNull Rect focusedRect, int direction) {
+ // Initialize the best candidate to something impossible so that
+ // the first plausible view will become the best choice.
+ final Rect bestCandidateRect = new Rect(focusedRect);
+
+ switch (direction) {
+ case View.FOCUS_LEFT:
+ bestCandidateRect.offset(focusedRect.width() + 1, 0);
+ break;
+ case View.FOCUS_RIGHT:
+ bestCandidateRect.offset(-(focusedRect.width() + 1), 0);
+ break;
+ case View.FOCUS_UP:
+ bestCandidateRect.offset(0, focusedRect.height() + 1);
+ break;
+ case View.FOCUS_DOWN:
+ bestCandidateRect.offset(0, -(focusedRect.height() + 1));
+ break;
+ default:
+ throw new IllegalArgumentException("direction must be one of "
+ + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+ }
+
+ T closest = null;
+
+ final int count = collectionAdapter.size(focusables);
+ final Rect focusableRect = new Rect();
+ for (int i = 0; i < count; i++) {
+ final T focusable = collectionAdapter.get(focusables, i);
+ if (focusable == focused) {
+ continue;
+ }
+
+ // get focus bounds of other view
+ adapter.obtainBounds(focusable, focusableRect);
+ if (isBetterCandidate(direction, focusedRect, focusableRect, bestCandidateRect)) {
+ bestCandidateRect.set(focusableRect);
+ closest = focusable;
+ }
+ }
+
+ return closest;
+ }
+
+ /**
+ * Is candidate a better candidate than currentBest for a focus search
+ * in a particular direction from a source rect? This is the core
+ * routine that determines the order of focus searching.
+ *
+ * @param direction the direction (up, down, left, right)
+ * @param source the source from which we are searching
+ * @param candidate the candidate rectangle
+ * @param currentBest the current best rectangle
+ * @return {@code true} if the candidate rectangle is a better than the
+ * current best rectangle, {@code false} otherwise
+ */
+ private static boolean isBetterCandidate(
+ @FocusRealDirection int direction, @NonNull Rect source,
+ @NonNull Rect candidate, @NonNull Rect currentBest) {
+ // To be a better candidate, need to at least be a candidate in the
+ // first place. :)
+ if (!isCandidate(source, candidate, direction)) {
+ return false;
+ }
+
+ // We know that candidateRect is a candidate. If currentBest is not
+ // a candidate, candidateRect is better.
+ if (!isCandidate(source, currentBest, direction)) {
+ return true;
+ }
+
+ // If candidateRect is better by beam, it wins.
+ if (beamBeats(direction, source, candidate, currentBest)) {
+ return true;
+ }
+
+ // If currentBest is better, then candidateRect cant' be. :)
+ if (beamBeats(direction, source, currentBest, candidate)) {
+ return false;
+ }
+
+ // Otherwise, do fudge-tastic comparison of the major and minor
+ // axis.
+ final int candidateDist = getWeightedDistanceFor(
+ majorAxisDistance(direction, source, candidate),
+ minorAxisDistance(direction, source, candidate));
+ final int currentBestDist = getWeightedDistanceFor(
+ majorAxisDistance(direction, source, currentBest),
+ minorAxisDistance(direction, source, currentBest));
+ return candidateDist < currentBestDist;
+ }
+
+ /**
+ * One rectangle may be another candidate than another by virtue of
+ * being exclusively in the beam of the source rect.
+ *
+ * @return whether rect1 is a better candidate than rect2 by virtue of
+ * it being in source's beam
+ */
+ private static boolean beamBeats(@FocusRealDirection int direction,
+ @NonNull Rect source, @NonNull Rect rect1, @NonNull Rect rect2) {
+ final boolean rect1InSrcBeam = beamsOverlap(direction, source, rect1);
+ final boolean rect2InSrcBeam = beamsOverlap(direction, source, rect2);
+
+ // If rect1 isn't exclusively in the src beam, it doesn't win.
+ if (rect2InSrcBeam || !rect1InSrcBeam) {
+ return false;
+ }
+
+ // We know rect1 is in the beam, and rect2 is not.
+
+ // If rect1 is to the direction of, and rect2 is not, rect1 wins.
+ // For example, for direction left, if rect1 is to the left of the
+ // source and rect2 is below, then we always prefer the in beam
+ // rect1, since rect2 could be reached by going down.
+ if (!isToDirectionOf(direction, source, rect2)) {
+ return true;
+ }
+
+ // For horizontal directions, being exclusively in beam always
+ // wins.
+ if (direction == View.FOCUS_LEFT || direction == View.FOCUS_RIGHT) {
+ return true;
+ }
+
+ // For vertical directions, beams only beat up to a point: now, as
+ // long as rect2 isn't completely closer, rect1 wins, e.g. for
+ // direction down, completely closer means for rect2's top edge to
+ // be closer to the source's top edge than rect1's bottom edge.
+ return majorAxisDistance(direction, source, rect1)
+ < majorAxisDistanceToFarEdge(direction, source, rect2);
+ }
+
+ /**
+ * Fudge-factor opportunity: how to calculate distance given major and
+ * minor axis distances.
+ * <p/>
+ * Warning: this fudge factor is finely tuned, be sure to run all focus
+ * tests if you dare tweak it.
+ */
+ private static int getWeightedDistanceFor(int majorAxisDistance, int minorAxisDistance) {
+ return 13 * majorAxisDistance * majorAxisDistance
+ + minorAxisDistance * minorAxisDistance;
+ }
+
+ /**
+ * Is destRect a candidate for the next focus given the direction? This
+ * checks whether the dest is at least partially to the direction of
+ * (e.g. left of) from source.
+ * <p/>
+ * Includes an edge case for an empty rect,which is used in some cases
+ * when searching from a point on the screen.
+ */
+ private static boolean isCandidate(@NonNull Rect srcRect, @NonNull Rect destRect,
+ @FocusRealDirection int direction) {
+ switch (direction) {
+ case View.FOCUS_LEFT:
+ return (srcRect.right > destRect.right || srcRect.left >= destRect.right)
+ && srcRect.left > destRect.left;
+ case View.FOCUS_RIGHT:
+ return (srcRect.left < destRect.left || srcRect.right <= destRect.left)
+ && srcRect.right < destRect.right;
+ case View.FOCUS_UP:
+ return (srcRect.bottom > destRect.bottom || srcRect.top >= destRect.bottom)
+ && srcRect.top > destRect.top;
+ case View.FOCUS_DOWN:
+ return (srcRect.top < destRect.top || srcRect.bottom <= destRect.top)
+ && srcRect.bottom < destRect.bottom;
+ }
+ throw new IllegalArgumentException("direction must be one of "
+ + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+ }
+
+
+ /**
+ * Do the "beams" w.r.t the given direction's axis of rect1 and rect2 overlap?
+ *
+ * @param direction the direction (up, down, left, right)
+ * @param rect1 the first rectangle
+ * @param rect2 the second rectangle
+ * @return whether the beams overlap
+ */
+ private static boolean beamsOverlap(@FocusRealDirection int direction,
+ @NonNull Rect rect1, @NonNull Rect rect2) {
+ switch (direction) {
+ case View.FOCUS_LEFT:
+ case View.FOCUS_RIGHT:
+ return (rect2.bottom >= rect1.top) && (rect2.top <= rect1.bottom);
+ case View.FOCUS_UP:
+ case View.FOCUS_DOWN:
+ return (rect2.right >= rect1.left) && (rect2.left <= rect1.right);
+ }
+ throw new IllegalArgumentException("direction must be one of "
+ + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+ }
+
+ /**
+ * e.g for left, is 'to left of'
+ */
+ private static boolean isToDirectionOf(@FocusRealDirection int direction,
+ @NonNull Rect src, @NonNull Rect dest) {
+ switch (direction) {
+ case View.FOCUS_LEFT:
+ return src.left >= dest.right;
+ case View.FOCUS_RIGHT:
+ return src.right <= dest.left;
+ case View.FOCUS_UP:
+ return src.top >= dest.bottom;
+ case View.FOCUS_DOWN:
+ return src.bottom <= dest.top;
+ }
+ throw new IllegalArgumentException("direction must be one of "
+ + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+ }
+
+ /**
+ * @return the distance from the edge furthest in the given direction
+ * of source to the edge nearest in the given direction of
+ * dest. If the dest is not in the direction from source,
+ * returns 0.
+ */
+ private static int majorAxisDistance(@FocusRealDirection int direction,
+ @NonNull Rect source, @NonNull Rect dest) {
+ return Math.max(0, majorAxisDistanceRaw(direction, source, dest));
+ }
+
+ private static int majorAxisDistanceRaw(@FocusRealDirection int direction,
+ @NonNull Rect source, @NonNull Rect dest) {
+ switch (direction) {
+ case View.FOCUS_LEFT:
+ return source.left - dest.right;
+ case View.FOCUS_RIGHT:
+ return dest.left - source.right;
+ case View.FOCUS_UP:
+ return source.top - dest.bottom;
+ case View.FOCUS_DOWN:
+ return dest.top - source.bottom;
+ }
+ throw new IllegalArgumentException("direction must be one of "
+ + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+ }
+
+ /**
+ * @return the distance along the major axis w.r.t the direction from
+ * the edge of source to the far edge of dest. If the dest is
+ * not in the direction from source, returns 1 to break ties
+ * with {@link #majorAxisDistance}.
+ */
+ private static int majorAxisDistanceToFarEdge(@FocusRealDirection int direction,
+ @NonNull Rect source, @NonNull Rect dest) {
+ return Math.max(1, majorAxisDistanceToFarEdgeRaw(direction, source, dest));
+ }
+
+ private static int majorAxisDistanceToFarEdgeRaw(
+ @FocusRealDirection int direction, @NonNull Rect source,
+ @NonNull Rect dest) {
+ switch (direction) {
+ case View.FOCUS_LEFT:
+ return source.left - dest.left;
+ case View.FOCUS_RIGHT:
+ return dest.right - source.right;
+ case View.FOCUS_UP:
+ return source.top - dest.top;
+ case View.FOCUS_DOWN:
+ return dest.bottom - source.bottom;
+ }
+ throw new IllegalArgumentException("direction must be one of "
+ + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+ }
+
+ /**
+ * Finds the distance on the minor axis w.r.t the direction to the
+ * nearest edge of the destination rectangle.
+ *
+ * @param direction the direction (up, down, left, right)
+ * @param source the source rect
+ * @param dest the destination rect
+ * @return the distance
+ */
+ private static int minorAxisDistance(@FocusRealDirection int direction, @NonNull Rect source,
+ @NonNull Rect dest) {
+ switch (direction) {
+ case View.FOCUS_LEFT:
+ case View.FOCUS_RIGHT:
+ // the distance between the center verticals
+ return Math.abs(
+ ((source.top + source.height() / 2) -
+ ((dest.top + dest.height() / 2))));
+ case View.FOCUS_UP:
+ case View.FOCUS_DOWN:
+ // the distance between the center horizontals
+ return Math.abs(
+ ((source.left + source.width() / 2) -
+ ((dest.left + dest.width() / 2))));
+ }
+ throw new IllegalArgumentException("direction must be one of "
+ + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+ }
+
+ /**
+ * Adapter used to obtain bounds from a generic data type.
+ */
+ public interface BoundsAdapter<T> {
+ void obtainBounds(T data, Rect outBounds);
+ }
+
+ /**
+ * Adapter used to obtain items from a generic collection type.
+ */
+ public interface CollectionAdapter<T, V> {
+ V get(T collection, int index);
+ int size(T collection);
+ }
+}
diff --git a/v7/appcompat/api/current.txt b/v7/appcompat/api/current.txt
index 8f35544..dff93b9 100644
--- a/v7/appcompat/api/current.txt
+++ b/v7/appcompat/api/current.txt
@@ -244,6 +244,7 @@
public abstract class AppCompatDelegate {
method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+ method public abstract void applyDayNight();
method public static android.support.v7.app.AppCompatDelegate create(android.app.Activity, android.support.v7.app.AppCompatCallback);
method public static android.support.v7.app.AppCompatDelegate create(android.app.Dialog, android.support.v7.app.AppCompatCallback);
method public abstract android.view.View createView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
@@ -265,12 +266,16 @@
method public abstract void setContentView(int);
method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public abstract void setHandleNativeActionModesEnabled(boolean);
+ method public abstract void setNightMode(int);
method public abstract void setSupportActionBar(android.support.v7.widget.Toolbar);
method public abstract void setTitle(java.lang.CharSequence);
method public abstract android.support.v7.view.ActionMode startSupportActionMode(android.support.v7.view.ActionMode.Callback);
field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
field public static final int FEATURE_SUPPORT_ACTION_BAR = 108; // 0x6c
field public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 109; // 0x6d
+ field public static final int MODE_NIGHT_AUTO = 2; // 0x2
+ field public static final int MODE_NIGHT_NO = 0; // 0x0
+ field public static final int MODE_NIGHT_YES = 1; // 0x1
}
public class AppCompatDialog extends android.app.Dialog implements android.support.v7.app.AppCompatCallback {
@@ -369,6 +374,7 @@
field public static int alertDialogStyle;
field public static int alertDialogTheme;
field public static int allowStacking;
+ field public static int alpha;
field public static int arrowHeadLength;
field public static int arrowShaftLength;
field public static int autoCompleteTextViewStyle;
@@ -384,6 +390,7 @@
field public static int buttonBarNeutralButtonStyle;
field public static int buttonBarPositiveButtonStyle;
field public static int buttonBarStyle;
+ field public static int buttonGravity;
field public static int buttonPanelSideLayout;
field public static int buttonStyle;
field public static int buttonStyleSmall;
@@ -514,11 +521,12 @@
field public static int thickness;
field public static int thumbTextPadding;
field public static int title;
+ field public static int titleMargin;
field public static int titleMarginBottom;
field public static int titleMarginEnd;
field public static int titleMarginStart;
field public static int titleMarginTop;
- field public static int titleMargins;
+ field public static deprecated int titleMargins;
field public static int titleTextAppearance;
field public static int titleTextColor;
field public static int titleTextStyle;
@@ -541,11 +549,7 @@
public static final class R.bool {
ctor public R.bool();
field public static int abc_action_bar_embed_tabs;
- field public static int abc_action_bar_embed_tabs_pre_jb;
- field public static int abc_action_bar_expanded_action_views_exclusive;
- field public static int abc_allow_stacked_button_bar;
field public static int abc_config_actionMenuItemAllCaps;
- field public static int abc_config_allowActionMenuItemTextWithIcon;
field public static int abc_config_closeDialogWhenTouchOutside;
field public static int abc_config_showMenuShortcutsWhenKeyboardPresent;
}
@@ -566,6 +570,13 @@
field public static int abc_search_url_text_selected;
field public static int abc_secondary_text_material_dark;
field public static int abc_secondary_text_material_light;
+ field public static int abc_tint_btn_checkable;
+ field public static int abc_tint_default;
+ field public static int abc_tint_edittext;
+ field public static int abc_tint_seek_thumb;
+ field public static int abc_tint_spinner;
+ field public static int abc_tint_switch_thumb;
+ field public static int abc_tint_switch_track;
field public static int accent_material_dark;
field public static int accent_material_light;
field public static int background_floating_material_dark;
@@ -671,7 +682,6 @@
field public static int abc_list_item_padding_horizontal_material;
field public static int abc_panel_menu_list_width;
field public static int abc_search_view_preferred_width;
- field public static int abc_search_view_text_min_width;
field public static int abc_seekbar_track_background_height_material;
field public static int abc_seekbar_track_progress_height_material;
field public static int abc_select_dialog_padding_start_material;
@@ -798,6 +808,7 @@
field public static int alertTitle;
field public static int always;
field public static int beginning;
+ field public static int bottom;
field public static int buttonPanel;
field public static int cancel_action;
field public static int checkbox;
@@ -867,6 +878,7 @@
field public static int time;
field public static int title;
field public static int title_template;
+ field public static int top;
field public static int topPanel;
field public static int up;
field public static int useLogo;
@@ -878,7 +890,6 @@
ctor public R.integer();
field public static int abc_config_activityDefaultDur;
field public static int abc_config_activityShortDur;
- field public static int abc_max_action_buttons;
field public static int cancel_button_image_alpha;
field public static int status_bar_notification_info_maxnum;
}
@@ -1177,6 +1188,13 @@
field public static int ThemeOverlay_AppCompat_Light;
field public static int Theme_AppCompat;
field public static int Theme_AppCompat_CompactMenu;
+ field public static int Theme_AppCompat_DayNight;
+ field public static int Theme_AppCompat_DayNight_DarkActionBar;
+ field public static int Theme_AppCompat_DayNight_Dialog;
+ field public static int Theme_AppCompat_DayNight_DialogWhenLarge;
+ field public static int Theme_AppCompat_DayNight_Dialog_Alert;
+ field public static int Theme_AppCompat_DayNight_Dialog_MinWidth;
+ field public static int Theme_AppCompat_DayNight_NoActionBar;
field public static int Theme_AppCompat_Dialog;
field public static int Theme_AppCompat_DialogWhenLarge;
field public static int Theme_AppCompat_Dialog_Alert;
@@ -1315,6 +1333,10 @@
field public static int AppCompatTextView_android_textAppearance;
field public static int AppCompatTextView_textAllCaps;
field public static int ButtonBarLayout_allowStacking;
+ field public static final int[] ColorStateListItem;
+ field public static int ColorStateListItem_alpha;
+ field public static int ColorStateListItem_android_alpha;
+ field public static int ColorStateListItem_android_color;
field public static final int[] CompoundButton;
field public static int CompoundButton_android_button;
field public static int CompoundButton_buttonTint;
@@ -1541,9 +1563,10 @@
field public static int Theme_windowMinWidthMajor;
field public static int Theme_windowMinWidthMinor;
field public static int Theme_windowNoTitle;
- field public static final int[] Toolbar;
+ field public static final deprecated int[] Toolbar;
field public static int Toolbar_android_gravity;
field public static int Toolbar_android_minHeight;
+ field public static int Toolbar_buttonGravity;
field public static int Toolbar_collapseContentDescription;
field public static int Toolbar_collapseIcon;
field public static int Toolbar_contentInsetEnd;
@@ -1560,11 +1583,12 @@
field public static int Toolbar_subtitleTextAppearance;
field public static int Toolbar_subtitleTextColor;
field public static int Toolbar_title;
+ field public static int Toolbar_titleMargin;
field public static int Toolbar_titleMarginBottom;
field public static int Toolbar_titleMarginEnd;
field public static int Toolbar_titleMarginStart;
field public static int Toolbar_titleMarginTop;
- field public static int Toolbar_titleMargins;
+ field public static deprecated int Toolbar_titleMargins;
field public static int Toolbar_titleTextAppearance;
field public static int Toolbar_titleTextColor;
field public static final int[] View;
@@ -2026,6 +2050,10 @@
method public int getPopupTheme();
method public java.lang.CharSequence getSubtitle();
method public java.lang.CharSequence getTitle();
+ method public int getTitleMarginBottom();
+ method public int getTitleMarginEnd();
+ method public int getTitleMarginStart();
+ method public int getTitleMarginTop();
method public boolean hasExpandedActionView();
method public boolean hideOverflowMenu();
method public void inflateMenu(int);
@@ -2051,6 +2079,11 @@
method public void setSubtitleTextColor(int);
method public void setTitle(int);
method public void setTitle(java.lang.CharSequence);
+ method public void setTitleMargin(int, int, int, int);
+ method public void setTitleMarginBottom(int);
+ method public void setTitleMarginEnd(int);
+ method public void setTitleMarginStart(int);
+ method public void setTitleMarginTop(int);
method public void setTitleTextAppearance(android.content.Context, int);
method public void setTitleTextColor(int);
method public boolean showOverflowMenu();
diff --git a/v7/appcompat/res-public/values/public_attrs.xml b/v7/appcompat/res-public/values/public_attrs.xml
index 3c812d7..1990eec 100644
--- a/v7/appcompat/res-public/values/public_attrs.xml
+++ b/v7/appcompat/res-public/values/public_attrs.xml
@@ -50,6 +50,7 @@
<public type="attr" name="actionViewClass"/>
<public type="attr" name="alertDialogStyle"/>
<public type="attr" name="alertDialogTheme"/>
+ <public type="attr" name="alpha"/>
<public type="attr" name="arrowHeadLength"/>
<public type="attr" name="arrowShaftLength"/>
<public type="attr" name="autoCompleteTextViewStyle"/>
@@ -65,6 +66,7 @@
<public type="attr" name="buttonBarNeutralButtonStyle"/>
<public type="attr" name="buttonBarPositiveButtonStyle"/>
<public type="attr" name="buttonBarStyle"/>
+ <public type="attr" name="buttonGravity" />
<public type="attr" name="buttonStyle"/>
<public type="attr" name="buttonStyleSmall"/>
<public type="attr" name="buttonTint"/>
@@ -126,6 +128,7 @@
<public type="attr" name="listPreferredItemPaddingRight"/>
<public type="attr" name="logo"/>
<public type="attr" name="logoDescription"/>
+ <public type="attr" name="maxButtonHeight" />
<public type="attr" name="measureWithLargestChild"/>
<public type="attr" name="middleBarArrowSize"/>
<public type="attr" name="navigationContentDescription"/>
@@ -181,6 +184,7 @@
<public type="attr" name="title"/>
<public type="attr" name="titleMarginBottom"/>
<public type="attr" name="titleMarginEnd"/>
+ <public type="attr" name="titleMargin"/>
<public type="attr" name="titleMargins"/>
<public type="attr" name="titleMarginStart"/>
<public type="attr" name="titleMarginTop"/>
diff --git a/v7/appcompat/res-public/values/public_styles.xml b/v7/appcompat/res-public/values/public_styles.xml
index 991ab54..973c317 100644
--- a/v7/appcompat/res-public/values/public_styles.xml
+++ b/v7/appcompat/res-public/values/public_styles.xml
@@ -61,6 +61,13 @@
<public type="style" name="TextAppearance.AppCompat.Widget.Switch"/>
<public type="style" name="TextAppearance.AppCompat.Widget.TextView.SpinnerItem"/>
<public type="style" name="Theme.AppCompat"/>
+ <public type="style" name="Theme.AppCompat.DayNight"/>
+ <public type="style" name="Theme.AppCompat.DayNight.DarkActionBar"/>
+ <public type="style" name="Theme.AppCompat.DayNight.Dialog"/>
+ <public type="style" name="Theme.AppCompat.DayNight.Dialog.Alert"/>
+ <public type="style" name="Theme.AppCompat.DayNight.Dialog.MinWidth"/>
+ <public type="style" name="Theme.AppCompat.DayNight.DialogWhenLarge"/>
+ <public type="style" name="Theme.AppCompat.DayNight.NoActionBar"/>
<public type="style" name="Theme.AppCompat.Dialog"/>
<public type="style" name="Theme.AppCompat.Dialog.Alert"/>
<public type="style" name="Theme.AppCompat.Dialog.MinWidth"/>
diff --git a/v7/appcompat/res/color/abc_tint_btn_checkable.xml b/v7/appcompat/res/color/abc_tint_btn_checkable.xml
new file mode 100644
index 0000000..0c663f6
--- /dev/null
+++ b/v7/appcompat/res/color/abc_tint_btn_checkable.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item android:state_enabled="false" android:color="?attr/colorControlNormal" app:alpha="?android:disabledAlpha"/>
+ <item android:state_checked="true" android:color="?attr/colorControlActivated"/>
+ <item android:color="?attr/colorControlNormal"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/color/abc_tint_default.xml b/v7/appcompat/res/color/abc_tint_default.xml
new file mode 100644
index 0000000..8d7c391
--- /dev/null
+++ b/v7/appcompat/res/color/abc_tint_default.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item android:state_enabled="false" android:color="?attr/colorControlNormal" app:alpha="?android:disabledAlpha"/>
+ <item android:state_focused="true" android:color="?attr/colorControlActivated"/>
+ <item android:state_pressed="true" android:color="?attr/colorControlActivated"/>
+ <item android:state_activated="true" android:color="?attr/colorControlActivated"/>
+ <item android:state_selected="true" android:color="?attr/colorControlActivated"/>
+ <item android:state_checked="true" android:color="?attr/colorControlActivated"/>
+ <item android:color="?attr/colorControlNormal"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/color/abc_tint_edittext.xml b/v7/appcompat/res/color/abc_tint_edittext.xml
new file mode 100644
index 0000000..536d77f
--- /dev/null
+++ b/v7/appcompat/res/color/abc_tint_edittext.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item android:state_enabled="false" android:color="?attr/colorControlNormal"
+ app:alpha="?android:disabledAlpha"/>
+ <item android:state_pressed="false" android:state_focused="false"
+ android:color="?attr/colorControlNormal"/>
+ <item android:color="?attr/colorControlActivated"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-w480dp/bools.xml b/v7/appcompat/res/color/abc_tint_seek_thumb.xml
similarity index 61%
rename from v7/appcompat/res/values-w480dp/bools.xml
rename to v7/appcompat/res/color/abc_tint_seek_thumb.xml
index 470f89b..cb53788 100644
--- a/v7/appcompat/res/values-w480dp/bools.xml
+++ b/v7/appcompat/res/color/abc_tint_seek_thumb.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!--
+ Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,6 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources>
- <bool name="abc_action_bar_embed_tabs_pre_jb">true</bool>
-</resources>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item android:state_enabled="false" android:color="?attr/colorControlActivated" app:alpha="?android:attr/disabledAlpha"/>
+ <item android:color="?attr/colorControlActivated"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/color/abc_tint_spinner.xml b/v7/appcompat/res/color/abc_tint_spinner.xml
new file mode 100644
index 0000000..44333dd
--- /dev/null
+++ b/v7/appcompat/res/color/abc_tint_spinner.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item android:state_enabled="false" android:color="?attr/colorControlNormal" app:alpha="?android:disabledAlpha"/>
+ <item android:state_pressed="false" android:state_focused="false" android:color="?attr/colorControlNormal"/>
+ <item android:color="?attr/colorControlActivated"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/color/abc_tint_switch_thumb.xml b/v7/appcompat/res/color/abc_tint_switch_thumb.xml
new file mode 100644
index 0000000..fc8bd24
--- /dev/null
+++ b/v7/appcompat/res/color/abc_tint_switch_thumb.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item android:state_enabled="false" android:color="?attr/colorSwitchThumbNormal" app:alpha="?android:attr/disabledAlpha"/>
+ <item android:state_checked="true" android:color="?attr/colorControlActivated"/>
+ <item android:color="?attr/colorSwitchThumbNormal"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/color/abc_tint_switch_track.xml b/v7/appcompat/res/color/abc_tint_switch_track.xml
new file mode 100644
index 0000000..22322f8
--- /dev/null
+++ b/v7/appcompat/res/color/abc_tint_switch_track.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item android:state_enabled="false" android:color="?android:attr/colorForeground" app:alpha="0.1"/>
+ <item android:state_checked="true" android:color="?attr/colorControlActivated" app:alpha="0.3"/>
+ <item android:color="?android:attr/colorForeground" app:alpha="0.3"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout/abc_alert_dialog_button_bar_material.xml b/v7/appcompat/res/layout/abc_alert_dialog_button_bar_material.xml
index d32ad10..08adfd1 100644
--- a/v7/appcompat/res/layout/abc_alert_dialog_button_bar_material.xml
+++ b/v7/appcompat/res/layout/abc_alert_dialog_button_bar_material.xml
@@ -28,7 +28,6 @@
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:gravity="bottom"
- app:allowStacking="@bool/abc_allow_stacked_button_bar"
style="?attr/buttonBarStyle">
<Button
diff --git a/v7/appcompat/res/layout/abc_search_view.xml b/v7/appcompat/res/layout/abc_search_view.xml
index a7446e3..b496f5d 100644
--- a/v7/appcompat/res/layout/abc_search_view.xml
+++ b/v7/appcompat/res/layout/abc_search_view.xml
@@ -80,7 +80,6 @@
android:layout_height="36dip"
android:layout_width="0dp"
android:layout_weight="1"
- android:minWidth="@dimen/abc_search_view_text_min_width"
android:layout_gravity="bottom"
android:paddingLeft="@dimen/abc_dropdownitem_text_padding_left"
android:paddingRight="@dimen/abc_dropdownitem_text_padding_right"
diff --git a/v7/appcompat/res/values-h320dp/bools.xml b/v7/appcompat/res/values-h320dp/bools.xml
deleted file mode 100644
index 5576c18..0000000
--- a/v7/appcompat/res/values-h320dp/bools.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <bool name="abc_allow_stacked_button_bar">true</bool>
-</resources>
diff --git a/v7/appcompat/res/values-land/bools.xml b/v7/appcompat/res/values-land/bools.xml
deleted file mode 100644
index 7d1a1af..0000000
--- a/v7/appcompat/res/values-land/bools.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <bool name="abc_action_bar_embed_tabs_pre_jb">true</bool>
-</resources>
diff --git a/v7/appcompat/res/values-land/config.xml b/v7/appcompat/res/values-land/config.xml
deleted file mode 100644
index d0d990d..0000000
--- a/v7/appcompat/res/values-land/config.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources>
- <bool name="abc_config_allowActionMenuItemTextWithIcon">true</bool>
-</resources>
diff --git a/v7/appcompat/res/values-large/bools.xml b/v7/appcompat/res/values-large/bools.xml
deleted file mode 100644
index 7d1a1af..0000000
--- a/v7/appcompat/res/values-large/bools.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <bool name="abc_action_bar_embed_tabs_pre_jb">true</bool>
-</resources>
diff --git a/v7/appcompat/res/values-large/config.xml b/v7/appcompat/res/values-large/config.xml
index c4f04a3..58e34a0 100644
--- a/v7/appcompat/res/values-large/config.xml
+++ b/v7/appcompat/res/values-large/config.xml
@@ -20,11 +20,6 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
<resources>
- <!-- Whether action menu items should obey the "withText" showAsAction.
- This may be set to false for situations where space is
- extremely limited. -->
- <bool name="abc_config_allowActionMenuItemTextWithIcon">true</bool>
-
<!-- see comment in values/config.xml -->
<dimen name="abc_config_prefDialogWidth">440dp</dimen>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/v7/appcompat/res/values-large/dimens.xml b/v7/appcompat/res/values-large/dimens.xml
index 16bb4f6..5afdda4 100644
--- a/v7/appcompat/res/values-large/dimens.xml
+++ b/v7/appcompat/res/values-large/dimens.xml
@@ -15,13 +15,6 @@
-->
<resources>
- <!-- Minimum width of the search view text entry area. -->
- <dimen name="abc_search_view_text_min_width">192dip</dimen>
- <!-- The maximum number of action buttons that should be permitted within
- an action bar/action mode. This will be used to determine how many
- showAsAction="ifRoom" items can fit. "always" items can override this. -->
- <integer name="abc_max_action_buttons">4</integer>
-
<item type="dimen" name="abc_dialog_fixed_width_major">60%</item>
<item type="dimen" name="abc_dialog_fixed_width_minor">90%</item>
<item type="dimen" name="abc_dialog_fixed_height_major">60%</item>
diff --git a/v7/appcompat/res/values-night/themes_daynight.xml b/v7/appcompat/res/values-night/themes_daynight.xml
new file mode 100644
index 0000000..965d355
--- /dev/null
+++ b/v7/appcompat/res/values-night/themes_daynight.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+
+ <!-- Material theme (day/night vesion) for activities. -->
+ <style name="Theme.AppCompat.DayNight" parent="Theme.AppCompat" />
+
+ <!-- Variant of AppCompat.DayNight that has a solid (opaque) action bar
+ with an inverse color profile. The dark action bar sharply stands out against
+ the light content (when applicable). -->
+ <style name="Theme.AppCompat.DayNight.DarkActionBar" parent="Theme.AppCompat" />
+
+ <!-- Variant of AppCompat.DayNight with no action bar. -->
+ <style name="Theme.AppCompat.DayNight.NoActionBar" parent="Theme.AppCompat.NoActionBar" />
+
+ <!-- Material theme (day/night vesion) for dialog windows and activities,
+ which is used by the {@code android.support.v7.app.Dialog} class. This changes
+ the window to be floating (not fill the entire screen), and puts a
+ frame around its contents. You can set this theme on an activity if
+ you would like to make an activity that looks like a Dialog. -->
+ <style name="Theme.AppCompat.DayNight.Dialog" parent="Theme.AppCompat.Dialog" />
+
+ <!-- Variant of Theme.AppCompat.DayNight.Dialog that has a nice minimum width for
+ a regular dialog. -->
+ <style name="Theme.AppCompat.DayNight.Dialog.MinWidth" parent="Theme.AppCompat.Dialog.MinWidth" />
+
+ <!-- Theme for a window that will be displayed either full-screen on
+ smaller screens (small, normal) or as a dialog on larger screens
+ (large, xlarge). -->
+ <style name="Theme.AppCompat.DayNight.DialogWhenLarge" parent="Theme.AppCompat.DialogWhenLarge" />
+
+ <!-- Material user theme for alert dialog windows, which is used by the
+ {@code android.support.v7.app.AlertDialog} class. -->
+ <style name="Theme.AppCompat.DayNight.Dialog.Alert" parent="Theme.AppCompat.Dialog.Alert" />
+
+</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-sw600dp/dimens.xml b/v7/appcompat/res/values-sw600dp/dimens.xml
index a4bc455..a83abb1 100644
--- a/v7/appcompat/res/values-sw600dp/dimens.xml
+++ b/v7/appcompat/res/values-sw600dp/dimens.xml
@@ -15,12 +15,6 @@
-->
<resources>
-
- <!-- The maximum number of action buttons that should be permitted within
- an action bar/action mode. This will be used to determine how many
- showAsAction="ifRoom" items can fit. "always" items can override this. -->
- <integer name="abc_max_action_buttons">5</integer>
-
<!-- Use the default title sizes on tablets. -->
<dimen name="abc_text_size_title_material_toolbar">20dp</dimen>
<!-- Use the default subtitle sizes on tablets. -->
diff --git a/v7/appcompat/res/values-tr/strings.xml b/v7/appcompat/res/values-tr/strings.xml
index 56aecf6..185cd4d 100644
--- a/v7/appcompat/res/values-tr/strings.xml
+++ b/v7/appcompat/res/values-tr/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="abc_action_mode_done" msgid="4076576682505996667">"Tamamlandı"</string>
+ <string name="abc_action_mode_done" msgid="4076576682505996667">"Bitti"</string>
<string name="abc_action_bar_home_description" msgid="4600421777120114993">"Ana ekrana git"</string>
<string name="abc_action_bar_up_description" msgid="1594238315039666878">"Yukarı git"</string>
<string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Diğer seçenekler"</string>
diff --git a/v7/appcompat/res/values-w360dp/dimens.xml b/v7/appcompat/res/values-w360dp/dimens.xml
deleted file mode 100644
index e5b2456..0000000
--- a/v7/appcompat/res/values-w360dp/dimens.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <!-- The maximum number of action buttons that should be permitted within
- an action bar/action mode. This will be used to determine how many
- showAsAction="ifRoom" items can fit. "always" items can override this. -->
- <integer name="abc_max_action_buttons">3</integer>
-</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-w480dp/config.xml b/v7/appcompat/res/values-w480dp/config.xml
deleted file mode 100644
index e95b6ff..0000000
--- a/v7/appcompat/res/values-w480dp/config.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources>
- <bool name="abc_config_allowActionMenuItemTextWithIcon">true</bool>
-</resources>
diff --git a/v7/appcompat/res/values-w500dp/dimens.xml b/v7/appcompat/res/values-w500dp/dimens.xml
deleted file mode 100644
index dd6458b..0000000
--- a/v7/appcompat/res/values-w500dp/dimens.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <!-- The maximum number of action buttons that should be permitted within
- an action bar/action mode. This will be used to determine how many
- showAsAction="ifRoom" items can fit. "always" items can override this. -->
- <integer name="abc_max_action_buttons">4</integer>
-</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-w600dp/dimens.xml b/v7/appcompat/res/values-w600dp/dimens.xml
deleted file mode 100644
index 252ba6a..0000000
--- a/v7/appcompat/res/values-w600dp/dimens.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <!-- The maximum number of action buttons that should be permitted within
- an action bar/action mode. This will be used to determine how many
- showAsAction="ifRoom" items can fit. "always" items can override this. -->
- <integer name="abc_max_action_buttons">5</integer>
-
- <!-- Minimum width of the search view text entry area. -->
- <dimen name="abc_search_view_text_min_width">192dip</dimen>
-</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-w720dp/bools.xml b/v7/appcompat/res/values-w720dp/bools.xml
deleted file mode 100644
index 05c5aab..0000000
--- a/v7/appcompat/res/values-w720dp/bools.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <bool name="abc_action_bar_expanded_action_views_exclusive">false</bool>
-</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-xlarge-land/dimens.xml b/v7/appcompat/res/values-xlarge-land/dimens.xml
deleted file mode 100644
index dea6c74..0000000
--- a/v7/appcompat/res/values-xlarge-land/dimens.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
-
- <!-- Minimum width of the search view text entry area. -->
- <dimen name="abc_search_view_text_min_width">256dip</dimen>
-
-</resources>
diff --git a/v7/appcompat/res/values-xlarge/bools.xml b/v7/appcompat/res/values-xlarge/bools.xml
deleted file mode 100644
index 05c5aab..0000000
--- a/v7/appcompat/res/values-xlarge/bools.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <bool name="abc_action_bar_expanded_action_views_exclusive">false</bool>
-</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-xlarge/dimens.xml b/v7/appcompat/res/values-xlarge/dimens.xml
index 0dd244a..f0d560d 100644
--- a/v7/appcompat/res/values-xlarge/dimens.xml
+++ b/v7/appcompat/res/values-xlarge/dimens.xml
@@ -15,15 +15,6 @@
-->
<resources>
-
- <!-- The maximum number of action buttons that should be permitted within
- an action bar/action mode. This will be used to determine how many
- showAsAction="ifRoom" items can fit. "always" items can override this. -->
- <integer name="abc_max_action_buttons">5</integer>
-
- <!-- Minimum width of the search view text entry area. -->
- <dimen name="abc_search_view_text_min_width">192dip</dimen>
-
<item type="dimen" name="abc_dialog_fixed_width_major">50%</item>
<item type="dimen" name="abc_dialog_fixed_width_minor">70%</item>
<item type="dimen" name="abc_dialog_fixed_height_major">60%</item>
diff --git a/v7/appcompat/res/values/attrs.xml b/v7/appcompat/res/values/attrs.xml
index 83cb1b2..6c4254d 100644
--- a/v7/appcompat/res/values/attrs.xml
+++ b/v7/appcompat/res/values/attrs.xml
@@ -797,17 +797,39 @@
<attr name="title" />
<attr name="subtitle" />
<attr name="android:gravity" />
- <attr name="titleMargins" format="dimension" />
+ <!-- Specifies extra space on the left, start, right and end sides
+ of the toolbar's title. Margin values should be positive. -->
+ <attr name="titleMargin" format="dimension" />
+ <!-- Specifies extra space on the start side of the toolbar's title.
+ If both this attribute and titleMargin are specified, then this
+ attribute takes precedence. Margin values should be positive. -->
<attr name="titleMarginStart" format="dimension" />
+ <!-- Specifies extra space on the end side of the toolbar's title.
+ If both this attribute and titleMargin are specified, then this
+ attribute takes precedence. Margin values should be positive. -->
<attr name="titleMarginEnd" format="dimension" />
+ <!-- Specifies extra space on the top side of the toolbar's title.
+ If both this attribute and titleMargin are specified, then this
+ attribute takes precedence. Margin values should be positive. -->
<attr name="titleMarginTop" format="dimension" />
+ <!-- Specifies extra space on the bottom side of the toolbar's title.
+ If both this attribute and titleMargin are specified, then this
+ attribute takes precedence. Margin values should be positive. -->
<attr name="titleMarginBottom" format="dimension" />
+ <!-- {@deprecated Use titleMargin} -->
+ <attr name="titleMargins" format="dimension" />
<attr name="contentInsetStart" />
<attr name="contentInsetEnd" />
<attr name="contentInsetLeft" />
<attr name="contentInsetRight" />
<attr name="maxButtonHeight" format="dimension" />
-
+ <attr name="buttonGravity">
+ <!-- Push object to the top of its container, not changing its size. -->
+ <flag name="top" value="0x30" />
+ <!-- Push object to the bottom of its container, not changing its size. -->
+ <flag name="bottom" value="0x50" />
+ </attr>
+ <!-- Icon drawable to use for the collapse button. -->
<attr name="collapseIcon" format="reference" />
<!-- Text to set as the content description for the collapse button. -->
<attr name="collapseContentDescription" format="string" />
@@ -820,10 +842,6 @@
<!-- Text to set as the content description for the navigation button
located at the start of the toolbar. -->
<attr name="navigationContentDescription" format="string" />
-
- <!-- Allows us to read in the minHeight attr pre-v16 -->
- <attr name="android:minHeight" />
-
<!-- Drawable to set as the logo that appears at the starting side of
the Toolbar, just after the navigation button. -->
<attr name="logo" />
@@ -834,6 +852,7 @@
<attr name="titleTextColor" format="color" />
<!-- A color to apply to the subtitle string. -->
<attr name="subtitleTextColor" format="color" />
+ <attr name="android:minHeight" />
</declare-styleable>
<declare-styleable name="PopupWindowBackgroundState">
@@ -961,4 +980,13 @@
<attr name="allowStacking" format="boolean" />
</declare-styleable>
+ <!-- Attributes that can be assigned to a ColorStateList item. -->
+ <declare-styleable name="ColorStateListItem">
+ <!-- Base color for this state. -->
+ <attr name="android:color" />
+ <!-- Alpha multiplier applied to the base color. -->
+ <attr name="alpha" format="float" />
+ <attr name="android:alpha"/>
+ </declare-styleable>
+
</resources>
diff --git a/v7/appcompat/res/values/bools.xml b/v7/appcompat/res/values/bools.xml
index 3508cf3..6e1089f 100644
--- a/v7/appcompat/res/values/bools.xml
+++ b/v7/appcompat/res/values/bools.xml
@@ -17,12 +17,6 @@
<resources>
<bool name="abc_action_bar_embed_tabs">true</bool>
- <bool name="abc_action_bar_embed_tabs_pre_jb">false</bool>
- <bool name="abc_action_bar_expanded_action_views_exclusive">true</bool>
<bool name="abc_config_showMenuShortcutsWhenKeyboardPresent">false</bool>
-
- <!-- Whether to allow vertically stacked button bars. This is disabled for
- configurations with a small (e.g. less than 320dp) screen height. -->
- <bool name="abc_allow_stacked_button_bar">false</bool>
</resources>
diff --git a/v7/appcompat/res/values/config.xml b/v7/appcompat/res/values/config.xml
index e0c521b..6d986ea 100644
--- a/v7/appcompat/res/values/config.xml
+++ b/v7/appcompat/res/values/config.xml
@@ -17,11 +17,6 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
<resources>
- <!-- Whether action menu items should obey the "withText" showAsAction
- flag. This may be set to false for situations where space is
- extremely limited. -->
- <bool name="abc_config_allowActionMenuItemTextWithIcon">false</bool>
-
<!-- The maximum width we would prefer dialogs to be. 0 if there is no
maximum (let them grow as large as the screen). Actual values are
specified for -large and -xlarge configurations. -->
@@ -45,4 +40,4 @@
<integer name="status_bar_notification_info_maxnum">999</integer>
<integer name="cancel_button_image_alpha">127</integer>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/v7/appcompat/res/values/dimens.xml b/v7/appcompat/res/values/dimens.xml
index 37130e3..3eeeb5d 100644
--- a/v7/appcompat/res/values/dimens.xml
+++ b/v7/appcompat/res/values/dimens.xml
@@ -15,12 +15,6 @@
-->
<resources>
-
- <!-- The maximum number of action buttons that should be permitted within
- an action bar/action mode. This will be used to determine how many
- showAsAction="ifRoom" items can fit. "always" items can override this. -->
- <integer name="abc_max_action_buttons">2</integer>
-
<!-- Maximum width for a stacked action bar tab. This prevents
action bar tabs from becoming too wide on a wide screen when only
a few are present. -->
@@ -33,8 +27,6 @@
<dimen name="abc_panel_menu_list_width">296dp</dimen>
- <!-- Minimum width of the search view text entry area. -->
- <dimen name="abc_search_view_text_min_width">160dip</dimen>
<!-- Preferred width of the search view. -->
<dimen name="abc_search_view_preferred_width">320dip</dimen>
diff --git a/v7/appcompat/res/values/styles_base.xml b/v7/appcompat/res/values/styles_base.xml
index b119217..b1e20d4 100644
--- a/v7/appcompat/res/values/styles_base.xml
+++ b/v7/appcompat/res/values/styles_base.xml
@@ -300,8 +300,9 @@
<item name="titleTextAppearance">@style/TextAppearance.Widget.AppCompat.Toolbar.Title</item>
<item name="subtitleTextAppearance">@style/TextAppearance.Widget.AppCompat.Toolbar.Subtitle</item>
<item name="android:minHeight">?attr/actionBarSize</item>
- <item name="titleMargins">4dp</item>
+ <item name="titleMargin">4dp</item>
<item name="maxButtonHeight">56dp</item>
+ <item name="buttonGravity">top</item>
<item name="collapseIcon">?attr/homeAsUpIndicator</item>
<item name="collapseContentDescription">@string/abc_toolbar_collapse_description</item>
<item name="contentInsetStart">16dp</item>
diff --git a/v7/appcompat/res/values/themes_daynight.xml b/v7/appcompat/res/values/themes_daynight.xml
new file mode 100644
index 0000000..bc7001d
--- /dev/null
+++ b/v7/appcompat/res/values/themes_daynight.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+
+ <!-- Material theme (day/night vesion) for activities. -->
+ <style name="Theme.AppCompat.DayNight" parent="Theme.AppCompat.Light" />
+
+ <!-- Variant of AppCompat.DayNight that has a solid (opaque) action bar
+ with an inverse color profile. The dark action bar sharply stands out against
+ the light content (when applicable). -->
+ <style name="Theme.AppCompat.DayNight.DarkActionBar" parent="Theme.AppCompat.Light.DarkActionBar" />
+
+ <!-- Variant of AppCompat.DayNight with no action bar. -->
+ <style name="Theme.AppCompat.DayNight.NoActionBar" parent="Theme.AppCompat.Light.NoActionBar" />
+
+ <!-- Material theme (day/night vesion) for dialog windows and activities,
+ which is used by the {@code android.support.v7.app.Dialog} class. This changes
+ the window to be floating (not fill the entire screen), and puts a
+ frame around its contents. You can set this theme on an activity if
+ you would like to make an activity that looks like a Dialog. -->
+ <style name="Theme.AppCompat.DayNight.Dialog" parent="Theme.AppCompat.Light.Dialog" />
+
+ <!-- Variant of Theme.AppCompat.DayNight.Dialog that has a nice minimum width for
+ a regular dialog. -->
+ <style name="Theme.AppCompat.DayNight.Dialog.MinWidth" parent="Theme.AppCompat.Light.Dialog.MinWidth" />
+
+ <!-- Theme for a window that will be displayed either full-screen on
+ smaller screens (small, normal) or as a dialog on larger screens
+ (large, xlarge). -->
+ <style name="Theme.AppCompat.DayNight.DialogWhenLarge" parent="Theme.AppCompat.Light.DialogWhenLarge" />
+
+ <!-- Material user theme for alert dialog windows, which is used by the
+ {@code android.support.v7.app.AlertDialog} class. -->
+ <style name="Theme.AppCompat.DayNight.Dialog.Alert" parent="Theme.AppCompat.Light.Dialog.Alert" />
+
+</resources>
\ No newline at end of file
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
index 378c558..2ce12ce 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
@@ -62,6 +62,12 @@
}
@Override
+ public void setTheme(int resid) {
+ getDelegate().applyDayNight();
+ super.setTheme(resid);
+ }
+
+ @Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
getDelegate().onPostCreate(savedInstanceState);
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
index ef38511..79ca07f 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
@@ -76,6 +76,40 @@
static final String TAG = "AppCompatDelegate";
/**
+ * Mode which means to not use night mode, and therefore not use {@code night} qualified
+ * resources, regardless of the time.
+ *
+ * @see #setNightMode(int)
+ */
+ public static final int MODE_NIGHT_NO = 0;
+
+ /**
+ * Mode which means to always use night mode, and therefore use {@code night} qualified
+ * resources, regardless of the time.
+ *
+ * @see #setNightMode(int)
+ */
+ public static final int MODE_NIGHT_YES = 1;
+
+ /**
+ * Mode which means to use night mode when it is determined that it is night or not..
+ *
+ * <p>The calculation used to determine whether it is night or not makes use of the location
+ * APIs (if this app has the necessary permissions). This allows us to generate accurate
+ * sunrise and sunset times. If this app does not have permission to access the location APIs
+ * then we use hardcoded times which will be less accurate.</p>
+ *
+ * <p>This is the default mode.</p>
+ *
+ * @see #setNightMode(int)
+ */
+ public static final int MODE_NIGHT_AUTO = 2;
+
+ @IntDef({MODE_NIGHT_NO, MODE_NIGHT_YES, MODE_NIGHT_AUTO})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface NightMode {}
+
+ /**
* Flag for enabling the support Action Bar.
*
* <p>This is enabled by default for some devices. The Action Bar replaces the title bar and
@@ -330,4 +364,31 @@
*/
public abstract boolean isHandleNativeActionModesEnabled();
+ /**
+ * Allow AppCompat to apply the {@code night} and {@code notnight} resource qualifiers.
+ *
+ * <p>Doing this enables the
+ * {@link R.style#Theme_AppCompat_DayNight Theme.AppCompat.DayNight}
+ * family of themes to work, using the computed twilight to automatically select a dark or
+ * light theme.</p>
+ *
+ * <p>You can override the night mode using {@link #setNightMode(int)}.</p>
+ *
+ * <p>This only works on devices running
+ * {@link Build.VERSION_CODES#ICE_CREAM_SANDWICH ICE_CREAM_SANDWICH} and above.</p>
+ *
+ * @see #setNightMode(int)
+ */
+ public abstract void applyDayNight();
+
+ /**
+ * Override the night mode used when {@link #applyDayNight()} is called. This method only takes
+ * effect for those situtations where {@link #applyDayNight()} works.
+ *
+ * <p>This needs to be called before {@link #applyDayNight()}. Defaults to
+ * {@link #MODE_NIGHT_AUTO}.</p>
+ *
+ * @see #applyDayNight()
+ */
+ public abstract void setNightMode(@NightMode int mode);
}
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
index 394cf60..4eba34f 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
@@ -113,6 +113,11 @@
abstract boolean onKeyShortcut(int keyCode, KeyEvent event);
@Override
+ public void setNightMode(@NightMode int mode) {
+ // no-op
+ }
+
+ @Override
public final ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() {
return new ActionBarDrawableToggleImpl();
}
@@ -189,6 +194,11 @@
return false;
}
+ @Override
+ public void applyDayNight() {
+ // no-op on v7
+ }
+
final boolean isDestroyed() {
return mIsDestroyed;
}
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
index 35e7ca3..7d5b7d9 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
@@ -16,15 +16,23 @@
package android.support.v7.app;
+import android.app.UiModeManager;
import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Build;
import android.support.v7.view.SupportActionModeWrapper;
import android.view.ActionMode;
import android.view.Window;
class AppCompatDelegateImplV14 extends AppCompatDelegateImplV11 {
+ private TwilightManager mTwilightManager;
private boolean mHandleNativeActionModes = true; // defaults to true
+ @NightMode
+ private int mNightMode = MODE_NIGHT_AUTO;
+
AppCompatDelegateImplV14(Context context, Window window, AppCompatCallback callback) {
super(context, window, callback);
}
@@ -46,6 +54,90 @@
return mHandleNativeActionModes;
}
+ @Override
+ public void applyDayNight() {
+ if (!isSystemControllingNightMode()) {
+ // If the system is not controlling night mode, let's do it ourselves
+ switch (mNightMode) {
+ case MODE_NIGHT_AUTO:
+ // For auto, we need to check whether it's night or not
+ setDayNightUsingTwilight();
+ break;
+ default:
+ // Else, we'll set the value directly
+ setDayNightConfiguration(mNightMode);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void setNightMode(@NightMode int mode) {
+ mNightMode = mode;
+ }
+
+ /**
+ * If possible, updates the Activity's {@link uiMode} to match whether we are at night or not.
+ */
+ void setDayNightUsingTwilight() {
+ // If the system isn't controlling night mode, we'll do it ourselves
+ if (getTwilightManager().isNight()) {
+ // If we're at 'night', set the night mode
+ setDayNightConfiguration(MODE_NIGHT_YES);
+ } else {
+ // Else, set the day mode
+ setDayNightConfiguration(MODE_NIGHT_NO);
+ }
+ }
+
+ /**
+ * Updates the {@link Resources} configuration {@code uiMode} with the
+ * chosen {@code UI_MODE_NIGHT} value.
+ */
+ void setDayNightConfiguration(@NightMode int mode) {
+ final Resources res = mContext.getResources();
+ final Configuration conf = res.getConfiguration();
+ final int currentNightMode = conf.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ int newNightMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
+ switch (mode) {
+ case MODE_NIGHT_NO:
+ newNightMode = Configuration.UI_MODE_NIGHT_NO;
+ break;
+ case MODE_NIGHT_YES:
+ newNightMode = Configuration.UI_MODE_NIGHT_YES;
+ break;
+ }
+
+ if (currentNightMode != newNightMode) {
+ conf.uiMode = (conf.uiMode & ~Configuration.UI_MODE_NIGHT_MASK) | newNightMode;
+ res.updateConfiguration(conf, res.getDisplayMetrics());
+ }
+ }
+
+ /**
+ * Returns true if the system is controlling night mode.
+ */
+ private boolean isSystemControllingNightMode() {
+ final UiModeManager uiModeManager =
+ (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
+
+ if (Build.VERSION.SDK_INT < 23
+ && uiModeManager.getCurrentModeType() != Configuration.UI_MODE_TYPE_CAR) {
+ // Night mode only has an effect with car mode enabled on < API v23
+ return false;
+ }
+
+ return uiModeManager.getNightMode() != UiModeManager.MODE_NIGHT_NO;
+ }
+
+ private TwilightManager getTwilightManager() {
+ if (mTwilightManager == null) {
+ mTwilightManager = new TwilightManager(mContext);
+ }
+ return mTwilightManager;
+ }
+
class AppCompatWindowCallbackV14 extends AppCompatWindowCallbackBase {
AppCompatWindowCallbackV14(Window.Callback callback) {
super(callback);
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java
index 006827c..7b4c5ae 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java
@@ -57,4 +57,9 @@
return null;
}
}
+
+ @Override
+ public void applyDayNight() {
+ // no-op. Let the framework handle DayNight stuff
+ }
}
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDialog.java b/v7/appcompat/src/android/support/v7/app/AppCompatDialog.java
index 11688e6..5f13992 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDialog.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDialog.java
@@ -46,6 +46,9 @@
// To workaround this, we call onCreate(null) in the ctor, and then again as usual in
// onCreate().
getDelegate().onCreate(null);
+
+ // Apply AppCompat's DayNight resources if needed
+ getDelegate().applyDayNight();
}
protected AppCompatDialog(Context context, boolean cancelable,
diff --git a/v7/appcompat/src/android/support/v7/view/ActionBarPolicy.java b/v7/appcompat/src/android/support/v7/view/ActionBarPolicy.java
index 0e50cc1..af0736a 100644
--- a/v7/appcompat/src/android/support/v7/view/ActionBarPolicy.java
+++ b/v7/appcompat/src/android/support/v7/view/ActionBarPolicy.java
@@ -17,6 +17,7 @@
package android.support.v7.view;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Build;
@@ -42,8 +43,34 @@
mContext = context;
}
+ /**
+ * Returns the maximum number of action buttons that should be permitted within an action
+ * bar/action mode. This will be used to determine how many showAsAction="ifRoom" items can fit.
+ * "always" items can override this.
+ */
public int getMaxActionButtons() {
- return mContext.getResources().getInteger(R.integer.abc_max_action_buttons);
+ final Configuration config = mContext.getResources().getConfiguration();
+ final int width = config.screenWidthDp;
+ final int height = config.screenHeightDp;
+ final int smallest;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
+ smallest = config.smallestScreenWidthDp;
+ } else {
+ smallest = 0;
+ }
+ if (smallest > 600 || width > 600 || (width > 960 && height > 720)
+ || (width > 720 && height > 960)) {
+ // For values-w600dp, values-sw600dp and values-xlarge.
+ return 5;
+ } else if (width >= 500 || (width > 640 && height > 480) || (width > 480 && height > 640)) {
+ // For values-w500dp and values-large.
+ return 4;
+ } else if (width >= 360) {
+ // For values-w360dp.
+ return 3;
+ } else {
+ return 2;
+ }
}
public boolean showsOverflowMenuButton() {
@@ -66,7 +93,11 @@
// The embedded tabs policy changed in Jellybean; give older apps the old policy
// so they get what they expect.
- return mContext.getResources().getBoolean(R.bool.abc_action_bar_embed_tabs_pre_jb);
+ final Configuration configuration = mContext.getResources().getConfiguration();
+ final int width = configuration.screenWidthDp;
+ final int height = configuration.screenHeightDp;
+ return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE ||
+ width >= 480 || (width >= 640 && height >= 480);
}
public int getTabContainerHeight() {
diff --git a/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItemView.java b/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItemView.java
index 0b80cf2..c03226c 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItemView.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItemView.java
@@ -71,8 +71,7 @@
public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final Resources res = context.getResources();
- mAllowTextWithIcon = res.getBoolean(
- R.bool.abc_config_allowActionMenuItemTextWithIcon);
+ mAllowTextWithIcon = shouldAllowTextWithIcon();
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.ActionMenuItemView, defStyle, 0);
mMinWidth = a.getDimensionPixelSize(
@@ -93,11 +92,22 @@
super.onConfigurationChanged(newConfig);
}
- mAllowTextWithIcon = getContext().getResources().getBoolean(
- R.bool.abc_config_allowActionMenuItemTextWithIcon);
+ mAllowTextWithIcon = shouldAllowTextWithIcon();
updateTextButtonVisibility();
}
+ /**
+ * Whether action menu items should obey the "withText" showAsAction flag. This may be set to
+ * false for situations where space is extremely limited. -->
+ */
+ private boolean shouldAllowTextWithIcon() {
+ final Configuration configuration = getContext().getResources().getConfiguration();
+ final int width = configuration.screenWidthDp;
+ final int height = configuration.screenHeightDp;
+ return width >= 480 || (width >= 640 && height >= 480)
+ || configuration.orientation == Configuration.ORIENTATION_LANDSCAPE;
+ }
+
@Override
public void setPadding(int l, int t, int r, int b) {
mSavedPaddingLeft = l;
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java b/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
index e0e1a83..7f2a6b6 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
@@ -132,8 +132,7 @@
public void onConfigurationChanged(Configuration newConfig) {
if (!mMaxItemsSet) {
- mMaxItems = mContext.getResources().getInteger(
- R.integer.abc_max_action_buttons);
+ mMaxItems = ActionBarPolicy.get(mContext).getMaxActionButtons();
}
if (mMenu != null) {
mMenu.onItemsChanged(true);
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatBackgroundHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatBackgroundHelper.java
index 90d823c..4750792 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatBackgroundHelper.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatBackgroundHelper.java
@@ -17,7 +17,6 @@
package android.support.v7.widget;
import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewCompat;
@@ -40,7 +39,7 @@
}
void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
- TypedArray a = mView.getContext().obtainStyledAttributes(attrs,
+ TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
R.styleable.ViewBackgroundHelper, defStyleAttr, 0);
try {
if (a.hasValue(R.styleable.ViewBackgroundHelper_android_background)) {
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatColorStateListInflater.java b/v7/appcompat/src/android/support/v7/widget/AppCompatColorStateListInflater.java
new file mode 100644
index 0000000..3024280
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatColorStateListInflater.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.widget;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.graphics.ColorUtils;
+import android.support.v7.appcompat.R;
+import android.util.AttributeSet;
+import android.util.StateSet;
+import android.util.Xml;
+
+import java.io.IOException;
+
+class AppCompatColorStateListInflater {
+
+ private static final int DEFAULT_COLOR = Color.RED;
+
+ /**
+ * Creates a ColorStateList from an XML document using given a set of
+ * {@link Resources} and a {@link Theme}.
+ *
+ * @param r Resources against which the ColorStateList should be inflated.
+ * @param parser Parser for the XML document defining the ColorStateList.
+ * @param theme Optional theme to apply to the color state list, may be
+ * {@code null}.
+ * @return A new color state list.
+ */
+ @NonNull
+ public static ColorStateList createFromXml(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @Nullable Resources.Theme theme) throws XmlPullParserException, IOException {
+ final AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ // Seek parser to start tag.
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ throw new XmlPullParserException("No start tag found");
+ }
+
+ return createFromXmlInner(r, parser, attrs, theme);
+ }
+
+ /**
+ * Create from inside an XML document. Called on a parser positioned at a
+ * tag in an XML document, tries to create a ColorStateList from that tag.
+ *
+ * @throws XmlPullParserException if the current tag is not <selector>
+ * @return A new color state list for the current tag.
+ */
+ @NonNull
+ private static ColorStateList createFromXmlInner(@NonNull Resources r,
+ @NonNull XmlPullParser parser, @NonNull AttributeSet attrs,
+ @Nullable Resources.Theme theme)
+ throws XmlPullParserException, IOException {
+ final String name = parser.getName();
+ if (!name.equals("selector")) {
+ throw new XmlPullParserException(
+ parser.getPositionDescription() + ": invalid color state list tag " + name);
+ }
+
+ return inflate(r, parser, attrs, theme);
+ }
+
+ /**
+ * Fill in this object based on the contents of an XML "selector" element.
+ */
+ private static ColorStateList inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Resources.Theme theme)
+ throws XmlPullParserException, IOException {
+ final int innerDepth = parser.getDepth() + 1;
+ int depth;
+ int type;
+ int defaultColor = DEFAULT_COLOR;
+
+ int[][] stateSpecList = new int[20][];
+ int[] colorList = new int[stateSpecList.length];
+ int listSize = 0;
+
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
+ if (type != XmlPullParser.START_TAG || depth > innerDepth
+ || !parser.getName().equals("item")) {
+ continue;
+ }
+
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorStateListItem);
+ final int baseColor = a.getColor(R.styleable.ColorStateListItem_android_color,
+ Color.MAGENTA);
+
+ float alphaMod = 1.0f;
+ if (a.hasValue(R.styleable.ColorStateListItem_android_alpha)) {
+ alphaMod = a.getFloat(R.styleable.ColorStateListItem_android_alpha, alphaMod);
+ } else if (a.hasValue(R.styleable.ColorStateListItem_alpha)) {
+ alphaMod = a.getFloat(R.styleable.ColorStateListItem_alpha, alphaMod);
+ }
+
+ a.recycle();
+
+ // Parse all unrecognized attributes as state specifiers.
+ int j = 0;
+ final int numAttrs = attrs.getAttributeCount();
+ int[] stateSpec = new int[numAttrs];
+ for (int i = 0; i < numAttrs; i++) {
+ final int stateResId = attrs.getAttributeNameResource(i);
+ if (stateResId != android.R.attr.color && stateResId != android.R.attr.alpha
+ && stateResId != R.attr.alpha) {
+ // Unrecognized attribute, add to state set
+ stateSpec[j++] = attrs.getAttributeBooleanValue(i, false)
+ ? stateResId : -stateResId;
+ }
+ }
+ stateSpec = StateSet.trimStateSet(stateSpec, j);
+
+ // Apply alpha modulation. If we couldn't resolve the color or
+ // alpha yet, the default values leave us enough information to
+ // modulate again during applyTheme().
+ final int color = modulateColorAlpha(baseColor, alphaMod);
+ if (listSize == 0 || stateSpec.length == 0) {
+ defaultColor = color;
+ }
+
+ colorList = GrowingArrayUtils.append(colorList, listSize, color);
+ stateSpecList = GrowingArrayUtils.append(stateSpecList, listSize, stateSpec);
+ listSize++;
+ }
+
+ int[] colors = new int[listSize];
+ int[][] stateSpecs = new int[listSize][];
+ System.arraycopy(colorList, 0, colors, 0, listSize);
+ System.arraycopy(stateSpecList, 0, stateSpecs, 0, listSize);
+
+ return new ColorStateList(stateSpecs, colors);
+ }
+
+ private static TypedArray obtainAttributes(Resources res, Resources.Theme theme,
+ AttributeSet set, int[] attrs) {
+ return theme == null ? res.obtainAttributes(set, attrs)
+ : theme.obtainStyledAttributes(set, attrs, 0, 0);
+ }
+
+ private static int modulateColorAlpha(int color, float alphaMod) {
+ return ColorUtils.setAlphaComponent(color, Math.round(Color.alpha(color) * alphaMod));
+ }
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java b/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
index 4fcaf13..e089c90 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
@@ -41,6 +41,7 @@
import java.util.ArrayList;
import java.util.WeakHashMap;
+import static android.support.v7.widget.ColorStateListUtils.getColorStateList;
import static android.support.v7.widget.ThemeUtils.getDisabledThemeAttrColor;
import static android.support.v7.widget.ThemeUtils.getThemeAttrColor;
import static android.support.v7.widget.ThemeUtils.getThemeAttrColorStateList;
@@ -301,11 +302,11 @@
if (tint == null) {
// ...if the cache did not contain a color state list, try and create one
if (resId == R.drawable.abc_edit_text_material) {
- tint = createEditTextColorStateList(context);
+ tint = getColorStateList(context, R.color.abc_tint_edittext);
} else if (resId == R.drawable.abc_switch_track_mtrl_alpha) {
- tint = createSwitchTrackColorStateList(context);
+ tint = getColorStateList(context, R.color.abc_tint_switch_track);
} else if (resId == R.drawable.abc_switch_thumb_material) {
- tint = createSwitchThumbColorStateList(context);
+ tint = getColorStateList(context, R.color.abc_tint_switch_thumb);
} else if (resId == R.drawable.abc_btn_default_mtrl_shape
|| resId == R.drawable.abc_btn_borderless_material) {
tint = createDefaultButtonColorStateList(context);
@@ -313,15 +314,15 @@
tint = createColoredButtonColorStateList(context);
} else if (resId == R.drawable.abc_spinner_mtrl_am_alpha
|| resId == R.drawable.abc_spinner_textfield_background_material) {
- tint = createSpinnerColorStateList(context);
+ tint = getColorStateList(context, R.color.abc_tint_spinner);
} else if (arrayContains(TINT_COLOR_CONTROL_NORMAL, resId)) {
tint = getThemeAttrColorStateList(context, R.attr.colorControlNormal);
} else if (arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)) {
- tint = createDefaultColorStateList(context);
+ tint = getColorStateList(context, R.color.abc_tint_default);
} else if (arrayContains(TINT_CHECKABLE_BUTTON_LIST, resId)) {
- tint = createCheckableButtonColorStateList(context);
+ tint = getColorStateList(context, R.color.abc_tint_btn_checkable);
} else if (resId == R.drawable.abc_seekbar_thumb_material) {
- tint = createSeekbarThumbColorStateList(context);
+ tint = getColorStateList(context, R.color.abc_tint_seek_thumb);
}
if (tint != null) {
@@ -352,164 +353,6 @@
themeTints.append(resId, tintList);
}
- private ColorStateList createDefaultColorStateList(Context context) {
- /**
- * Generate the default color state list which uses the colorControl attributes.
- * Order is important here. The default enabled state needs to go at the bottom.
- */
-
- final int colorControlNormal = getThemeAttrColor(context, R.attr.colorControlNormal);
- final int colorControlActivated = getThemeAttrColor(context, R.attr.colorControlActivated);
-
- final int[][] states = new int[7][];
- final int[] colors = new int[7];
- int i = 0;
-
- // Disabled state
- states[i] = ThemeUtils.DISABLED_STATE_SET;
- colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
- i++;
-
- states[i] = ThemeUtils.FOCUSED_STATE_SET;
- colors[i] = colorControlActivated;
- i++;
-
- states[i] = ThemeUtils.ACTIVATED_STATE_SET;
- colors[i] = colorControlActivated;
- i++;
-
- states[i] = ThemeUtils.PRESSED_STATE_SET;
- colors[i] = colorControlActivated;
- i++;
-
- states[i] = ThemeUtils.CHECKED_STATE_SET;
- colors[i] = colorControlActivated;
- i++;
-
- states[i] = ThemeUtils.SELECTED_STATE_SET;
- colors[i] = colorControlActivated;
- i++;
-
- // Default enabled state
- states[i] = ThemeUtils.EMPTY_STATE_SET;
- colors[i] = colorControlNormal;
- i++;
-
- return new ColorStateList(states, colors);
- }
-
- private ColorStateList createCheckableButtonColorStateList(Context context) {
- final int[][] states = new int[3][];
- final int[] colors = new int[3];
- int i = 0;
-
- // Disabled state
- states[i] = ThemeUtils.DISABLED_STATE_SET;
- colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
- i++;
-
- states[i] = ThemeUtils.CHECKED_STATE_SET;
- colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
- i++;
-
- // Default enabled state
- states[i] = ThemeUtils.EMPTY_STATE_SET;
- colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
- i++;
-
- return new ColorStateList(states, colors);
- }
-
- private ColorStateList createSwitchTrackColorStateList(Context context) {
- final int[][] states = new int[3][];
- final int[] colors = new int[3];
- int i = 0;
-
- // Disabled state
- states[i] = ThemeUtils.DISABLED_STATE_SET;
- colors[i] = getThemeAttrColor(context, android.R.attr.colorForeground, 0.1f);
- i++;
-
- states[i] = ThemeUtils.CHECKED_STATE_SET;
- colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated, 0.3f);
- i++;
-
- // Default enabled state
- states[i] = ThemeUtils.EMPTY_STATE_SET;
- colors[i] = getThemeAttrColor(context, android.R.attr.colorForeground, 0.3f);
- i++;
-
- return new ColorStateList(states, colors);
- }
-
- private ColorStateList createSwitchThumbColorStateList(Context context) {
- final int[][] states = new int[3][];
- final int[] colors = new int[3];
- int i = 0;
-
- final ColorStateList thumbColor = getThemeAttrColorStateList(context,
- R.attr.colorSwitchThumbNormal);
-
- if (thumbColor != null && thumbColor.isStateful()) {
- // If colorSwitchThumbNormal is a valid ColorStateList, extract the default and
- // disabled colors from it
-
- // Disabled state
- states[i] = ThemeUtils.DISABLED_STATE_SET;
- colors[i] = thumbColor.getColorForState(states[i], 0);
- i++;
-
- states[i] = ThemeUtils.CHECKED_STATE_SET;
- colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
- i++;
-
- // Default enabled state
- states[i] = ThemeUtils.EMPTY_STATE_SET;
- colors[i] = thumbColor.getDefaultColor();
- i++;
- } else {
- // Else we'll use an approximation using the default disabled alpha
-
- // Disabled state
- states[i] = ThemeUtils.DISABLED_STATE_SET;
- colors[i] = getDisabledThemeAttrColor(context, R.attr.colorSwitchThumbNormal);
- i++;
-
- states[i] = ThemeUtils.CHECKED_STATE_SET;
- colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
- i++;
-
- // Default enabled state
- states[i] = ThemeUtils.EMPTY_STATE_SET;
- colors[i] = getThemeAttrColor(context, R.attr.colorSwitchThumbNormal);
- i++;
- }
-
- return new ColorStateList(states, colors);
- }
-
- private ColorStateList createEditTextColorStateList(Context context) {
- final int[][] states = new int[3][];
- final int[] colors = new int[3];
- int i = 0;
-
- // Disabled state
- states[i] = ThemeUtils.DISABLED_STATE_SET;
- colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
- i++;
-
- states[i] = ThemeUtils.NOT_PRESSED_OR_FOCUSED_STATE_SET;
- colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
- i++;
-
- // Default enabled state
- states[i] = ThemeUtils.EMPTY_STATE_SET;
- colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
- i++;
-
- return new ColorStateList(states, colors);
- }
-
private ColorStateList createDefaultButtonColorStateList(Context context) {
return createButtonColorStateList(context, R.attr.colorButtonNormal);
}
@@ -547,44 +390,6 @@
return new ColorStateList(states, colors);
}
- private ColorStateList createSpinnerColorStateList(Context context) {
- final int[][] states = new int[3][];
- final int[] colors = new int[3];
- int i = 0;
-
- // Disabled state
- states[i] = ThemeUtils.DISABLED_STATE_SET;
- colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
- i++;
-
- states[i] = ThemeUtils.NOT_PRESSED_OR_FOCUSED_STATE_SET;
- colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
- i++;
-
- states[i] = ThemeUtils.EMPTY_STATE_SET;
- colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
- i++;
-
- return new ColorStateList(states, colors);
- }
-
- private ColorStateList createSeekbarThumbColorStateList(Context context) {
- final int[][] states = new int[2][];
- final int[] colors = new int[2];
- int i = 0;
-
- // Disabled state
- states[i] = ThemeUtils.DISABLED_STATE_SET;
- colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlActivated);
- i++;
-
- states[i] = ThemeUtils.EMPTY_STATE_SET;
- colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
- i++;
-
- return new ColorStateList(states, colors);
- }
-
private static class ColorFilterLruCache extends LruCache<Integer, PorterDuffColorFilter> {
public ColorFilterLruCache(int maxSize) {
diff --git a/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java b/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java
index 20fa89d..f24f282 100644
--- a/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java
+++ b/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java
@@ -30,6 +30,9 @@
* @hide
*/
public class ButtonBarLayout extends LinearLayout {
+ // Whether to allow vertically stacked button bars. This is disabled for
+ // configurations with a small (e.g. less than 320dp) screen height. -->
+ private static final int ALLOW_STACKING_MIN_HEIGHT_DP = 320;
/** Whether the current configuration allows stacking. */
private boolean mAllowStacking;
@@ -37,8 +40,12 @@
public ButtonBarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
+ final boolean allowStackingDefault =
+ context.getResources().getConfiguration().screenHeightDp
+ >= ALLOW_STACKING_MIN_HEIGHT_DP;
final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonBarLayout);
- mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking, false);
+ mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking,
+ allowStackingDefault);
ta.recycle();
}
@@ -107,4 +114,4 @@
private boolean isStacked() {
return getOrientation() == LinearLayout.VERTICAL;
}
-}
\ No newline at end of file
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/ColorStateListUtils.java b/v7/appcompat/src/android/support/v7/widget/ColorStateListUtils.java
new file mode 100644
index 0000000..d5ff6c9
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/ColorStateListUtils.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.widget;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.os.Build;
+import android.support.annotation.ColorRes;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+import android.util.Log;
+import android.util.TypedValue;
+
+class ColorStateListUtils {
+
+ private static final String LOG_TAG = "ColorStateListUtils";
+ private static final ThreadLocal<TypedValue> TL_TYPED_VALUE = new ThreadLocal<>();
+
+ private ColorStateListUtils() {}
+
+ /**
+ * Returns the {@link ColorStateList} from the given resource. The resource can include
+ * themeable attributes.
+ */
+ static ColorStateList getColorStateList(@NonNull Context context, @ColorRes int resId) {
+ if (Build.VERSION.SDK_INT >= 23) {
+ // On M+ we can use the framework
+ return context.getColorStateList(resId);
+ }
+ // Before that, we'll try and inflate it manually
+ final ColorStateList csl = inflateColorStateList(context, resId);
+ if (csl != null) {
+ return csl;
+ }
+ // If we reach here then we couldn't inflate it, so let the framework handle it
+ return ContextCompat.getColorStateList(context, resId);
+ }
+
+ /**
+ * Inflates a {@link ColorStateList} from resources, honouring theme attributes.
+ */
+ private static ColorStateList inflateColorStateList(Context context, int resId) {
+ if (isColorInt(context, resId)) {
+ // The resource is a color int, we can't handle it so return null
+ return null;
+ }
+
+ final Resources r = context.getResources();
+ final XmlPullParser xml = r.getXml(resId);
+ try {
+ return AppCompatColorStateListInflater.createFromXml(r, xml, context.getTheme());
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Failed to inflate ColorStateList, leaving it to the framework", e);
+ }
+ return null;
+ }
+
+ static boolean isColorInt(Context context, int resId) {
+ final Resources r = context.getResources();
+
+ final TypedValue value = getTypedValue();
+ r.getValue(resId, value, true);
+
+ return value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+ && value.type <= TypedValue.TYPE_LAST_COLOR_INT;
+ }
+
+ private static TypedValue getTypedValue() {
+ TypedValue tv = TL_TYPED_VALUE.get();
+ if (tv == null) {
+ tv = new TypedValue();
+ TL_TYPED_VALUE.set(tv);
+ }
+ return tv;
+ }
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/GrowingArrayUtils.java b/v7/appcompat/src/android/support/v7/widget/GrowingArrayUtils.java
new file mode 100644
index 0000000..1d97400
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/GrowingArrayUtils.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.widget;
+
+import java.lang.reflect.Array;
+
+/**
+ * A helper class that aims to provide comparable growth performance to ArrayList, but on primitive
+ * arrays. Common array operations are implemented for efficient use in dynamic containers.
+ *
+ * All methods in this class assume that the length of an array is equivalent to its capacity and
+ * NOT the number of elements in the array. The current size of the array is always passed in as a
+ * parameter.
+ */
+class GrowingArrayUtils {
+
+ /**
+ * Appends an element to the end of the array, growing the array if there is no more room.
+ * @param array The array to which to append the element. This must NOT be null.
+ * @param currentSize The number of elements in the array. Must be less than or equal to
+ * array.length.
+ * @param element The element to append.
+ * @return the array to which the element was appended. This may be different than the given
+ * array.
+ */
+ public static <T> T[] append(T[] array, int currentSize, T element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 > array.length) {
+ T[] newArray = (T[]) Array.newInstance(array.getClass().getComponentType(),
+ growSize(currentSize));
+ System.arraycopy(array, 0, newArray, 0, currentSize);
+ array = newArray;
+ }
+ array[currentSize] = element;
+ return array;
+ }
+
+ /**
+ * Primitive int version of {@link #append(Object[], int, Object)}.
+ */
+ public static int[] append(int[] array, int currentSize, int element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 > array.length) {
+ int[] newArray = new int[growSize(currentSize)];
+ System.arraycopy(array, 0, newArray, 0, currentSize);
+ array = newArray;
+ }
+ array[currentSize] = element;
+ return array;
+ }
+
+ /**
+ * Primitive long version of {@link #append(Object[], int, Object)}.
+ */
+ public static long[] append(long[] array, int currentSize, long element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 > array.length) {
+ long[] newArray = new long[growSize(currentSize)];
+ System.arraycopy(array, 0, newArray, 0, currentSize);
+ array = newArray;
+ }
+ array[currentSize] = element;
+ return array;
+ }
+
+ /**
+ * Primitive boolean version of {@link #append(Object[], int, Object)}.
+ */
+ public static boolean[] append(boolean[] array, int currentSize, boolean element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 > array.length) {
+ boolean[] newArray = new boolean[growSize(currentSize)];
+ System.arraycopy(array, 0, newArray, 0, currentSize);
+ array = newArray;
+ }
+ array[currentSize] = element;
+ return array;
+ }
+
+ /**
+ * Inserts an element into the array at the specified index, growing the array if there is no
+ * more room.
+ *
+ * @param array The array to which to append the element. Must NOT be null.
+ * @param currentSize The number of elements in the array. Must be less than or equal to
+ * array.length.
+ * @param element The element to insert.
+ * @return the array to which the element was appended. This may be different than the given
+ * array.
+ */
+ public static <T> T[] insert(T[] array, int currentSize, int index, T element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 <= array.length) {
+ System.arraycopy(array, index, array, index + 1, currentSize - index);
+ array[index] = element;
+ return array;
+ }
+
+ T[] newArray = (T[]) Array.newInstance(array.getClass().getComponentType(),
+ growSize(currentSize));
+ System.arraycopy(array, 0, newArray, 0, index);
+ newArray[index] = element;
+ System.arraycopy(array, index, newArray, index + 1, array.length - index);
+ return newArray;
+ }
+
+ /**
+ * Primitive int version of {@link #insert(Object[], int, int, Object)}.
+ */
+ public static int[] insert(int[] array, int currentSize, int index, int element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 <= array.length) {
+ System.arraycopy(array, index, array, index + 1, currentSize - index);
+ array[index] = element;
+ return array;
+ }
+
+ int[] newArray = new int[growSize(currentSize)];
+ System.arraycopy(array, 0, newArray, 0, index);
+ newArray[index] = element;
+ System.arraycopy(array, index, newArray, index + 1, array.length - index);
+ return newArray;
+ }
+
+ /**
+ * Primitive long version of {@link #insert(Object[], int, int, Object)}.
+ */
+ public static long[] insert(long[] array, int currentSize, int index, long element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 <= array.length) {
+ System.arraycopy(array, index, array, index + 1, currentSize - index);
+ array[index] = element;
+ return array;
+ }
+
+ long[] newArray = new long[growSize(currentSize)];
+ System.arraycopy(array, 0, newArray, 0, index);
+ newArray[index] = element;
+ System.arraycopy(array, index, newArray, index + 1, array.length - index);
+ return newArray;
+ }
+
+ /**
+ * Primitive boolean version of {@link #insert(Object[], int, int, Object)}.
+ */
+ public static boolean[] insert(boolean[] array, int currentSize, int index, boolean element) {
+ assert currentSize <= array.length;
+
+ if (currentSize + 1 <= array.length) {
+ System.arraycopy(array, index, array, index + 1, currentSize - index);
+ array[index] = element;
+ return array;
+ }
+
+ boolean[] newArray = new boolean[growSize(currentSize)];
+ System.arraycopy(array, 0, newArray, 0, index);
+ newArray[index] = element;
+ System.arraycopy(array, index, newArray, index + 1, array.length - index);
+ return newArray;
+ }
+
+ /**
+ * Given the current size of an array, returns an ideal size to which the array should grow.
+ * This is typically double the given size, but should not be relied upon to do so in the
+ * future.
+ */
+ public static int growSize(int currentSize) {
+ return currentSize <= 4 ? 8 : currentSize * 2;
+ }
+
+ // Uninstantiable
+ private GrowingArrayUtils() {}
+}
\ No newline at end of file
diff --git a/v7/appcompat/src/android/support/v7/widget/SearchView.java b/v7/appcompat/src/android/support/v7/widget/SearchView.java
index e4644d0..0b59400 100644
--- a/v7/appcompat/src/android/support/v7/widget/SearchView.java
+++ b/v7/appcompat/src/android/support/v7/widget/SearchView.java
@@ -48,7 +48,9 @@
import android.text.TextWatcher;
import android.text.style.ImageSpan;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -1648,6 +1650,14 @@
mThreshold = getThreshold();
}
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ DisplayMetrics metrics = getResources().getDisplayMetrics();
+ setMinWidth((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ getSearchViewTextMinWidthDp(), metrics));
+ }
+
void setSearchView(SearchView searchView) {
mSearchView = searchView;
}
@@ -1743,6 +1753,23 @@
}
return super.onKeyPreIme(keyCode, event);
}
+
+ /**
+ * Get minimum width of the search view text entry area.
+ */
+ private int getSearchViewTextMinWidthDp() {
+ final Configuration configuration = getResources().getConfiguration();
+ final int width = configuration.screenWidthDp;
+ final int height = configuration.screenHeightDp;
+ final int orientation = configuration.orientation;
+ if (width >= 960 && height >= 720
+ && orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ return 256;
+ } else if (width >= 600 || (width >= 640 && height >= 480)) {
+ return 192;
+ };
+ return 160;
+ }
}
private static class AutoCompleteTextViewReflector {
@@ -1821,4 +1848,4 @@
imm.showSoftInput(view, flags);
}
}
-}
\ No newline at end of file
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/ThemeUtils.java b/v7/appcompat/src/android/support/v7/widget/ThemeUtils.java
index 1c0151b..02ee49a 100644
--- a/v7/appcompat/src/android/support/v7/widget/ThemeUtils.java
+++ b/v7/appcompat/src/android/support/v7/widget/ThemeUtils.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
import android.graphics.Color;
import android.support.v4.graphics.ColorUtils;
import android.util.TypedValue;
@@ -61,7 +60,7 @@
public static int getThemeAttrColor(Context context, int attr) {
TEMP_ARRAY[0] = attr;
- TypedArray a = context.obtainStyledAttributes(null, TEMP_ARRAY);
+ TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, null, TEMP_ARRAY);
try {
return a.getColor(0, 0);
} finally {
@@ -71,7 +70,7 @@
public static ColorStateList getThemeAttrColorStateList(Context context, int attr) {
TEMP_ARRAY[0] = attr;
- TypedArray a = context.obtainStyledAttributes(null, TEMP_ARRAY);
+ TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, null, TEMP_ARRAY);
try {
return a.getColorStateList(0);
} finally {
diff --git a/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java b/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
index 50e1046..32749f3 100644
--- a/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
+++ b/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
@@ -118,6 +118,16 @@
}
public ColorStateList getColorStateList(int index) {
+ if (mWrapped.hasValue(index)) {
+ final int resourceId = mWrapped.getResourceId(index, 0);
+ if (resourceId != 0) {
+ final ColorStateList value =
+ ColorStateListUtils.getColorStateList(mContext, resourceId);
+ if (value != null) {
+ return value;
+ }
+ }
+ }
return mWrapped.getColorStateList(index);
}
diff --git a/v7/appcompat/src/android/support/v7/widget/Toolbar.java b/v7/appcompat/src/android/support/v7/widget/Toolbar.java
index a5f9612..eae7722 100644
--- a/v7/appcompat/src/android/support/v7/widget/Toolbar.java
+++ b/v7/appcompat/src/android/support/v7/widget/Toolbar.java
@@ -105,6 +105,32 @@
* <p>In modern Android UIs developers should lean more on a visually distinct color scheme for
* toolbars than on their application icon. The use of application icon plus title as a standard
* layout is discouraged on API 21 devices and newer.</p>
+ *
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_buttonGravity
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_collapseContentDescription
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_collapseIcon
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetEnd
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetLeft
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetRight
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetStart
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_android_gravity
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_logo
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_logoDescription
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_maxButtonHeight
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationContentDescription
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationIcon
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_popupTheme
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_subtitle
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_subtitleTextAppearance
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_subtitleTextColor
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_title
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMargin
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginBottom
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginEnd
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginStart
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginTop
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleTextAppearance
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleTextColor
*/
public class Toolbar extends ViewGroup {
private static final String TAG = "Toolbar";
@@ -206,9 +232,15 @@
mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0);
mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0);
mGravity = a.getInteger(R.styleable.Toolbar_android_gravity, mGravity);
- mButtonGravity = Gravity.TOP;
- mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom =
- a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, 0);
+ mButtonGravity = a.getInteger(R.styleable.Toolbar_buttonGravity, Gravity.TOP);
+
+ // First read the correct attribute
+ int titleMargin = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargin, 0);
+ if (a.hasValue(R.styleable.Toolbar_titleMargins)) {
+ // Now read the deprecated attribute, if it has a value
+ titleMargin = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, titleMargin);
+ }
+ mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom = titleMargin;
final int marginStart = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginStart, -1);
if (marginStart >= 0) {
@@ -263,6 +295,7 @@
if (!TextUtils.isEmpty(subtitle)) {
setSubtitle(subtitle);
}
+
// Set the default context, since setPopupTheme() may be a no-op.
mPopupContext = getContext();
setPopupTheme(a.getResourceId(R.styleable.Toolbar_popupTheme, 0));
@@ -325,6 +358,116 @@
return mPopupTheme;
}
+ /**
+ * Sets the title margin.
+ *
+ * @param start the starting title margin in pixels
+ * @param top the top title margin in pixels
+ * @param end the ending title margin in pixels
+ * @param bottom the bottom title margin in pixels
+ * @see #getTitleMarginStart()
+ * @see #getTitleMarginTop()
+ * @see #getTitleMarginEnd()
+ * @see #getTitleMarginBottom()
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMargin
+ */
+ public void setTitleMargin(int start, int top, int end, int bottom) {
+ mTitleMarginStart = start;
+ mTitleMarginTop = top;
+ mTitleMarginEnd = end;
+ mTitleMarginBottom = bottom;
+
+ requestLayout();
+ }
+
+ /**
+ * @return the starting title margin in pixels
+ * @see #setTitleMarginStart(int)
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginStart
+ */
+ public int getTitleMarginStart() {
+ return mTitleMarginStart;
+ }
+
+ /**
+ * Sets the starting title margin in pixels.
+ *
+ * @param margin the starting title margin in pixels
+ * @see #getTitleMarginStart()
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginStart
+ */
+ public void setTitleMarginStart(int margin) {
+ mTitleMarginStart = margin;
+
+ requestLayout();
+ }
+
+ /**
+ * @return the top title margin in pixels
+ * @see #setTitleMarginTop(int)
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginTop
+ */
+ public int getTitleMarginTop() {
+ return mTitleMarginTop;
+ }
+
+ /**
+ * Sets the top title margin in pixels.
+ *
+ * @param margin the top title margin in pixels
+ * @see #getTitleMarginTop()
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginTop
+ */
+ public void setTitleMarginTop(int margin) {
+ mTitleMarginTop = margin;
+
+ requestLayout();
+ }
+
+ /**
+ * @return the ending title margin in pixels
+ * @see #setTitleMarginEnd(int)
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginEnd
+ */
+ public int getTitleMarginEnd() {
+ return mTitleMarginEnd;
+ }
+
+ /**
+ * Sets the ending title margin in pixels.
+ *
+ * @param margin the ending title margin in pixels
+ * @see #getTitleMarginEnd()
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginEnd
+ */
+ public void setTitleMarginEnd(int margin) {
+ mTitleMarginEnd = margin;
+
+ requestLayout();
+ }
+
+ /**
+ * @return the bottom title margin in pixels
+ * @see #setTitleMarginBottom(int)
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginBottom
+ */
+ public int getTitleMarginBottom() {
+ return mTitleMarginBottom;
+ }
+
+ /**
+ * Sets the bottom title margin in pixels.
+ *
+ * @param margin the bottom title margin in pixels
+ * @see #getTitleMarginBottom()
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginBottom
+ */
+ public void setTitleMarginBottom(int margin) {
+ mTitleMarginBottom = margin;
+
+ requestLayout();
+ }
+
public void onRtlPropertiesChanged(int layoutDirection) {
if (Build.VERSION.SDK_INT >= 17) {
super.onRtlPropertiesChanged(layoutDirection);
@@ -721,6 +864,8 @@
* as screen readers or tooltips.
*
* @return The navigation button's content description
+ *
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationContentDescription
*/
@Nullable
public CharSequence getNavigationContentDescription() {
@@ -734,6 +879,8 @@
*
* @param resId Resource ID of a content description string to set, or 0 to
* clear the description
+ *
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationContentDescription
*/
public void setNavigationContentDescription(@StringRes int resId) {
setNavigationContentDescription(resId != 0 ? getContext().getText(resId) : null);
@@ -746,6 +893,8 @@
*
* @param description Content description to set, or <code>null</code> to
* clear the content description
+ *
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationContentDescription
*/
public void setNavigationContentDescription(@Nullable CharSequence description) {
if (!TextUtils.isEmpty(description)) {
@@ -767,6 +916,8 @@
* tooltips.</p>
*
* @param resId Resource ID of a drawable to set
+ *
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationIcon
*/
public void setNavigationIcon(@DrawableRes int resId) {
setNavigationIcon(mDrawableManager.getDrawable(getContext(), resId));
@@ -783,6 +934,8 @@
* tooltips.</p>
*
* @param icon Drawable to set, may be null to clear the icon
+ *
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationIcon
*/
public void setNavigationIcon(@Nullable Drawable icon) {
if (icon != null) {
@@ -803,6 +956,8 @@
* Return the current drawable used as the navigation icon.
*
* @return The navigation icon drawable
+ *
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationIcon
*/
@Nullable
public Drawable getNavigationIcon() {
diff --git a/v7/mediarouter/api/current.txt b/v7/mediarouter/api/current.txt
index 92bbbb3..c027380 100644
--- a/v7/mediarouter/api/current.txt
+++ b/v7/mediarouter/api/current.txt
@@ -182,6 +182,7 @@
public abstract class AppCompatDelegate {
method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+ method public abstract void applyDayNight();
method public static android.support.v7.app.AppCompatDelegate create(android.app.Activity, android.support.v7.app.AppCompatCallback);
method public static android.support.v7.app.AppCompatDelegate create(android.app.Dialog, android.support.v7.app.AppCompatCallback);
method public abstract android.view.View createView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
@@ -203,12 +204,16 @@
method public abstract void setContentView(int);
method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public abstract void setHandleNativeActionModesEnabled(boolean);
+ method public abstract void setNightMode(int);
method public abstract void setSupportActionBar(android.support.v7.widget.Toolbar);
method public abstract void setTitle(java.lang.CharSequence);
method public abstract android.support.v7.view.ActionMode startSupportActionMode(android.support.v7.view.ActionMode.Callback);
field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
field public static final int FEATURE_SUPPORT_ACTION_BAR = 108; // 0x6c
field public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY = 109; // 0x6d
+ field public static final int MODE_NIGHT_AUTO = 2; // 0x2
+ field public static final int MODE_NIGHT_NO = 0; // 0x0
+ field public static final int MODE_NIGHT_YES = 1; // 0x1
}
public class AppCompatDialog extends android.app.Dialog implements android.support.v7.app.AppCompatCallback {
diff --git a/v7/mediarouter/res/values-fr/strings.xml b/v7/mediarouter/res/values-fr/strings.xml
index de94f18..6e14ccc 100644
--- a/v7/mediarouter/res/values-fr/strings.xml
+++ b/v7/mediarouter/res/values-fr/strings.xml
@@ -19,7 +19,7 @@
<string name="mr_system_route_name" msgid="5441529851481176817">"Système"</string>
<string name="mr_user_route_category_name" msgid="7498112907524977311">"Appareils"</string>
<string name="mr_button_content_description" msgid="3698378085901466129">"Icône Cast"</string>
- <string name="mr_chooser_title" msgid="414301941546135990">"Diffuser sur"</string>
+ <string name="mr_chooser_title" msgid="414301941546135990">"Caster sur"</string>
<string name="mr_chooser_searching" msgid="6349900579507521956">"Recherche d\'appareils en cours…"</string>
<string name="mr_controller_disconnect" msgid="1227264889412989580">"Déconnecter"</string>
<string name="mr_controller_stop" msgid="4570331844078181931">"Arrêter de diffuser"</string>
diff --git a/v7/mediarouter/res/values-hy-rAM/strings.xml b/v7/mediarouter/res/values-hy-rAM/strings.xml
index 85bd488..b05c17c 100644
--- a/v7/mediarouter/res/values-hy-rAM/strings.xml
+++ b/v7/mediarouter/res/values-hy-rAM/strings.xml
@@ -19,7 +19,7 @@
<string name="mr_system_route_name" msgid="5441529851481176817">"Համակարգ"</string>
<string name="mr_user_route_category_name" msgid="7498112907524977311">"Սարքեր"</string>
<string name="mr_button_content_description" msgid="3698378085901466129">"Հեռարձակման կոճակ"</string>
- <string name="mr_chooser_title" msgid="414301941546135990">"Հեռարձակել դեպի"</string>
+ <string name="mr_chooser_title" msgid="414301941546135990">"Ընտրեք սարքը"</string>
<string name="mr_chooser_searching" msgid="6349900579507521956">"Սարքերի որոնում"</string>
<string name="mr_controller_disconnect" msgid="1227264889412989580">"Անջատել"</string>
<string name="mr_controller_stop" msgid="4570331844078181931">"Դադարեցնել հեռարձակումը"</string>
diff --git a/v7/mediarouter/res/values-ja/strings.xml b/v7/mediarouter/res/values-ja/strings.xml
index ea8d838..ffe9054 100644
--- a/v7/mediarouter/res/values-ja/strings.xml
+++ b/v7/mediarouter/res/values-ja/strings.xml
@@ -19,7 +19,7 @@
<string name="mr_system_route_name" msgid="5441529851481176817">"システム"</string>
<string name="mr_user_route_category_name" msgid="7498112907524977311">"端末"</string>
<string name="mr_button_content_description" msgid="3698378085901466129">"キャストアイコン"</string>
- <string name="mr_chooser_title" msgid="414301941546135990">"キャスト先"</string>
+ <string name="mr_chooser_title" msgid="414301941546135990">"キャストするデバイス"</string>
<string name="mr_chooser_searching" msgid="6349900579507521956">"端末を検索しています"</string>
<string name="mr_controller_disconnect" msgid="1227264889412989580">"接続を解除"</string>
<string name="mr_controller_stop" msgid="4570331844078181931">"キャストを停止"</string>
diff --git a/v7/mediarouter/res/values-ko/strings.xml b/v7/mediarouter/res/values-ko/strings.xml
index 2b3455f..fc1dbac 100644
--- a/v7/mediarouter/res/values-ko/strings.xml
+++ b/v7/mediarouter/res/values-ko/strings.xml
@@ -19,7 +19,7 @@
<string name="mr_system_route_name" msgid="5441529851481176817">"시스템"</string>
<string name="mr_user_route_category_name" msgid="7498112907524977311">"기기"</string>
<string name="mr_button_content_description" msgid="3698378085901466129">"전송 버튼"</string>
- <string name="mr_chooser_title" msgid="414301941546135990">"전송 대상"</string>
+ <string name="mr_chooser_title" msgid="414301941546135990">"전송할 기기"</string>
<string name="mr_chooser_searching" msgid="6349900579507521956">"기기를 찾는 중"</string>
<string name="mr_controller_disconnect" msgid="1227264889412989580">"연결 해제"</string>
<string name="mr_controller_stop" msgid="4570331844078181931">"전송 중지"</string>
diff --git a/v7/preference/Android.mk b/v7/preference/Android.mk
index e2db0f1..4fb0f03 100644
--- a/v7/preference/Android.mk
+++ b/v7/preference/Android.mk
@@ -14,14 +14,14 @@
LOCAL_PATH := $(call my-dir)
-# Build the resources using the current SDK version.
+# Build the resources and resource constants using the current SDK version.
# We do this here because the final static library must be compiled with an older
# SDK version than the resources. The resources library and the R class that it
# contains will not be linked into the final static library.
include $(CLEAR_VARS)
LOCAL_MODULE := android-support-v7-preference-res
LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
+LOCAL_SRC_FILES := $(call all-java-files-under, constants)
LOCAL_RESOURCE_DIR := \
frameworks/support/v7/appcompat/res \
frameworks/support/v7/recyclerview/res \
diff --git a/v7/preference/build.gradle b/v7/preference/build.gradle
index a0681ed..db64731 100644
--- a/v7/preference/build.gradle
+++ b/v7/preference/build.gradle
@@ -30,6 +30,7 @@
sourceSets {
main.manifest.srcFile 'AndroidManifest.xml'
main.java.srcDir 'src'
+ main.java.srcDir 'constants'
main.res.srcDir 'res'
main.assets.srcDir 'assets'
main.resources.srcDir 'src'
diff --git a/v7/preference/constants/android/support/v7/preference/AndroidResources.java b/v7/preference/constants/android/support/v7/preference/AndroidResources.java
new file mode 100644
index 0000000..d529ad2
--- /dev/null
+++ b/v7/preference/constants/android/support/v7/preference/AndroidResources.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.support.v7.preference;
+
+public class AndroidResources {
+
+ public static final int ANDROID_R_ICON_FRAME = android.R.id.icon_frame;
+ public static final int ANDROID_R_LIST_CONTAINER = android.R.id.list_container;
+ public static final int ANDROID_R_SWITCH_WIDGET = android.R.id.switch_widget;
+ public static final int ANDROID_R_PREFERENCE_FRAGMENT_STYLE
+ = android.R.attr.preferenceFragmentStyle;
+ public static final int ANDROID_R_EDITTEXT_PREFERENCE_STYLE
+ = android.R.attr.editTextPreferenceStyle;
+
+}
diff --git a/v7/preference/res/layout/preference_list_fragment.xml b/v7/preference/res/layout/preference_list_fragment.xml
index 44c5438..fe11fb2 100644
--- a/v7/preference/res/layout/preference_list_fragment.xml
+++ b/v7/preference/res/layout/preference_list_fragment.xml
@@ -21,7 +21,7 @@
android:layout_width="match_parent" >
<FrameLayout
- android:id="@+id/list_container"
+ android:id="@android:id/list_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
diff --git a/v7/preference/res/layout/preference_widget_checkbox.xml b/v7/preference/res/layout/preference_widget_checkbox.xml
index e53b7df..cc99443 100644
--- a/v7/preference/res/layout/preference_widget_checkbox.xml
+++ b/v7/preference/res/layout/preference_widget_checkbox.xml
@@ -18,7 +18,7 @@
<!-- Layout used by CheckBoxPreference for the checkbox style. This is inflated
inside android.R.layout.preference. -->
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/checkbox"
+ android:id="@android:id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
diff --git a/v7/preference/res/values/attrs.xml b/v7/preference/res/values/attrs.xml
index 5d39b63..310a444 100644
--- a/v7/preference/res/values/attrs.xml
+++ b/v7/preference/res/values/attrs.xml
@@ -245,4 +245,11 @@
<attr name="android:maxHeight" />
</declare-styleable>
+ <!-- Used to access some android attrs -->
+ <declare-styleable name="BackgroundStyle">
+ <attr name="android:selectableItemBackground" />
+ <!-- Need a non-android: attr here so that gradle doesn't remove it -->
+ <attr name="selectableItemBackground" />
+ </declare-styleable>
+
</resources>
diff --git a/v7/preference/src/android/support/v7/preference/CheckBoxPreference.java b/v7/preference/src/android/support/v7/preference/CheckBoxPreference.java
index 61eb13c..94f0751 100644
--- a/v7/preference/src/android/support/v7/preference/CheckBoxPreference.java
+++ b/v7/preference/src/android/support/v7/preference/CheckBoxPreference.java
@@ -61,7 +61,8 @@
}
public CheckBoxPreference(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.checkBoxPreferenceStyle);
+ this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.checkBoxPreferenceStyle,
+ android.R.attr.checkBoxPreferenceStyle));
}
public CheckBoxPreference(Context context) {
@@ -72,7 +73,7 @@
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- View checkboxView = holder.findViewById(R.id.checkbox);
+ View checkboxView = holder.findViewById(android.R.id.checkbox);
if (checkboxView != null && checkboxView instanceof Checkable) {
((Checkable) checkboxView).setChecked(mChecked);
}
diff --git a/v7/preference/src/android/support/v7/preference/DialogPreference.java b/v7/preference/src/android/support/v7/preference/DialogPreference.java
index bf0f4ef..158accb 100644
--- a/v7/preference/src/android/support/v7/preference/DialogPreference.java
+++ b/v7/preference/src/android/support/v7/preference/DialogPreference.java
@@ -90,7 +90,8 @@
}
public DialogPreference(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.dialogPreferenceStyle);
+ this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.dialogPreferenceStyle,
+ android.R.attr.dialogPreferenceStyle));
}
public DialogPreference(Context context) {
diff --git a/v7/preference/src/android/support/v7/preference/EditTextPreference.java b/v7/preference/src/android/support/v7/preference/EditTextPreference.java
index c4941e6..423a0c9 100644
--- a/v7/preference/src/android/support/v7/preference/EditTextPreference.java
+++ b/v7/preference/src/android/support/v7/preference/EditTextPreference.java
@@ -20,6 +20,7 @@
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.v4.content.res.TypedArrayUtils;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.EditText;
@@ -46,7 +47,8 @@
}
public EditTextPreference(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.editTextPreferenceStyle);
+ this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.editTextPreferenceStyle,
+ AndroidResources.ANDROID_R_EDITTEXT_PREFERENCE_STYLE));
}
public EditTextPreference(Context context) {
diff --git a/v7/preference/src/android/support/v7/preference/ListPreference.java b/v7/preference/src/android/support/v7/preference/ListPreference.java
index 848cfad..4507b26 100644
--- a/v7/preference/src/android/support/v7/preference/ListPreference.java
+++ b/v7/preference/src/android/support/v7/preference/ListPreference.java
@@ -74,7 +74,8 @@
}
public ListPreference(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.dialogPreferenceStyle);
+ this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.dialogPreferenceStyle,
+ android.R.attr.dialogPreferenceStyle));
}
public ListPreference(Context context) {
diff --git a/v7/preference/src/android/support/v7/preference/Preference.java b/v7/preference/src/android/support/v7/preference/Preference.java
index f386592..84550cf 100644
--- a/v7/preference/src/android/support/v7/preference/Preference.java
+++ b/v7/preference/src/android/support/v7/preference/Preference.java
@@ -272,7 +272,7 @@
mShouldDisableView =
TypedArrayUtils.getBoolean(a, R.styleable.Preference_shouldDisableView,
- R.styleable.Preference_shouldDisableView, true);
+ R.styleable.Preference_android_shouldDisableView, true);
a.recycle();
}
@@ -316,7 +316,8 @@
* @see #Preference(Context, AttributeSet, int)
*/
public Preference(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.preferenceStyle);
+ this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle,
+ android.R.attr.preferenceStyle));
}
/**
@@ -506,7 +507,10 @@
imageView.setVisibility(mIcon != null ? View.VISIBLE : View.GONE);
}
- final View imageFrame = holder.findViewById(R.id.icon_frame);
+ View imageFrame = holder.findViewById(R.id.icon_frame);
+ if (imageFrame == null) {
+ imageFrame = holder.findViewById(AndroidResources.ANDROID_R_ICON_FRAME);
+ }
if (imageFrame != null) {
imageFrame.setVisibility(mIcon != null ? View.VISIBLE : View.GONE);
}
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceCategory.java b/v7/preference/src/android/support/v7/preference/PreferenceCategory.java
index bed7f7e..10a0753 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceCategory.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceCategory.java
@@ -17,6 +17,7 @@
package android.support.v7.preference;
import android.content.Context;
+import android.support.v4.content.res.TypedArrayUtils;
import android.util.AttributeSet;
/**
@@ -43,7 +44,8 @@
}
public PreferenceCategory(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.preferenceCategoryStyle);
+ this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceCategoryStyle,
+ android.R.attr.preferenceCategoryStyle));
}
public PreferenceCategory(Context context) {
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java b/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java
index 221d3ea..368a3a4 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java
@@ -257,10 +257,10 @@
final View view = themedInflater.inflate(mLayoutResId, container, false);
- final View rawListContainer = view.findViewById(R.id.list_container);
+ final View rawListContainer = view.findViewById(AndroidResources.ANDROID_R_LIST_CONTAINER);
if (!(rawListContainer instanceof ViewGroup)) {
- throw new RuntimeException("Content has view with id attribute 'R.id.list_container' "
- + "that is not a ViewGroup class");
+ throw new RuntimeException("Content has view with id attribute "
+ + "'android.R.id.list_container' that is not a ViewGroup class");
}
final ViewGroup listContainer = (ViewGroup) rawListContainer;
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java b/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java
index c4ccc66..fefab02 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java
@@ -16,6 +16,8 @@
package android.support.v7.preference;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
@@ -271,8 +273,18 @@
public PreferenceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final PreferenceLayout pl = mPreferenceLayouts.get(viewType);
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+ TypedArray a
+ = parent.getContext().obtainStyledAttributes(null, R.styleable.BackgroundStyle);
+ Drawable background
+ = a.getDrawable(R.styleable.BackgroundStyle_android_selectableItemBackground);
+ if (background == null) {
+ background = parent.getContext().getResources()
+ .getDrawable(android.R.drawable.list_selector_background);
+ }
+ a.recycle();
final View view = inflater.inflate(pl.resId, parent, false);
+ view.setBackgroundDrawable(background);
final ViewGroup widgetFrame = (ViewGroup) view.findViewById(android.R.id.widget_frame);
if (widgetFrame != null) {
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceScreen.java b/v7/preference/src/android/support/v7/preference/PreferenceScreen.java
index e64ebcc..ee8b056 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceScreen.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceScreen.java
@@ -17,6 +17,7 @@
package android.support.v7.preference;
import android.content.Context;
+import android.support.v4.content.res.TypedArrayUtils;
import android.util.AttributeSet;
/**
@@ -79,7 +80,8 @@
* @hide-
*/
public PreferenceScreen(Context context, AttributeSet attrs) {
- super(context, attrs, R.attr.preferenceScreenStyle);
+ super(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceScreenStyle,
+ android.R.attr.preferenceScreenStyle));
}
@Override
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java b/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java
index f9eaf22..156d5a8 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java
@@ -39,6 +39,8 @@
mCachedViews.put(android.R.id.summary, itemView.findViewById(android.R.id.summary));
mCachedViews.put(android.R.id.icon, itemView.findViewById(android.R.id.icon));
mCachedViews.put(R.id.icon_frame, itemView.findViewById(R.id.icon_frame));
+ mCachedViews.put(AndroidResources.ANDROID_R_ICON_FRAME,
+ itemView.findViewById(AndroidResources.ANDROID_R_ICON_FRAME));
}
/**
diff --git a/v8/renderscript/Android.mk b/v8/renderscript/Android.mk
index 520c8a1..d077d72 100644
--- a/v8/renderscript/Android.mk
+++ b/v8/renderscript/Android.mk
@@ -24,7 +24,7 @@
LOCAL_CFLAGS += -std=c++11
LOCAL_MODULE := android-support-v8-renderscript
-LOCAL_SDK_VERSION := 22
+LOCAL_SDK_VERSION := 21
LOCAL_SRC_FILES := $(call all-java-files-under, java/src)
LOCAL_JAVA_LIBRARIES := android-support-annotations
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java b/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
index 5dcb50b..5624218 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
@@ -49,13 +49,17 @@
@SuppressWarnings({"UnusedDeclaration", "deprecation"})
static final boolean LOG_ENABLED = false;
static final int SUPPORT_LIB_API = 23;
+ static final int SUPPORT_LIB_VERSION = 2301;
static private ArrayList<RenderScript> mProcessContextList = new ArrayList<RenderScript>();
private boolean mIsProcessContext = false;
+ private boolean mEnableMultiInput = false;
+ // TODO: Update to set to true at the correct API level when reduce is added.
+ private boolean mEnableReduce = false;
+
private int mContextFlags = 0;
private int mContextSdkVersion = 0;
-
private Context mApplicationContext;
private String mNativeLibDir;
@@ -97,7 +101,7 @@
static Object lock = new Object();
// Non-threadsafe functions.
- native boolean nLoadSO(boolean useNative, int deviceApi);
+ native boolean nLoadSO(boolean useNative, int deviceApi, String libPath);
native boolean nLoadIOSO();
native long nDeviceCreate();
native void nDeviceDestroy(long dev);
@@ -165,49 +169,42 @@
if (sNative == 1) {
// Workarounds that may disable thunking go here
- ApplicationInfo info = null;
+ ApplicationInfo info;
try {
info = ctx.getPackageManager().getApplicationInfo(ctx.getPackageName(),
PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
// assume no workarounds needed
+ return true;
+ }
+ long minorVersion = 0;
+
+ // load minorID from reflection
+ try {
+ Class<?> javaRS = Class.forName("android.renderscript.RenderScript");
+ Method getMinorID = javaRS.getDeclaredMethod("getMinorID");
+ minorVersion = ((java.lang.Long)getMinorID.invoke(null)).longValue();
+ } catch (Exception e) {
+ // minor version remains 0 on devices with no possible WARs
}
- if (info != null) {
- long minorVersion = 0;
- // load minorID from reflection
- try {
- Class<?> javaRS = Class.forName("android.renderscript.RenderScript");
- if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
- Method getMinorID = javaRS.getDeclaredMethod("getMinorID");
- minorVersion = ((java.lang.Long)getMinorID.invoke(null)).longValue();
- } else {
- //For M+
- Method getMinorVersion = javaRS.getDeclaredMethod("getMinorVersion");
- minorVersion = ((java.lang.Long)getMinorVersion.invoke(null)).longValue();
- }
- } catch (Exception e) {
- // minor version remains 0 on devices with no possible WARs
- }
-
- if (info.metaData != null) {
- // asynchronous teardown: minor version 1+
- if (info.metaData.getBoolean("com.android.support.v8.renderscript.EnableAsyncTeardown") == true) {
- if (minorVersion == 0) {
- sNative = 0;
- }
- }
-
- // blur issues on some drivers with 4.4
- if (info.metaData.getBoolean("com.android.support.v8.renderscript.EnableBlurWorkaround") == true) {
- if (android.os.Build.VERSION.SDK_INT <= 19) {
- //android.util.Log.e("rs", "war on");
- sNative = 0;
- }
+ if (info.metaData != null) {
+ // asynchronous teardown: minor version 1+
+ if (info.metaData.getBoolean("com.android.support.v8.renderscript.EnableAsyncTeardown") == true) {
+ if (minorVersion == 0) {
+ sNative = 0;
}
}
- // end of workarounds
+
+ // blur issues on some drivers with 4.4
+ if (info.metaData.getBoolean("com.android.support.v8.renderscript.EnableBlurWorkaround") == true) {
+ if (android.os.Build.VERSION.SDK_INT <= 19) {
+ //android.util.Log.e("rs", "war on");
+ sNative = 0;
+ }
+ }
}
+ // end of workarounds
}
}
@@ -651,6 +648,31 @@
}
}
+ native void rsnScriptForEach(long con, long id, int slot, long[] ains,
+ long aout, byte[] params, int[] limits);
+
+ synchronized void nScriptForEach(long id, int slot, long[] ains, long aout,
+ byte[] params, int[] limits) {
+ if (!mEnableMultiInput) {
+ Log.e(LOG_TAG, "Multi-input kernels are not supported, please change targetSdkVersion to >= 23");
+ throw new RSRuntimeException("Multi-input kernels are not supported before API 23)");
+ }
+ validate();
+ rsnScriptForEach(mContext, id, slot, ains, aout, params, limits);
+ }
+
+ native void rsnScriptReduce(long con, long id, int slot, long ain, long aout,
+ int[] limits);
+ synchronized void nScriptReduce(long id, int slot, long ain, long aout, int[] limits) {
+ if (!mEnableReduce) {
+ // TODO: Update to include the API level when reduce is added.
+ Log.e(LOG_TAG, "Reduce kernels are not supported");
+ throw new RSRuntimeException("Reduce kernels are not supported");
+ }
+ validate();
+ rsnScriptReduce(mContext, id, slot, ain, aout, limits);
+ }
+
native void rsnScriptInvokeV(long con, long id, int slot, byte[] params, boolean mUseInc);
synchronized void nScriptInvokeV(long id, int slot, byte[] params, boolean mUseInc) {
validate();
@@ -749,7 +771,7 @@
Log.e(LOG_TAG, "Error loading RS Compat library for Incremental Intrinsic Support: " + e);
throw new RSRuntimeException("Error loading RS Compat library for Incremental Intrinsic Support: " + e);
}
- if (!nIncLoadSO(SUPPORT_LIB_API)) {
+ if (!nIncLoadSO(SUPPORT_LIB_API, mNativeLibDir + "/libRSSupport.so")) {
throw new RSRuntimeException("Error loading libRSSupport library for Incremental Intrinsic Support");
}
mIncLoaded = true;
@@ -945,7 +967,7 @@
// Additional Entry points For inc libRSSupport
- native boolean nIncLoadSO(int deviceApi);
+ native boolean nIncLoadSO(int deviceApi, String libPath);
native long nIncDeviceCreate();
native void nIncDeviceDestroy(long dev);
// Methods below are wrapped to protect the non-threadsafe
@@ -996,10 +1018,10 @@
validate();
return rsnIncTypeCreate(mIncCon, eid, x, y, z, mips, faces, yuv);
}
- native long rsnIncAllocationCreateTyped(long con, long incCon, long alloc, long type);
- synchronized long nIncAllocationCreateTyped(long alloc, long type) {
+ native long rsnIncAllocationCreateTyped(long con, long incCon, long alloc, long type, int xBytesSize);
+ synchronized long nIncAllocationCreateTyped(long alloc, long type, int xBytesSize) {
validate();
- return rsnIncAllocationCreateTyped(mContext, mIncCon, alloc, type);
+ return rsnIncAllocationCreateTyped(mContext, mIncCon, alloc, type, xBytesSize);
}
long mDev;
@@ -1185,6 +1207,14 @@
}
}
+ void validateObject(BaseObj o) {
+ if (o != null) {
+ if (o.mRS != this) {
+ throw new RSIllegalArgumentException("Attempting to use an object across contexts.");
+ }
+ }
+ }
+
void validate() {
if (mContext == 0) {
throw new RSInvalidStateException("Calling RS with no Context active.");
@@ -1293,7 +1323,10 @@
mContextType = ContextType.NORMAL;
if (ctx != null) {
mApplicationContext = ctx.getApplicationContext();
- mNativeLibDir = mApplicationContext.getApplicationInfo().nativeLibraryDir;
+ // Only set mNativeLibDir for API 9+.
+ if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.FROYO) {
+ mNativeLibDir = mApplicationContext.getApplicationInfo().nativeLibraryDir;
+ }
}
mIncDev = 0;
mIncCon = 0;
@@ -1339,12 +1372,18 @@
sUseGCHooks = false;
}
try {
- System.loadLibrary("rsjni");
+ // For API 9+, always use the absolute path of librsjni.so
+ // Bug: 25226912
+ if (rs.mNativeLibDir != null) {
+ System.load(rs.mNativeLibDir + "/librsjni.so");
+ } else {
+ System.loadLibrary("rsjni");
+ }
sInitialized = true;
sPointerSize = rsnSystemGetPointerSize();
} catch (UnsatisfiedLinkError e) {
Log.e(LOG_TAG, "Error loading RS jni library: " + e);
- throw new RSRuntimeException("Error loading RS jni library: " + e);
+ throw new RSRuntimeException("Error loading RS jni library: " + e + " Support lib API: " + SUPPORT_LIB_VERSION);
}
}
}
@@ -1359,24 +1398,37 @@
useIOlib = true;
}
+ // The target API level used to init dispatchTable.
int dispatchAPI = sdkVersion;
if (sdkVersion < android.os.Build.VERSION.SDK_INT) {
// If the device API is higher than target API level, init dispatch table based on device API.
dispatchAPI = android.os.Build.VERSION.SDK_INT;
}
- if (!rs.nLoadSO(useNative, dispatchAPI)) {
+
+ String rssupportPath = null;
+ // For API 9+, always use the absolute path of libRSSupport.so
+ // Bug: 25226912
+ if (rs.mNativeLibDir != null) {
+ rssupportPath = rs.mNativeLibDir + "/libRSSupport.so";
+ }
+ if (!rs.nLoadSO(useNative, dispatchAPI, rssupportPath)) {
if (useNative) {
android.util.Log.v(LOG_TAG, "Unable to load libRS.so, falling back to compat mode");
useNative = false;
}
try {
- System.loadLibrary("RSSupport");
+ if (rs.mNativeLibDir != null) {
+ System.load(rssupportPath);
+ } else {
+ System.loadLibrary("RSSupport");
+ }
} catch (UnsatisfiedLinkError e) {
- Log.e(LOG_TAG, "Error loading RS Compat library: " + e);
- throw new RSRuntimeException("Error loading RS Compat library: " + e);
+ Log.e(LOG_TAG, "Error loading RS Compat library: " + e + " Support lib version: " + SUPPORT_LIB_VERSION);
+ throw new RSRuntimeException("Error loading RS Compat library: " + e + " Support lib version: " + SUPPORT_LIB_VERSION);
}
- if (!rs.nLoadSO(false, dispatchAPI)) {
- throw new RSRuntimeException("Error loading libRSSupport library");
+ if (!rs.nLoadSO(false, dispatchAPI, rssupportPath)) {
+ Log.e(LOG_TAG, "Error loading RS Compat library: nLoadSO() failed; Support lib version: " + SUPPORT_LIB_VERSION);
+ throw new RSRuntimeException("Error loading libRSSupport library, Support lib version: " + SUPPORT_LIB_VERSION);
}
}
@@ -1392,6 +1444,18 @@
}
}
+ // For old APIs with dlopen bug, need to load blas lib in Java first.
+ // Only try load to blasV8 when the desired API level includes IntrinsicBLAS.
+ if (dispatchAPI >= 23) {
+ // Enable multi-input kernels only when diapatchAPI is M+.
+ rs.mEnableMultiInput = true;
+ try {
+ System.loadLibrary("blasV8");
+ } catch (UnsatisfiedLinkError e) {
+ Log.v(LOG_TAG, "Unable to load BLAS lib, ONLY BNNM will be supported: " + e);
+ }
+ }
+
rs.mDev = rs.nDeviceCreate();
rs.mContext = rs.nContextCreate(rs.mDev, 0, sdkVersion, ct.mID, rs.mNativeLibDir);
rs.mContextType = ct;
@@ -1595,9 +1659,21 @@
}
nContextDeinitToClient(mContext);
mMessageThread.mRun = false;
- try {
- mMessageThread.join();
- } catch(InterruptedException e) {
+
+ // Wait for mMessageThread to join. Try in a loop, in case this thread gets interrupted
+ // during the wait. If interrupted, set the "interrupted" status of the current thread.
+ boolean hasJoined = false, interrupted = false;
+ while (!hasJoined) {
+ try {
+ mMessageThread.join();
+ hasJoined = true;
+ } catch (InterruptedException e) {
+ interrupted = true;
+ }
+ }
+ if (interrupted) {
+ Log.v(LOG_TAG, "Interrupted during wait for MessageThread to join");
+ Thread.currentThread().interrupt();
}
nContextDestroy();
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Script.java b/v8/renderscript/java/src/android/support/v8/renderscript/Script.java
index cacd146..7cb52ae 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Script.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/Script.java
@@ -44,9 +44,11 @@
long dInType = 0;
long dummyAlloc = 0;
if (ain != null) {
- dInElement = ain.getType().getElement().getDummyElement(mRS);
- dInType = ain.getType().getDummyType(mRS, dInElement);
- dummyAlloc = mRS.nIncAllocationCreateTyped(ain.getID(mRS), dInType);
+ Type inType = ain.getType();
+ dInElement = inType.getElement().getDummyElement(mRS);
+ dInType = inType.getDummyType(mRS, dInElement);
+ int xBytesSize = inType.getX() * inType.getElement().getBytesSize();
+ dummyAlloc = mRS.nIncAllocationCreateTyped(ain.getID(mRS), dInType, xBytesSize);
ain.setIncAllocID(dummyAlloc);
}
@@ -317,6 +319,101 @@
/**
* Only intended for use by generated reflected code.
*
+ * @hide
+ */
+ protected void forEach(int slot, Allocation[] ains, Allocation aout,
+ FieldPacker v) {
+ forEach(slot, ains, aout, v, null);
+ }
+
+ /**
+ * Only intended for use by generated reflected code.
+ *
+ * @hide
+ */
+ protected void forEach(int slot, Allocation[] ains, Allocation aout,
+ FieldPacker v, LaunchOptions sc) {
+ // TODO: Is this necessary if nScriptForEach calls validate as well?
+ mRS.validate();
+ if (ains != null) {
+ for (Allocation ain : ains) {
+ mRS.validateObject(ain);
+ }
+ }
+ mRS.validateObject(aout);
+
+ if (ains == null && aout == null) {
+ throw new RSIllegalArgumentException(
+ "At least one of ain or aout is required to be non-null.");
+ }
+
+ long[] in_ids;
+ if (ains != null) {
+ in_ids = new long[ains.length];
+ for (int index = 0; index < ains.length; ++index) {
+ in_ids[index] = ains[index].getID(mRS);
+ }
+ } else {
+ in_ids = null;
+ }
+
+ long out_id = 0;
+ if (aout != null) {
+ out_id = aout.getID(mRS);
+ }
+
+ byte[] params = null;
+ if (v != null) {
+ params = v.getData();
+ }
+
+ int[] limits = null;
+ if (sc != null) {
+ limits = new int[6];
+
+ limits[0] = sc.xstart;
+ limits[1] = sc.xend;
+ limits[2] = sc.ystart;
+ limits[3] = sc.yend;
+ limits[4] = sc.zstart;
+ limits[5] = sc.zend;
+ }
+
+ mRS.nScriptForEach(getID(mRS), slot, in_ids, out_id, params, limits);
+ }
+
+ /**
+ * Only intended for use by generated reflected code.
+ *
+ * @hide
+ */
+ protected void reduce(int slot, Allocation ain, Allocation aout, LaunchOptions sc) {
+ mRS.validate();
+ mRS.validateObject(ain);
+ mRS.validateObject(aout);
+
+ if (ain == null || aout == null) {
+ throw new RSIllegalArgumentException(
+ "Both ain and aout are required to be non-null.");
+ }
+
+ long in_id = ain.getID(mRS);
+ long out_id = aout.getID(mRS);
+
+ int[] limits = null;
+ if (sc != null) {
+ limits = new int[2];
+
+ limits[0] = sc.xstart;
+ limits[1] = sc.xend;
+ }
+
+ mRS.nScriptReduce(getID(mRS), slot, in_id, out_id, limits);
+ }
+
+ /**
+ * Only intended for use by generated reflected code.
+ *
* @param index
* @param v
*/
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java
index 15e8b55..065712a 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java
@@ -307,10 +307,10 @@
value = ((Long)obj).longValue();
size = 8;
} else if (obj instanceof Float) {
- value = ((Float)obj).longValue();
+ value = Float.floatToRawIntBits(((Float)obj).floatValue());
size = 4;
} else if (obj instanceof Double) {
- value = ((Double)obj).longValue();
+ value = Double.doubleToRawLongBits(((Double)obj).doubleValue());
size = 8;
}
}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java
index a5b046b..2d2923e 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java
@@ -97,7 +97,7 @@
* type.
*/
public void forEach(Allocation aout) {
- forEach(0, null, aout, null);
+ forEach(0, (Allocation) null, aout, null);
}
/**
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java
index ea45461..0d398b7 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java
@@ -115,7 +115,7 @@
* type.
*/
public void forEach(Allocation aout) {
- forEach(0, null, aout, null);
+ forEach(0, (Allocation) null, aout, null);
}
/**
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java
index bcd37f1..9dd9cba 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java
@@ -117,7 +117,7 @@
* type.
*/
public void forEach(Allocation aout) {
- forEach(0, null, aout, null);
+ forEach(0, (Allocation) null, aout, null);
}
/**
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java
index f3d0df7..6c84020 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java
@@ -74,7 +74,7 @@
* type.
*/
public void forEach(Allocation aout) {
- forEach(0, null, aout, null);
+ forEach(0, (Allocation) null, aout, null);
}
/**
diff --git a/v8/renderscript/jni/Android.mk b/v8/renderscript/jni/Android.mk
index d2af4a9..72f8720 100644
--- a/v8/renderscript/jni/Android.mk
+++ b/v8/renderscript/jni/Android.mk
@@ -50,6 +50,6 @@
LOCAL_MODULE_TAGS := optional
LOCAL_REQUIRED_MODULES := libRSSupport
-LOCAL_LDFLAGS += -ldl
+LOCAL_LDFLAGS += -ldl -llog
include $(BUILD_SHARED_LIBRARY)
diff --git a/v8/renderscript/jni/android_renderscript_RenderScript.cpp b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
index 9d69a2d..bf6e598 100644
--- a/v8/renderscript/jni/android_renderscript_RenderScript.cpp
+++ b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
@@ -30,6 +30,8 @@
//#define LOG_API ALOG
#define LOG_API(...)
+#define LOG_ERR(...) __android_log_print(ANDROID_LOG_ERROR, "RenderScript JNI", __VA_ARGS__);
+#define RS_JNI_VERSION 2301
#define NELEM(m) (sizeof(m) / sizeof((m)[0]))
@@ -269,23 +271,31 @@
// Incremental Support lib
static dispatchTable dispatchTabInc;
-static jboolean nLoadSO(JNIEnv *_env, jobject _this, jboolean useNative, jint targetApi) {
+static jboolean nLoadSO(JNIEnv *_env, jobject _this, jboolean useNative, jint targetApi, jstring libPath) {
void* handle = NULL;
if (useNative) {
handle = dlopen("libRS.so", RTLD_LAZY | RTLD_LOCAL);
} else {
- handle = dlopen("libRSSupport.so", RTLD_LAZY | RTLD_LOCAL);
+ // For API 9+, dlopen the full path of libRSSupport.
+ if (libPath != NULL) {
+ const char * libPathJni = _env->GetStringUTFChars(libPath, JNI_FALSE);
+ handle = dlopen(libPathJni, RTLD_LAZY | RTLD_LOCAL);
+ _env->ReleaseStringUTFChars(libPath, libPathJni);
+ } else {
+ handle = dlopen("libRSSupport.so", RTLD_LAZY | RTLD_LOCAL);
+ }
}
if (handle == NULL) {
- LOG_API("couldn't dlopen %s, %s", filename, dlerror());
+ LOG_ERR("couldn't dlopen %s; librsjni version: %d", dlerror(), RS_JNI_VERSION);
return false;
}
if (loadSymbols(handle, dispatchTab, targetApi) == false) {
- LOG_API("%s init failed!", filename);
+ LOG_ERR("Dispatch table init failed! librsjni version: %d", RS_JNI_VERSION);
+ dlclose(handle);
return false;
}
- LOG_API("Successfully loaded %s", filename);
+ LOG_API("Successfully loaded runtime");
return true;
}
@@ -294,11 +304,11 @@
void* handleIO = NULL;
handleIO = dlopen("libRSSupportIO.so", RTLD_LAZY | RTLD_LOCAL);
if (handleIO == NULL) {
- LOG_API("Couldn't load libRSSupportIO.so");
+ LOG_ERR("Couldn't load libRSSupportIO.so, librsjni version: %d", RS_JNI_VERSION);
return false;
}
if (loadIOSuppSyms(handleIO, ioDispatch) == false) {
- LOG_API("libRSSupportIO init failed!");
+ LOG_ERR("libRSSupportIO init failed! librsjni version: %d", RS_JNI_VERSION);
return false;
}
return true;
@@ -362,31 +372,30 @@
size_t numValues, numDependencies;
RsScriptFieldID* fieldIDs;
- uintptr_t* values;
RsClosure* depClosures;
RsScriptFieldID* depFieldIDs;
if (fieldIDs_length != values_length || values_length != sizes_length) {
- LOG_API("Unmatched field IDs, values, and sizes in closure creation.");
+ LOG_ERR("Unmatched field IDs, values, and sizes in closure creation.");
goto exit;
}
numValues = (size_t)fieldIDs_length;
if (depClosures_length != depFieldIDs_length) {
- LOG_API("Unmatched closures and field IDs for dependencies in closure creation.");
+ LOG_ERR("Unmatched closures and field IDs for dependencies in closure creation.");
goto exit;
}
numDependencies = (size_t)depClosures_length;
if (numDependencies > numValues) {
- LOG_API("Unexpected number of dependencies in closure creation");
+ LOG_ERR("Unexpected number of dependencies in closure creation");
goto exit;
}
if (numValues > RS_CLOSURE_MAX_NUMBER_ARGS_AND_BINDINGS) {
- LOG_API("Too many arguments or globals in closure creation");
+ LOG_ERR("Too many arguments or globals in closure creation");
goto exit;
}
@@ -399,15 +408,6 @@
fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
}
- values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
- if (values == nullptr) {
- goto exit;
- }
-
- for (size_t i = 0; i < numValues; i++) {
- values[i] = (uintptr_t)jValues[i];
- }
-
depClosures = (RsClosure*)alloca(sizeof(RsClosure) * numDependencies);
if (depClosures == nullptr) {
goto exit;
@@ -428,7 +428,7 @@
ret = (jlong)(uintptr_t)dispatchTab.ClosureCreate(
(RsContext)con, (RsScriptKernelID)kernelID, (RsAllocation)returnValue,
- fieldIDs, numValues, values, numValues,
+ fieldIDs, numValues, jValues, numValues,
(int*)jSizes, numValues,
depClosures, numDependencies,
depFieldIDs, numDependencies);
@@ -461,17 +461,16 @@
size_t numValues;
RsScriptFieldID* fieldIDs;
- uintptr_t* values;
if (fieldIDs_length != values_length || values_length != sizes_length) {
- LOG_API("Unmatched field IDs, values, and sizes in closure creation.");
+ LOG_ERR("Unmatched field IDs, values, and sizes in closure creation.");
goto exit;
}
numValues = (size_t) fieldIDs_length;
if (numValues > RS_CLOSURE_MAX_NUMBER_ARGS_AND_BINDINGS) {
- LOG_API("Too many arguments or globals in closure creation");
+ LOG_ERR("Too many arguments or globals in closure creation");
goto exit;
}
@@ -484,18 +483,9 @@
fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
}
- values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
- if (values == nullptr) {
- goto exit;
- }
-
- for (size_t i = 0; i < numValues; i++) {
- values[i] = (uintptr_t)jValues[i];
- }
-
ret = (jlong)(uintptr_t)dispatchTab.InvokeClosureCreate(
(RsContext)con, (RsScriptInvokeID)invokeID, jParams, jParamLength,
- fieldIDs, numValues, values, numValues,
+ fieldIDs, numValues, jValues, numValues,
(int*)jSizes, numValues);
exit:
@@ -511,15 +501,17 @@
static void
nClosureSetArg(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
jint index, jlong value, jint size) {
+ // Size is signed with -1 indicating the values is an Allocation
dispatchTab.ClosureSetArg((RsContext)con, (RsClosure)closureID, (uint32_t)index,
- (uintptr_t)value, (size_t)size);
+ (uintptr_t)value, size);
}
static void
nClosureSetGlobal(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
jlong fieldID, jlong value, jint size) {
+ // Size is signed with -1 indicating the values is an Allocation
dispatchTab.ClosureSetGlobal((RsContext)con, (RsClosure)closureID,
- (RsScriptFieldID)fieldID, (uintptr_t)value, (size_t)size);
+ (RsScriptFieldID)fieldID, (int64_t)value, size);
}
static long
@@ -536,7 +528,7 @@
RsClosure* closures;
if (numClosures > (jsize) RS_SCRIPT_GROUP_MAX_NUMBER_CLOSURES) {
- LOG_API("Too many closures in script group");
+ LOG_ERR("Too many closures in script group");
goto exit;
}
@@ -912,7 +904,7 @@
jint len = 0;
if (data) {
len = _env->GetArrayLength(data);
- jint *ptr = _env->GetIntArrayElements(data, NULL);
+ ptr = _env->GetIntArrayElements(data, NULL);
}
LOG_API("nContextSendMessage, con(%p), id(%i), len(%i)", (RsContext)con, id, len);
dispatchTab.ContextSendMessage((RsContext)con, id, (const uint8_t *)ptr, len * sizeof(int));
@@ -1546,6 +1538,7 @@
NULL, 0, NULL, 0);
}
}
+
static void
nScriptForEachV(JNIEnv *_env, jobject _this, jlong con, jlong incCon,
jlong script, jint slot, jlong ain, jlong aout, jbyteArray params, jboolean mUseInc)
@@ -1627,6 +1620,165 @@
_env->ReleaseByteArrayElements(params, ptr, JNI_ABORT);
}
+static void
+nScriptForEachMulti(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot,
+ jlongArray ains, jlong aout, jbyteArray params,
+ jintArray limits)
+{
+ LOG_API("nScriptForEach, con(%p), s(%p), slot(%i) ains(%p) aout(%" PRId64 ")", (RsContext)con, (void *)script, slot, ains, aout);
+
+ jint in_len = 0;
+ jlong *in_ptr = nullptr;
+
+ RsAllocation *in_allocs = nullptr;
+
+ if (ains != nullptr) {
+ in_len = _env->GetArrayLength(ains);
+ if (in_len > (jint)RS_KERNEL_MAX_ARGUMENTS) {
+ LOG_ERR("Too many arguments in kernel launch.");
+ // TODO (b/20758983): Report back to Java and throw an exception
+ return;
+ }
+
+ // TODO (b/20760800): Check in_ptr is not null
+ in_ptr = _env->GetLongArrayElements(ains, nullptr);
+ if (sizeof(RsAllocation) == sizeof(jlong)) {
+ in_allocs = (RsAllocation*)in_ptr;
+
+ } else {
+ // Convert from 64-bit jlong types to the native pointer type.
+
+ in_allocs = (RsAllocation*)alloca(in_len * sizeof(RsAllocation));
+ if (in_allocs == nullptr) {
+ LOG_ERR("Failed launching kernel for lack of memory.");
+ _env->ReleaseLongArrayElements(ains, in_ptr, JNI_ABORT);
+ return;
+ }
+
+ for (int index = in_len; --index >= 0;) {
+ in_allocs[index] = (RsAllocation)in_ptr[index];
+ }
+ }
+ }
+
+ jint param_len = 0;
+ jbyte *param_ptr = nullptr;
+
+ if (params != nullptr) {
+ param_len = _env->GetArrayLength(params);
+ param_ptr = _env->GetByteArrayElements(params, nullptr);
+ }
+
+ RsScriptCall sc, *sca = nullptr;
+ uint32_t sc_size = 0;
+
+ jint limit_len = 0;
+ jint *limit_ptr = nullptr;
+
+ if (limits != nullptr) {
+ limit_len = _env->GetArrayLength(limits);
+ limit_ptr = _env->GetIntArrayElements(limits, nullptr);
+
+ if (limit_len != 6) {
+ LOG_ERR("LaunchOptions cannot be recognized.");
+ goto exit;
+ }
+
+ sc.xStart = limit_ptr[0];
+ sc.xEnd = limit_ptr[1];
+ sc.yStart = limit_ptr[2];
+ sc.yEnd = limit_ptr[3];
+ sc.zStart = limit_ptr[4];
+ sc.zEnd = limit_ptr[5];
+ sc.strategy = RS_FOR_EACH_STRATEGY_DONT_CARE;
+ sc.arrayStart = 0;
+ sc.arrayEnd = 0;
+ sc.array2Start = 0;
+ sc.array2End = 0;
+ sc.array3Start = 0;
+ sc.array3End = 0;
+ sc.array4Start = 0;
+ sc.array4End = 0;
+
+ sca = ≻
+ }
+
+ dispatchTabInc.ScriptForEachMulti((RsContext)con, (RsScript)script, slot,
+ in_allocs, in_len, (RsAllocation)aout,
+ param_ptr, param_len, sca, sc_size);
+
+exit:
+
+ if (ains != nullptr) {
+ _env->ReleaseLongArrayElements(ains, in_ptr, JNI_ABORT);
+ }
+
+ if (params != nullptr) {
+ _env->ReleaseByteArrayElements(params, param_ptr, JNI_ABORT);
+ }
+
+ if (limits != nullptr) {
+ _env->ReleaseIntArrayElements(limits, limit_ptr, JNI_ABORT);
+ }
+}
+
+static void
+nScriptReduce(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot,
+ jlong ain, jlong aout, jintArray limits)
+{
+ LOG_API("nScriptReduce, con(%p), s(%p), slot(%i) ain(%" PRId64 ") aout(%" PRId64 ")", (RsContext)con, (void *)script, slot, ain, aout);
+
+ RsScriptCall sc, *sca = nullptr;
+ uint32_t sc_size = 0;
+
+ jint limit_len = 0;
+ jint *limit_ptr = nullptr;
+
+ bool limitLengthValid = true;
+
+ // If the caller passed limits, reflect them in the RsScriptCall.
+ if (limits != nullptr) {
+ limit_len = _env->GetArrayLength(limits);
+ limit_ptr = _env->GetIntArrayElements(limits, nullptr);
+
+ // We expect to be passed an array [x1, x2] which specifies
+ // the sub-range for a 1-dimensional reduction.
+ if (limit_len == 2) {
+ sc.xStart = limit_ptr[0];
+ sc.xEnd = limit_ptr[1];
+ sc.yStart = 0;
+ sc.yEnd = 0;
+ sc.zStart = 0;
+ sc.zEnd = 0;
+ sc.strategy = RS_FOR_EACH_STRATEGY_DONT_CARE;
+ sc.arrayStart = 0;
+ sc.arrayEnd = 0;
+ sc.array2Start = 0;
+ sc.array2End = 0;
+ sc.array3Start = 0;
+ sc.array3End = 0;
+ sc.array4Start = 0;
+ sc.array4End = 0;
+
+ sca = ≻
+ sc_size = sizeof(sc);
+ } else {
+ LOG_ERR("LaunchOptions cannot be recognized.");
+ limitLengthValid = false;
+ }
+ }
+
+ if (limitLengthValid) {
+ dispatchTab.ScriptReduce((RsContext)con, (RsScript)script, slot,
+ (RsAllocation)ain, (RsAllocation)aout,
+ sca, sc_size);
+ }
+
+ if (limits != nullptr) {
+ _env->ReleaseIntArrayElements(limits, limit_ptr, JNI_ABORT);
+ }
+}
+
// -----------------------------------
static jlong
@@ -1831,19 +1983,34 @@
// ---------------------------------------------------------------------------
// For Incremental Intrinsic Support
-static jboolean nIncLoadSO(JNIEnv *_env, jobject _this, jint deviceApi) {
+static jboolean nIncLoadSO(JNIEnv *_env, jobject _this, jint deviceApi, jstring libPath) {
void* handle = NULL;
- handle = dlopen("libRSSupport.so", RTLD_LAZY | RTLD_LOCAL);
+ // For API 9+, dlopen the full path of libRSSupport.
+ if (libPath != NULL) {
+ const char * libPathJni = _env->GetStringUTFChars(libPath, JNI_FALSE);
+ handle = dlopen(libPathJni, RTLD_LAZY | RTLD_LOCAL);
+ _env->ReleaseStringUTFChars(libPath, libPathJni);
+ } else {
+ handle = dlopen("libRSSupport.so", RTLD_LAZY | RTLD_LOCAL);
+ }
+
if (handle == NULL) {
- LOG_API("couldn't dlopen %s, %s", filename, dlerror());
+ LOG_ERR("couldn't dlopen %s; librsjni version: %d", dlerror(), RS_JNI_VERSION);
return false;
}
if (loadSymbols(handle, dispatchTabInc, deviceApi) == false) {
- LOG_API("%s init failed!", filename);
+ LOG_ERR("Dispatch Table init failed! librsjni version: %d", RS_JNI_VERSION);
+ dlclose(handle);
return false;
}
- LOG_API("Successfully loaded %s", filename);
+ dispatchTabInc.AllocationCreateStrided = (AllocationCreateStridedFnPtr)dlsym(handle, "rsAllocationCreateStrided");
+ if (dispatchTabInc.AllocationCreateStrided == NULL) {
+ LOG_ERR("Couldn't initialize dispatchTabInc.AllocationCreateStrided");
+ dlclose(handle);
+ return false;
+ }
+ LOG_API("Successfully loaded compat runtime");
return true;
}
@@ -1921,7 +2088,7 @@
// -----------------------------------
// Create Allocation from pointer
static jlong
-nIncAllocationCreateTyped(JNIEnv *_env, jobject _this, jlong con, jlong incCon, jlong alloc, jlong type)
+nIncAllocationCreateTyped(JNIEnv *_env, jobject _this, jlong con, jlong incCon, jlong alloc, jlong type, jint xBytesSize)
{
LOG_API("nAllocationCreateTyped, con(%p), type(%p), mip(%i), usage(%i), ptr(%p)",
incCon, (RsElement)type, mips, usage, (void *)pointer);
@@ -1932,10 +2099,26 @@
pIn = dispatchTab.AllocationGetPointer((RsContext)con, (RsAllocation)alloc, 0,
RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, 0, 0,
&strideIn, sizeof(size_t));
- ainI = dispatchTabInc.AllocationCreateTyped((RsContext)incCon, (RsType)type,
- RS_ALLOCATION_MIPMAP_NONE,
- RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
- (uintptr_t)pIn);
+ /*
+ * By definition stride is a roundup of xBytesSize with requiredAlignment, so requiredAlignment must
+ * be strictly larger than the difference of (stride - xBytesSize).
+ *
+ * We can prove that as long as requiredAlignment satisfies the following two conditions, the
+ * memory layout will be identical :
+ * 1. Smaller or equal than stride;
+ * 2. Larger than minRequiredAlignment.
+ *
+ * In this case we can simply choose the first power of 2 that satisfies both conditions.
+ */
+ size_t requiredAlignment = 16;
+ size_t minRequiredAlignment = strideIn - xBytesSize;
+ while (requiredAlignment <= minRequiredAlignment) {
+ requiredAlignment <<= 1;
+ }
+ ainI = dispatchTabInc.AllocationCreateStrided((RsContext)incCon, (RsType)type,
+ RS_ALLOCATION_MIPMAP_NONE,
+ RS_ALLOCATION_USAGE_INCREMENTAL_SUPPORT | RS_ALLOCATION_USAGE_SHARED,
+ (uintptr_t)pIn, requiredAlignment);
}
return (jlong)(uintptr_t) ainI;
}
@@ -1946,7 +2129,7 @@
static const char *classPathName = "android/support/v8/renderscript/RenderScript";
static JNINativeMethod methods[] = {
-{"nLoadSO", "(ZI)Z", (bool*)nLoadSO },
+{"nLoadSO", "(ZILjava/lang/String;)Z", (bool*)nLoadSO },
{"nLoadIOSO", "()Z", (bool*)nLoadIOSO },
{"nDeviceCreate", "()J", (void*)nDeviceCreate },
{"nDeviceDestroy", "(J)V", (void*)nDeviceDestroy },
@@ -2010,8 +2193,10 @@
{"rsnScriptInvokeV", "(JJI[BZ)V", (void*)nScriptInvokeV },
{"rsnScriptForEach", "(JJJIJJZ)V", (void*)nScriptForEach },
{"rsnScriptForEach", "(JJJIJJ[BZ)V", (void*)nScriptForEachV },
+{"rsnScriptForEach", "(JJI[JJ[B[I)V", (void*)nScriptForEachMulti },
{"rsnScriptForEachClipped", "(JJJIJJIIIIIIZ)V", (void*)nScriptForEachClipped },
{"rsnScriptForEachClipped", "(JJJIJJ[BIIIIIIZ)V", (void*)nScriptForEachClippedV },
+{"rsnScriptReduce", "(JJIJJ[I)V", (void*)nScriptReduce },
{"rsnScriptSetVarI", "(JJIIZ)V", (void*)nScriptSetVarI },
{"rsnScriptSetVarJ", "(JJIJZ)V", (void*)nScriptSetVarJ },
{"rsnScriptSetVarF", "(JJIFZ)V", (void*)nScriptSetVarF },
@@ -2044,7 +2229,7 @@
{"rsnSystemGetPointerSize", "()I", (void*)nSystemGetPointerSize },
// Entry points for Inc libRSSupport
-{"nIncLoadSO", "(I)Z", (bool*)nIncLoadSO },
+{"nIncLoadSO", "(ILjava/lang/String;)Z", (bool*)nIncLoadSO },
{"nIncDeviceCreate", "()J", (void*)nIncDeviceCreate },
{"nIncDeviceDestroy", "(J)V", (void*)nIncDeviceDestroy },
{"rsnIncContextCreate", "(JIII)J", (void*)nIncContextCreate },
@@ -2053,7 +2238,7 @@
{"rsnIncObjDestroy", "(JJ)V", (void*)nIncObjDestroy },
{"rsnIncElementCreate", "(JJIZI)J", (void*)nIncElementCreate },
{"rsnIncTypeCreate", "(JJIIIZZI)J", (void*)nIncTypeCreate },
-{"rsnIncAllocationCreateTyped", "(JJJJ)J", (void*)nIncAllocationCreateTyped },
+{"rsnIncAllocationCreateTyped", "(JJJJI)J", (void*)nIncAllocationCreateTyped },
};
// ---------------------------------------------------------------------------
diff --git a/v8/renderscript/rs_support/Android.mk b/v8/renderscript/rs_support/Android.mk
index c3f2401..7781fdf 100644
--- a/v8/renderscript/rs_support/Android.mk
+++ b/v8/renderscript/rs_support/Android.mk
@@ -29,7 +29,7 @@
rsg_generator.c
LOCAL_CXX_STL := none
-LOCAL_ADDRESS_SANITIZER := false
+LOCAL_SANITIZE := never
include $(BUILD_HOST_EXECUTABLE)
@@ -53,8 +53,8 @@
)
$(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
-$(GEN) : PRIVATE_CUSTOM_TOOL = $(RSG_GENERATOR_SUPPORT) $< $@ <$(PRIVATE_PATH)/rs.spec
-$(GEN) : $(RSG_GENERATOR_SUPPORT) $(LOCAL_PATH)/rs.spec
+$(GEN) : PRIVATE_CUSTOM_TOOL = cat $(PRIVATE_PATH)/rs.spec $(PRIVATE_PATH)/rs_compat.spec | $(RSG_GENERATOR_SUPPORT) $< $@
+$(GEN) : $(RSG_GENERATOR_SUPPORT) $(LOCAL_PATH)/rs.spec $(LOCAL_PATH)/rs_compat.spec
$(GEN): $(generated_sources_dir)/%.h : $(LOCAL_PATH)/%.h.rsg
$(transform-generated-source)
@@ -70,8 +70,8 @@
)
$(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
-$(GEN) : PRIVATE_CUSTOM_TOOL = $(RSG_GENERATOR_SUPPORT) $< $@ <$(PRIVATE_PATH)/rs.spec
-$(GEN) : $(RSG_GENERATOR_SUPPORT) $(LOCAL_PATH)/rs.spec
+$(GEN) : PRIVATE_CUSTOM_TOOL = cat $(PRIVATE_PATH)/rs.spec $(PRIVATE_PATH)/rs_compat.spec | $(RSG_GENERATOR_SUPPORT) $< $@
+$(GEN) : $(RSG_GENERATOR_SUPPORT) $(LOCAL_PATH)/rs.spec $(LOCAL_PATH)/rs_compat.spec
$(GEN): $(generated_sources_dir)/%.cpp : $(LOCAL_PATH)/%.cpp.rsg
$(transform-generated-source)
@@ -81,7 +81,6 @@
LOCAL_GENERATED_SOURCES += $(GEN)
LOCAL_SRC_FILES:= \
- rsAdapter.cpp \
rsAllocation.cpp \
rsClosure.cpp \
rsCompatibilityLib.cpp \
@@ -139,7 +138,7 @@
ifeq ($(ARCH_ARM_HAVE_ARMV7A),true)
LOCAL_CFLAGS_arm := -DARCH_ARM_HAVE_VFP -DARCH_ARM_USE_INTRINSICS
LOCAL_ASFLAGS_arm := -mfpu=neon
-# frameworks/rs/cpu_ref/rsCpuIntrinsics_neon_3DLUT.S does not compile.
+# Clang does not support nested .irp in *_Blur.S
LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
LOCAL_SRC_FILES_arm := \
cpu_ref/rsCpuIntrinsics_neon_3DLUT.S \
@@ -152,13 +151,15 @@
endif
LOCAL_REQUIRED_MODULES := libblasV8
+LOCAL_STATIC_LIBRARIES := libbnnmlowpV8
LOCAL_LDFLAGS += -llog -ldl
LOCAL_NDK_STL_VARIANT := stlport_static
LOCAL_C_INCLUDES += frameworks/compile/libbcc/include
LOCAL_C_INCLUDES += external/cblas/include
+LOCAL_C_INCLUDES += external/gemmlowp/eight_bit_int_gemm
-LOCAL_CFLAGS += $(rs_base_CFLAGS)
+LOCAL_CFLAGS += $(rs_base_CFLAGS) -DGEMMLOWP_USE_STLPORT
LOCAL_MODULE:= libRSSupport
LOCAL_MODULE_TAGS := optional