Skip to content

Instantly share code, notes, and snippets.

@zaki50
Created August 18, 2015 07:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zaki50/6cbe208bee2f8b2db3d2 to your computer and use it in GitHub Desktop.
Save zaki50/6cbe208bee2f8b2db3d2 to your computer and use it in GitHub Desktop.
diff -Nur support-v4-22.2.1/android/support/v4/animation/AnimatorCompatHelper.java support-v4-23.0.0/android/support/v4/animation/AnimatorCompatHelper.java
--- support-v4-22.2.1/android/support/v4/animation/AnimatorCompatHelper.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/animation/AnimatorCompatHelper.java 2015-06-23 08:43:44.000000000 +0900
@@ -17,6 +17,7 @@
package android.support.v4.animation;
import android.os.Build;
+import android.view.View;
abstract public class AnimatorCompatHelper {
@@ -37,4 +38,8 @@
AnimatorCompatHelper() {
}
+
+ public static void clearInterpolator(View view) {
+ IMPL.clearInterpolator(view);
+ }
}
diff -Nur support-v4-22.2.1/android/support/v4/animation/AnimatorProvider.java support-v4-23.0.0/android/support/v4/animation/AnimatorProvider.java
--- support-v4-22.2.1/android/support/v4/animation/AnimatorProvider.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/animation/AnimatorProvider.java 2015-06-23 08:43:44.000000000 +0900
@@ -16,6 +16,8 @@
package android.support.v4.animation;
+import android.view.View;
+
/**
* A simple interface to do things in animation pulse.
* <p>
@@ -32,4 +34,6 @@
* Animator callback interface.
*/
ValueAnimatorCompat emptyValueAnimator();
+
+ void clearInterpolator(View view);
}
diff -Nur support-v4-22.2.1/android/support/v4/animation/DonutAnimatorCompatProvider.java support-v4-23.0.0/android/support/v4/animation/DonutAnimatorCompatProvider.java
--- support-v4-22.2.1/android/support/v4/animation/DonutAnimatorCompatProvider.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/animation/DonutAnimatorCompatProvider.java 2015-06-23 08:43:44.000000000 +0900
@@ -148,4 +148,8 @@
return mFraction;
}
}
+
+ @Override
+ public void clearInterpolator(View view) {
+ }
}
diff -Nur support-v4-22.2.1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java support-v4-23.0.0/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java
--- support-v4-22.2.1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java 2015-06-23 08:43:44.000000000 +0900
@@ -18,6 +18,7 @@
import android.animation.Animator;
import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.view.View;
@@ -31,6 +32,8 @@
*/
class HoneycombMr1AnimatorCompatProvider implements AnimatorProvider {
+ private TimeInterpolator mDefaultInterpolator;
+
@Override
public ValueAnimatorCompat emptyValueAnimator() {
return new HoneycombValueAnimatorCompat(ValueAnimator.ofFloat(0f, 1f));
@@ -121,4 +124,12 @@
mWrapped.onAnimationRepeat(mValueAnimatorCompat);
}
}
+
+ @Override
+ public void clearInterpolator(View view) {
+ if (mDefaultInterpolator == null) {
+ mDefaultInterpolator = new ValueAnimator().getInterpolator();
+ }
+ view.animate().setInterpolator(mDefaultInterpolator);
+ }
}
diff -Nur support-v4-22.2.1/android/support/v4/app/ActivityCompat.java support-v4-23.0.0/android/support/v4/app/ActivityCompat.java
--- support-v4-22.2.1/android/support/v4/app/ActivityCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/ActivityCompat.java 2015-08-06 08:26:24.000000000 +0900
@@ -19,15 +19,21 @@
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.Matrix;
import android.graphics.RectF;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.os.Parcelable;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.view.View;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -36,6 +42,35 @@
* introduced after API level 4 in a backwards compatible fashion.
*/
public class ActivityCompat extends ContextCompat {
+
+ /**
+ * This interface is the contract for receiving the results for permission requests.
+ */
+ public interface OnRequestPermissionsResultCallback {
+
+ /**
+ * Callback for the result from requesting permissions. This method
+ * is invoked for every call on {@link #requestPermissions(android.app.Activity,
+ * String[], int)}.
+ * <p>
+ * <strong>Note:</strong> It is possible that the permissions request interaction
+ * with the user is interrupted. In this case you will receive empty permissions
+ * and results arrays which should be treated as a cancellation.
+ * </p>
+ *
+ * @param requestCode The request code passed in {@link #requestPermissions(
+ * android.app.Activity, String[], int)}
+ * @param permissions The requested permissions. Never null.
+ * @param grantResults The grant results for the corresponding permissions
+ * which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
+ * or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
+ *
+ * @see #requestPermissions(android.app.Activity, String[], int)
+ */
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults);
+ }
+
/**
* Invalidate the activity's options menu, if able.
*
@@ -163,6 +198,27 @@
}
/**
+ * Backwards compatible implementation of {@link android.app.Activity#getReferrer()
+ * Activity.getReferrer}. Uses the platform's implementation if available, otherwise
+ * only falls back to digging any explicitly specified referrer from the activity's intent.
+ */
+ public Uri getReferrer(Activity activity) {
+ if (Build.VERSION.SDK_INT >= 22) {
+ return ActivityCompat22.getReferrer(activity);
+ }
+ Intent intent = activity.getIntent();
+ Uri referrer = intent.getParcelableExtra("android.intent.extra.REFERRER");
+ if (referrer != null) {
+ return referrer;
+ }
+ String referrerName = intent.getStringExtra("android.intent.extra.REFERRER_NAME");
+ if (referrerName != null) {
+ return Uri.parse(referrerName);
+ }
+ return null;
+ }
+
+ /**
* When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
* android.view.View, String)} was used to start an Activity, <var>callback</var>
* will be called to handle shared elements on the <i>launched</i> Activity. This requires
@@ -205,6 +261,110 @@
}
}
+ /**
+ * Requests permissions to be granted to this application. These permissions
+ * must be requested in your manifest, they should not be granted to your app,
+ * and they should have protection level {@link android.content.pm.PermissionInfo
+ * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
+ * the platform or a third-party app.
+ * <p>
+ * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
+ * are granted at install time if requested in the manifest. Signature permissions
+ * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
+ * install time if requested in the manifest and the signature of your app matches
+ * the signature of the app declaring the permissions.
+ * </p>
+ * <p>
+ * If your app does not have the requested permissions the user will be presented
+ * with UI for accepting them. After the user has accepted or rejected the
+ * requested permissions you will receive a callback reporting whether the
+ * permissions were granted or not. Your activity has to implement {@link
+ * android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback}
+ * and the results of permission requests will be delivered to its {@link
+ * android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(
+ * int, String[], int[])} method.
+ * </p>
+ * <p>
+ * Note that requesting a permission does not guarantee it will be granted and
+ * your app should be able to run without having this permission.
+ * </p>
+ * <p>
+ * This method may start an activity allowing the user to choose which permissions
+ * to grant and which to reject. Hence, you should be prepared that your activity
+ * may be paused and resumed. Further, granting some permissions may require
+ * a restart of you application. In such a case, the system will recreate the
+ * activity stack before delivering the result to your onRequestPermissionsResult(
+ * int, String[], int[]).
+ * </p>
+ * <p>
+ * When checking whether you have a permission you should use {@link
+ * #checkSelfPermission(android.content.Context, String)}.
+ * </p>
+ *
+ * @param activity The target activity.
+ * @param permissions The requested permissions.
+ * @param requestCode Application specific request code to match with a result
+ * reported to {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult(
+ * int, String[], int[])}.
+ *
+ * @see #checkSelfPermission(android.content.Context, String)
+ * @see #shouldShowRequestPermissionRationale(android.app.Activity, String)
+ */
+ public static void requestPermissions(final @NonNull Activity activity,
+ final @NonNull String[] permissions, final int requestCode) {
+ if (Build.VERSION.SDK_INT >= 23) {
+ ActivityCompatApi23.requestPermissions(activity, permissions, requestCode);
+ } else if (activity instanceof OnRequestPermissionsResultCallback) {
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ final int[] grantResults = new int[permissions.length];
+
+ PackageManager packageManager = activity.getPackageManager();
+ String packageName = activity.getPackageName();
+
+ final int permissionCount = permissions.length;
+ for (int i = 0; i < permissionCount; i++) {
+ grantResults[i] = packageManager.checkPermission(
+ permissions[i], packageName);
+ }
+
+ ((OnRequestPermissionsResultCallback) activity).onRequestPermissionsResult(
+ requestCode, permissions, grantResults);
+ }
+ });
+ }
+ }
+
+ /**
+ * Gets whether you should show UI with rationale for requesting a permission.
+ * You should do this only if you do not have the permission and the context in
+ * which the permission is requested does not clearly communicate to the user
+ * what would be the benefit from granting this permission.
+ * <p>
+ * For example, if you write a camera app, requesting the camera permission
+ * would be expected by the user and no rationale for why it is requested is
+ * needed. If however, the app needs location for tagging photos then a non-tech
+ * savvy user may wonder how location is related to taking photos. In this case
+ * you may choose to show UI with rationale of requesting this permission.
+ * </p>
+ *
+ * @param activity The target activity.
+ * @param permission A permission your app wants to request.
+ * @return Whether you can show permission rationale UI.
+ *
+ * @see #checkSelfPermission(android.content.Context, String)
+ * @see #requestPermissions(android.app.Activity, String[], int)
+ */
+ public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
+ @NonNull String permission) {
+ if (Build.VERSION.SDK_INT >= 23) {
+ return ActivityCompatApi23.shouldShowRequestPermissionRationale(activity, permission);
+ }
+ return false;
+ }
+
private static ActivityCompat21.SharedElementCallback21 createCallback(
SharedElementCallback callback) {
ActivityCompat21.SharedElementCallback21 newCallback = null;
diff -Nur support-v4-22.2.1/android/support/v4/app/ActivityCompat22.java support-v4-23.0.0/android/support/v4/app/ActivityCompat22.java
--- support-v4-22.2.1/android/support/v4/app/ActivityCompat22.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/ActivityCompat22.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,26 @@
+/*
+ * 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.app;
+
+import android.app.Activity;
+import android.net.Uri;
+
+class ActivityCompat22 {
+ public static Uri getReferrer(Activity activity) {
+ return activity.getReferrer();
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/app/ActivityCompat23.java support-v4-23.0.0/android/support/v4/app/ActivityCompat23.java
--- support-v4-22.2.1/android/support/v4/app/ActivityCompat23.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/ActivityCompat23.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,39 @@
+/*
+ * 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.app;
+
+import android.app.Activity;
+
+class ActivityCompatApi23 {
+ public interface RequestPermissionsRequestCodeValidator {
+ public void validateRequestPermissionsRequestCode(int requestCode);
+ }
+
+ public static void requestPermissions(Activity activity, String[] permissions,
+ int requestCode) {
+ if (activity instanceof RequestPermissionsRequestCodeValidator) {
+ ((RequestPermissionsRequestCodeValidator) activity)
+ .validateRequestPermissionsRequestCode(requestCode);
+ }
+ activity.requestPermissions(permissions, requestCode);
+ }
+
+ public static boolean shouldShowRequestPermissionRationale(Activity activity,
+ String permission) {
+ return activity.shouldShowRequestPermissionRationale(permission);
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/app/AppOpsManagerCompat.java support-v4-23.0.0/android/support/v4/app/AppOpsManagerCompat.java
--- support-v4-22.2.1/android/support/v4/app/AppOpsManagerCompat.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/AppOpsManagerCompat.java 2015-07-08 08:46:10.000000000 +0900
@@ -0,0 +1,140 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.NonNull;
+
+/**
+ * Helper for accessing features in android.app.AppOpsManager
+ * introduced after API level 4 in a backwards compatible fashion.
+ */
+public class AppOpsManagerCompat {
+
+ /**
+ * Result from {@link #noteOp}: the given caller is allowed to
+ * perform the given operation.
+ */
+ public static final int MODE_ALLOWED = 0;
+
+ /**
+ * Result from {@link #noteOp}: the given caller is not allowed to perform
+ * the given operation, and this attempt should <em>silently fail</em> (it
+ * should not cause the app to crash).
+ */
+ public static final int MODE_IGNORED = 1;
+
+ /**
+ * Result from {@link #noteOp}: the given caller should use its default
+ * security check. This mode is not normally used; it should only be used
+ * with appop permissions, and callers must explicitly check for it and
+ * deal with it.
+ */
+ public static final int MODE_DEFAULT = 3;
+
+ private static class AppOpsManagerImpl {
+ public String permissionToOp(String permission) {
+ return null;
+ }
+
+ public int noteOp(Context context, String op, int uid, String packageName) {
+ return MODE_IGNORED;
+ }
+
+ public int noteProxyOp(Context context, String op, String proxiedPackageName) {
+ return MODE_IGNORED;
+ }
+ }
+
+ private static class AppOpsManager23 extends AppOpsManagerImpl {
+ @Override
+ public String permissionToOp(String permission) {
+ return AppOpsManagerCompat23.permissionToOp(permission);
+ }
+
+ @Override
+ public int noteOp(Context context, String op, int uid, String packageName) {
+ return AppOpsManagerCompat23.noteOp(context, op, uid, packageName);
+ }
+
+ @Override
+ public int noteProxyOp(Context context, String op, String proxiedPackageName) {
+ return AppOpsManagerCompat23.noteProxyOp(context, op, proxiedPackageName);
+ }
+ }
+
+ private static final AppOpsManagerImpl IMPL;
+ static {
+ if (Build.VERSION.SDK_INT >= 23) {
+ IMPL = new AppOpsManager23();
+ } else {
+ IMPL = new AppOpsManagerImpl();
+ }
+ }
+
+ /**
+ * Gets the app op name associated with a given permission.
+ *
+ * @param permission The permission.
+ * @return The app op associated with the permission or null.
+ */
+ public static String permissionToOp(@NonNull String permission) {
+ return IMPL.permissionToOp(permission);
+ }
+
+ /**
+ * Make note of an application performing an operation. Note that you must pass
+ * in both the uid and name of the application to be checked; this function will verify
+ * that these two match, and if not, return {@link #MODE_IGNORED}. If this call
+ * succeeds, the last execution time of the operation for this app will be updated to
+ * the current time.
+ * @param context Your context.
+ * @param op The operation to note. One of the OPSTR_* constants.
+ * @param uid The user id of the application attempting to perform the operation.
+ * @param packageName The name of the application attempting to perform the operation.
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+ * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+ * causing the app to crash).
+ * @throws SecurityException If the app has been configured to crash on this op.
+ */
+ public static int noteOp(@NonNull Context context, @NonNull String op, int uid,
+ @NonNull String packageName) {
+ return IMPL.noteOp(context, op, uid, packageName);
+ }
+
+ /**
+ * Make note of an application performing an operation on behalf of another
+ * application when handling an IPC. Note that you must pass the package name
+ * of the application that is being proxied while its UID will be inferred from
+ * the IPC state; this function will verify that the calling uid and proxied
+ * package name match, and if not, return {@link #MODE_IGNORED}. If this call
+ * succeeds, the last execution time of the operation for the proxied app and
+ * your app will be updated to the current time.
+ * @param context Your context.
+ * @param op The operation to note. One of the OPSTR_* constants.
+ * @param proxiedPackageName The name of the application calling into the proxy application.
+ * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+ * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+ * causing the app to crash).
+ * @throws SecurityException If the app has been configured to crash on this op.
+ */
+ public static int noteProxyOp(@NonNull Context context, @NonNull String op,
+ @NonNull String proxiedPackageName) {
+ return IMPL.noteProxyOp(context, op, proxiedPackageName);
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/app/AppOpsManagerCompat23.java support-v4-23.0.0/android/support/v4/app/AppOpsManagerCompat23.java
--- support-v4-22.2.1/android/support/v4/app/AppOpsManagerCompat23.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/AppOpsManagerCompat23.java 2015-07-08 08:46:10.000000000 +0900
@@ -0,0 +1,39 @@
+/*
+ * 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.app;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+
+/**
+ * AppOpsManager implementations for API 23.
+ */
+public class AppOpsManagerCompat23 {
+ public static String permissionToOp(String permission) {
+ return AppOpsManager.permissionToOp(permission);
+ }
+
+ public static int noteOp(Context context, String op, int uid, String packageName) {
+ AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ return appOpsManager.noteOp(op, uid, packageName);
+ }
+
+ public static int noteProxyOp(Context context, String op, String proxiedPackageName) {
+ AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ return appOpsManager.noteProxyOp(op, proxiedPackageName);
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/app/BackStackRecord.java support-v4-23.0.0/android/support/v4/app/BackStackRecord.java
--- support-v4-22.2.1/android/support/v4/app/BackStackRecord.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/BackStackRecord.java 2015-07-02 08:14:40.000000000 +0900
@@ -16,13 +16,11 @@
package android.support.v4.app;
-import android.graphics.Rect;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
-import android.support.v4.util.LogWriter;
-import android.support.v4.util.Pair;
import android.support.v4.util.ArrayMap;
+import android.support.v4.util.LogWriter;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
@@ -33,7 +31,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collection;
final class BackStackState implements Parcelable {
final int[] mOps;
@@ -48,7 +45,7 @@
final ArrayList<String> mSharedElementSourceNames;
final ArrayList<String> mSharedElementTargetNames;
- public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
+ public BackStackState(BackStackRecord bse) {
int numRemoved = 0;
BackStackRecord.Op op = bse.mHead;
while (op != null) {
@@ -371,14 +368,14 @@
public CharSequence getBreadCrumbTitle() {
if (mBreadCrumbTitleRes != 0) {
- return mManager.mActivity.getText(mBreadCrumbTitleRes);
+ return mManager.mHost.getContext().getText(mBreadCrumbTitleRes);
}
return mBreadCrumbTitleText;
}
public CharSequence getBreadCrumbShortTitle() {
if (mBreadCrumbShortTitleRes != 0) {
- return mManager.mActivity.getText(mBreadCrumbShortTitleRes);
+ return mManager.mHost.getContext().getText(mBreadCrumbShortTitleRes);
}
return mBreadCrumbShortTitleText;
}
@@ -675,12 +672,13 @@
} break;
case OP_REPLACE: {
Fragment f = op.fragment;
+ int containerId = f.mContainerId;
if (mManager.mAdded != null) {
for (int i=0; i<mManager.mAdded.size(); i++) {
Fragment old = mManager.mAdded.get(i);
if (FragmentManagerImpl.DEBUG) Log.v(TAG,
"OP_REPLACE: adding=" + f + " old=" + old);
- if (f == null || old.mContainerId == f.mContainerId) {
+ if (old.mContainerId == containerId) {
if (old == f) {
op.fragment = f = null;
} else {
@@ -774,7 +772,7 @@
*/
private void calculateFragments(SparseArray<Fragment> firstOutFragments,
SparseArray<Fragment> lastInFragments) {
- if (!mManager.mContainer.hasView()) {
+ if (!mManager.mContainer.onHasView()) {
return; // nothing to see, so no transitions
}
Op op = mHead;
@@ -832,7 +830,7 @@
*/
public void calculateBackFragments(SparseArray<Fragment> firstOutFragments,
SparseArray<Fragment> lastInFragments) {
- if (!mManager.mContainer.hasView()) {
+ if (!mManager.mContainer.onHasView()) {
return; // nothing to see, so no transitions
}
Op op = mHead;
@@ -1023,7 +1021,7 @@
// Adding a non-existent target view makes sure that the transitions don't target
// any views by default. They'll only target the views we tell add. If we don't
// add any, then no views will be targeted.
- state.nonExistentView = new View(mManager.mActivity);
+ state.nonExistentView = new View(mManager.mHost.getContext());
boolean anyTransitionStarted = false;
// Go over all leaving fragments.
@@ -1073,7 +1071,7 @@
if (inFragment == null || outFragment == null) {
return null;
}
- return FragmentTransitionCompat21.cloneTransition(isBack ?
+ return FragmentTransitionCompat21.wrapSharedElementTransition(isBack ?
outFragment.getSharedElementReturnTransition() :
inFragment.getSharedElementEnterTransition());
}
@@ -1131,7 +1129,7 @@
*/
private boolean configureTransitions(int containerId, TransitionState state, boolean isBack,
SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
- ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.findViewById(containerId);
+ ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.onFindViewById(containerId);
if (sceneRoot == null) {
return false;
}
@@ -1142,27 +1140,31 @@
Object sharedElementTransition = getSharedElementTransition(inFragment, outFragment,
isBack);
Object exitTransition = getExitTransition(outFragment, isBack);
- if (enterTransition == null && sharedElementTransition == null &&
- exitTransition == null) {
- return false; // no transitions!
- }
ArrayMap<String, View> namedViews = null;
ArrayList<View> sharedElementTargets = new ArrayList<View>();
if (sharedElementTransition != null) {
namedViews = remapSharedElements(state, outFragment, isBack);
- sharedElementTargets.add(state.nonExistentView);
- sharedElementTargets.addAll(namedViews.values());
-
- // Notify the start of the transition.
- SharedElementCallback callback = isBack ?
- outFragment.mEnterTransitionCallback :
- inFragment.mEnterTransitionCallback;
- if (callback != null) {
- ArrayList<String> names = new ArrayList<String>(namedViews.keySet());
- ArrayList<View> views = new ArrayList<View>(namedViews.values());
- callback.onSharedElementStart(names, views, null);
+ if (namedViews.isEmpty()) {
+ sharedElementTransition = null;
+ namedViews = null;
+ } else {
+ // Notify the start of the transition.
+ SharedElementCallback callback = isBack ?
+ outFragment.mEnterTransitionCallback :
+ inFragment.mEnterTransitionCallback;
+ if (callback != null) {
+ ArrayList<String> names = new ArrayList<String>(namedViews.keySet());
+ ArrayList<View> views = new ArrayList<View>(namedViews.values());
+ callback.onSharedElementStart(names, views, null);
+ }
+ prepareSharedElementTransition(state, sceneRoot, sharedElementTransition,
+ inFragment, outFragment, isBack, sharedElementTargets);
}
}
+ if (enterTransition == null && sharedElementTransition == null &&
+ exitTransition == null) {
+ return false; // no transitions!
+ }
ArrayList<View> exitingViews = new ArrayList<View>();
exitTransition = captureExitingViews(exitTransition, outFragment, exitingViews,
@@ -1190,16 +1192,14 @@
}
};
- if (sharedElementTransition != null) {
- prepareSharedElementTransition(state, sceneRoot, sharedElementTransition,
- inFragment, outFragment, isBack, sharedElementTargets);
- }
-
ArrayList<View> enteringViews = new ArrayList<View>();
ArrayMap<String, View> renamedViews = new ArrayMap<String, View>();
- boolean allowOverlap = isBack ? inFragment.getAllowReturnTransitionOverlap() :
- inFragment.getAllowEnterTransitionOverlap();
+ boolean allowOverlap = true;
+ if (inFragment != null) {
+ allowOverlap = isBack ? inFragment.getAllowReturnTransitionOverlap() :
+ inFragment.getAllowEnterTransitionOverlap();
+ }
Object transition = FragmentTransitionCompat21.mergeTransitions(enterTransition,
exitTransition, sharedElementTransition, allowOverlap);
@@ -1207,7 +1207,7 @@
FragmentTransitionCompat21.addTransitionTargets(enterTransition,
sharedElementTransition, sceneRoot, viewRetriever, state.nonExistentView,
state.enteringEpicenterView, state.nameOverrides, enteringViews,
- renamedViews, sharedElementTargets);
+ namedViews, renamedViews, sharedElementTargets);
excludeHiddenFragmentsAfterEnter(sceneRoot, state, containerId, transition);
// We want to exclude hidden views later, so we need a non-null list in the
@@ -1243,10 +1243,8 @@
ArrayMap<String, View> namedViews = mapSharedElementsIn(
state, isBack, inFragment);
- sharedElementTargets.add(state.nonExistentView);
- sharedElementTargets.addAll(namedViews.values());
- FragmentTransitionCompat21.addTargets(sharedElementTransition,
- sharedElementTargets);
+ FragmentTransitionCompat21.setSharedElementTargets(sharedElementTransition,
+ state.nonExistentView, namedViews, sharedElementTargets);
setEpicenterIn(namedViews, state);
@@ -1392,7 +1390,7 @@
private static void setNameOverride(ArrayMap<String, String> overrides,
String source, String target) {
- if (source != null && target != null && !source.equals(target)) {
+ if (source != null && target != null) {
for (int index = 0; index < overrides.size(); index++) {
if (source.equals(overrides.valueAt(index))) {
overrides.setValueAt(index, target);
diff -Nur support-v4-22.2.1/android/support/v4/app/BaseFragmentActivityDonut.java support-v4-23.0.0/android/support/v4/app/BaseFragmentActivityDonut.java
--- support-v4-22.2.1/android/support/v4/app/BaseFragmentActivityDonut.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/BaseFragmentActivityDonut.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,54 @@
+/*
+ * 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.app;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Base class for {@code FragmentActivity} to be able to use Donut APIs.
+ */
+abstract class BaseFragmentActivityDonut extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ if (Build.VERSION.SDK_INT < 11 && getLayoutInflater().getFactory() == null) {
+ // On pre-HC devices we need to manually install ourselves as a Factory.
+ // On HC and above, we are automatically installed as a private factory
+ getLayoutInflater().setFactory(this);
+ }
+
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(String name, Context context, AttributeSet attrs) {
+ final View v = dispatchFragmentsOnCreateView(null, name, context, attrs);
+ if (v == null) {
+ return super.onCreateView(name, context, attrs);
+ }
+ return v;
+ }
+
+ abstract View dispatchFragmentsOnCreateView(View parent, String name,
+ Context context, AttributeSet attrs);
+
+}
diff -Nur support-v4-22.2.1/android/support/v4/app/BaseFragmentActivityHoneycomb.java support-v4-23.0.0/android/support/v4/app/BaseFragmentActivityHoneycomb.java
--- support-v4-22.2.1/android/support/v4/app/BaseFragmentActivityHoneycomb.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/BaseFragmentActivityHoneycomb.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,39 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Base class for {@code FragmentActivity} to be able to use v11 APIs.
+ */
+abstract class BaseFragmentActivityHoneycomb extends BaseFragmentActivityDonut {
+
+ @Override
+ public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+ final View v = dispatchFragmentsOnCreateView(parent, name, context, attrs);
+ if (v == null && Build.VERSION.SDK_INT >= 11) {
+ // If we're running on HC or above, let the super have a go
+ return super.onCreateView(parent, name, context, attrs);
+ }
+ return v;
+ }
+
+}
diff -Nur support-v4-22.2.1/android/support/v4/app/DialogFragment.java support-v4-23.0.0/android/support/v4/app/DialogFragment.java
--- support-v4-22.2.1/android/support/v4/app/DialogFragment.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/DialogFragment.java 2015-06-23 08:43:44.000000000 +0900
@@ -306,22 +306,29 @@
}
mDialog = onCreateDialog(savedInstanceState);
- switch (mStyle) {
+
+ if (mDialog != null) {
+ setupDialog(mDialog, mStyle);
+
+ return (LayoutInflater) mDialog.getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ }
+ return (LayoutInflater) mHost.getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ /** @hide */
+ public void setupDialog(Dialog dialog, int style) {
+ switch (style) {
case STYLE_NO_INPUT:
- mDialog.getWindow().addFlags(
+ dialog.getWindow().addFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
// fall through...
case STYLE_NO_FRAME:
case STYLE_NO_TITLE:
- mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
}
- if (mDialog != null) {
- return (LayoutInflater) mDialog.getContext().getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- }
- return (LayoutInflater) mActivity.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
}
/**
diff -Nur support-v4-22.2.1/android/support/v4/app/Fragment.java support-v4-23.0.0/android/support/v4/app/Fragment.java
--- support-v4-22.2.1/android/support/v4/app/Fragment.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/Fragment.java 2015-07-22 08:36:20.000000000 +0900
@@ -22,11 +22,10 @@
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.util.SimpleArrayMap;
@@ -89,20 +88,21 @@
mArguments = in.readBundle();
mSavedFragmentState = in.readBundle();
}
-
- public Fragment instantiate(FragmentActivity activity, Fragment parent) {
+
+ public Fragment instantiate(FragmentHostCallback host, Fragment parent) {
if (mInstance != null) {
return mInstance;
}
-
+
+ final Context context = host.getContext();
if (mArguments != null) {
- mArguments.setClassLoader(activity.getClassLoader());
+ mArguments.setClassLoader(context.getClassLoader());
}
-
- mInstance = Fragment.instantiate(activity, mClassName, mArguments);
-
+
+ mInstance = Fragment.instantiate(context, mClassName, mArguments);
+
if (mSavedFragmentState != null) {
- mSavedFragmentState.setClassLoader(activity.getClassLoader());
+ mSavedFragmentState.setClassLoader(context.getClassLoader());
mInstance.mSavedFragmentState = mSavedFragmentState;
}
mInstance.setIndex(mIndex, parent);
@@ -113,14 +113,14 @@
mInstance.mTag = mTag;
mInstance.mRetainInstance = mRetainInstance;
mInstance.mDetached = mDetached;
- mInstance.mFragmentManager = activity.mFragments;
+ mInstance.mFragmentManager = host.mFragmentManager;
if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
"Instantiated fragment " + mInstance);
return mInstance;
}
-
+
public int describeContents() {
return 0;
}
@@ -228,17 +228,17 @@
// True if this fragment has been restored from previously saved state.
boolean mRestored;
-
+
// Number of active back stack entries this fragment is in.
int mBackStackNesting;
-
+
// The fragment manager we are associated with. Set as soon as the
// fragment is used in a transaction; cleared after it has been removed
// from all transactions.
FragmentManagerImpl mFragmentManager;
- // Activity this fragment is attached to.
- FragmentActivity mActivity;
+ // Host this fragment is attached to.
+ FragmentHostCallback mHost;
// Private fragment manager for child fragments inside of this one.
FragmentManagerImpl mChildFragmentManager;
@@ -348,10 +348,12 @@
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
+ @Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in, null);
}
+ @Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
@@ -606,22 +608,39 @@
}
/**
- * Return the Activity this fragment is currently associated with.
+ * Return the {@link Context} this fragment is currently associated with.
+ */
+ public Context getContext() {
+ return mHost == null ? null : mHost.getContext();
+ }
+
+ /**
+ * Return the {@link FragmentActivity} this fragment is currently associated with.
+ * May return {@code null} if the fragment is associated with a {@link Context}
+ * instead.
*/
final public FragmentActivity getActivity() {
- return mActivity;
+ return mHost == null ? null : (FragmentActivity) mHost.getActivity();
}
-
+
+ /**
+ * Return the host object of this fragment. May return {@code null} if the fragment
+ * isn't currently being hosted.
+ */
+ final public Object getHost() {
+ return mHost == null ? null : mHost.onGetHost();
+ }
+
/**
* Return <code>getActivity().getResources()</code>.
*/
final public Resources getResources() {
- if (mActivity == null) {
+ if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
- return mActivity.getResources();
+ return mHost.getContext().getResources();
}
-
+
/**
* Return a localized, styled CharSequence from the application's package's
* default string table.
@@ -701,7 +720,7 @@
* Return true if the fragment is currently added to its activity.
*/
final public boolean isAdded() {
- return mActivity != null && mAdded;
+ return mHost != null && mAdded;
}
/**
@@ -812,14 +831,14 @@
* Report that this fragment would like to participate in populating
* the options menu by receiving a call to {@link #onCreateOptionsMenu}
* and related methods.
- *
+ *
* @param hasMenu If true, the fragment has menu items to contribute.
*/
public void setHasOptionsMenu(boolean hasMenu) {
if (mHasMenu != hasMenu) {
mHasMenu = hasMenu;
if (isAdded() && !isHidden()) {
- mActivity.supportInvalidateOptionsMenu();
+ mHost.onSupportInvalidateOptionsMenu();
}
}
}
@@ -837,7 +856,7 @@
if (mMenuVisible != menuVisible) {
mMenuVisible = menuVisible;
if (mHasMenu && isAdded() && !isHidden()) {
- mActivity.supportInvalidateOptionsMenu();
+ mHost.onSupportInvalidateOptionsMenu();
}
}
}
@@ -878,42 +897,42 @@
if (mLoaderManager != null) {
return mLoaderManager;
}
- if (mActivity == null) {
+ if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, true);
+ mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, true);
return mLoaderManager;
}
-
+
/**
- * Call {@link Activity#startActivity(Intent)} on the fragment's
+ * Call {@link Activity#startActivity(Intent)} from the fragment's
* containing Activity.
*/
public void startActivity(Intent intent) {
- if (mActivity == null) {
+ if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
- mActivity.startActivityFromFragment(this, intent, -1);
+ mHost.onStartActivityFromFragment(this /*fragment*/, intent, -1);
}
-
+
/**
- * Call {@link Activity#startActivityForResult(Intent, int)} on the fragment's
+ * Call {@link Activity#startActivityForResult(Intent, int)} from the fragment's
* containing Activity.
*/
public void startActivityForResult(Intent intent, int requestCode) {
- if (mActivity == null) {
+ if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
- mActivity.startActivityFromFragment(this, intent, requestCode);
+ mHost.onStartActivityFromFragment(this /*fragment*/, intent, requestCode);
}
-
+
/**
* Receive the result from a previous call to
* {@link #startActivityForResult(Intent, int)}. This follows the
* related Activity API as described there in
* {@link Activity#onActivityResult(int, int, Intent)}.
- *
+ *
* @param requestCode The integer request code originally supplied to
* startActivityForResult(), allowing you to identify who this
* result came from.
@@ -924,19 +943,142 @@
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
}
-
+
+ /**
+ * Requests permissions to be granted to this application. These permissions
+ * must be requested in your manifest, they should not be granted to your app,
+ * and they should have protection level {@link android.content.pm.PermissionInfo
+ * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
+ * the platform or a third-party app.
+ * <p>
+ * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
+ * are granted at install time if requested in the manifest. Signature permissions
+ * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
+ * install time if requested in the manifest and the signature of your app matches
+ * the signature of the app declaring the permissions.
+ * </p>
+ * <p>
+ * If your app does not have the requested permissions the user will be presented
+ * with UI for accepting them. After the user has accepted or rejected the
+ * requested permissions you will receive a callback on {@link
+ * #onRequestPermissionsResult(int, String[], int[])} reporting whether the
+ * permissions were granted or not.
+ * </p>
+ * <p>
+ * Note that requesting a permission does not guarantee it will be granted and
+ * your app should be able to run without having this permission.
+ * </p>
+ * <p>
+ * This method may start an activity allowing the user to choose which permissions
+ * to grant and which to reject. Hence, you should be prepared that your activity
+ * may be paused and resumed. Further, granting some permissions may require
+ * a restart of you application. In such a case, the system will recreate the
+ * activity stack before delivering the result to {@link
+ * #onRequestPermissionsResult(int, String[], int[])}.
+ * </p>
+ * <p>
+ * When checking whether you have a permission you should use {@link
+ * android.content.Context#checkSelfPermission(String)}.
+ * </p>
+ * <p>
+ * A sample permissions request looks like this:
+ * </p>
+ * <code><pre><p>
+ * private void showContacts() {
+ * if (getActivity().checkSelfPermission(Manifest.permission.READ_CONTACTS)
+ * != PackageManager.PERMISSION_GRANTED) {
+ * requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
+ * PERMISSIONS_REQUEST_READ_CONTACTS);
+ * } else {
+ * doShowContacts();
+ * }
+ * }
+ *
+ * {@literal @}Override
+ * public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ * int[] grantResults) {
+ * if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS
+ * && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ * doShowContacts();
+ * }
+ * }
+ * </code></pre></p>
+ *
+ * @param permissions The requested permissions.
+ * @param requestCode Application specific request code to match with a result
+ * reported to {@link #onRequestPermissionsResult(int, String[], int[])}.
+ *
+ * @see #onRequestPermissionsResult(int, String[], int[])
+ * @see android.content.Context#checkSelfPermission(String)
+ */
+ public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
+ if (mHost == null) {
+ throw new IllegalStateException("Fragment " + this + " not attached to Activity");
+ }
+ mHost.onRequestPermissionsFromFragment(this, permissions,requestCode);
+ }
+
+ /**
+ * Callback for the result from requesting permissions. This method
+ * is invoked for every call on {@link #requestPermissions(String[], int)}.
+ * <p>
+ * <strong>Note:</strong> It is possible that the permissions request interaction
+ * with the user is interrupted. In this case you will receive empty permissions
+ * and results arrays which should be treated as a cancellation.
+ * </p>
+ *
+ * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
+ * @param permissions The requested permissions. Never null.
+ * @param grantResults The grant results for the corresponding permissions
+ * which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
+ * or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
+ *
+ * @see #requestPermissions(String[], int)
+ */
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ /* callback - do nothing */
+ }
+
+ /**
+ * Gets whether you should show UI with rationale for requesting a permission.
+ * You should do this only if you do not have the permission and the context in
+ * which the permission is requested does not clearly communicate to the user
+ * what would be the benefit from granting this permission.
+ * <p>
+ * For example, if you write a camera app, requesting the camera permission
+ * would be expected by the user and no rationale for why it is requested is
+ * needed. If however, the app needs location for tagging photos then a non-tech
+ * savvy user may wonder how location is related to taking photos. In this case
+ * you may choose to show UI with rationale of requesting this permission.
+ * </p>
+ *
+ * @param permission A permission your app wants to request.
+ * @return Whether you can show permission rationale UI.
+ *
+ * @see Context#checkSelfPermission(String)
+ * @see #requestPermissions(String[], int)
+ * @see #onRequestPermissionsResult(int, String[], int[])
+ */
+ public boolean shouldShowRequestPermissionRationale(@NonNull String permission) {
+ if (mHost != null) {
+ return mHost.onShouldShowRequestPermissionRationale(permission);
+ }
+ return false;
+ }
+
/**
* @hide Hack so that DialogFragment can make its Dialog before creating
* its views, and the view construction can use the dialog's context for
* inflation. Maybe this should become a public API. Note sure.
*/
public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
- LayoutInflater result = mActivity.getLayoutInflater().cloneInContext(mActivity);
+ LayoutInflater result = mHost.onGetLayoutInflater();
getChildFragmentManager(); // Init if needed; use raw implementation below.
LayoutInflaterCompat.setFactory(result, mChildFragmentManager.getLayoutInflaterFactory());
return result;
}
-
+
/**
* Called when a fragment is being created as part of a view layout
* inflation, typically from setting the content view of an activity. This
@@ -973,31 +1115,61 @@
* {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentArguments.java
* create}
*
- * @param activity The Activity that is inflating this fragment.
+ * @param context The Activity that is inflating this fragment.
* @param attrs The attributes at the tag where the fragment is
* being created.
* @param savedInstanceState If the fragment is being re-created from
* a previous saved state, this is the state.
*/
+ public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) {
+ mCalled = true;
+ final Activity hostActivity = mHost == null ? null : mHost.getActivity();
+ if (hostActivity != null) {
+ mCalled = false;
+ onInflate(hostActivity, attrs, savedInstanceState);
+ }
+ }
+
+ /**
+ * Called when a fragment is being created as part of a view layout
+ * inflation, typically from setting the content view of an activity.
+ * <p>Deprecated. See {@link #onInflate(Context, AttributeSet, Bundle)}.
+ */
+ @Deprecated
public void onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState) {
mCalled = true;
}
/**
+ * Called when a fragment is first attached to its context.
+ * {@link #onCreate(Bundle)} will be called after this.
+ */
+ public void onAttach(Context context) {
+ mCalled = true;
+ final Activity hostActivity = mHost == null ? null : mHost.getActivity();
+ if (hostActivity != null) {
+ mCalled = false;
+ onAttach(hostActivity);
+ }
+ }
+
+ /**
* Called when a fragment is first attached to its activity.
* {@link #onCreate(Bundle)} will be called after this.
+ * <p>Deprecated. See {@link #onAttach(Context)}.
*/
+ @Deprecated
public void onAttach(Activity activity) {
mCalled = true;
}
-
+
/**
* Called when a fragment loads an animation.
*/
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
return null;
}
-
+
/**
* Called to do initial creation of a fragment. This is called after
* {@link #onAttach(Activity)} and before
@@ -1104,19 +1276,19 @@
*/
public void onStart() {
mCalled = true;
-
+
if (!mLoadersStarted) {
mLoadersStarted = true;
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);
+ mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
}
if (mLoaderManager != null) {
mLoaderManager.doStart();
}
}
}
-
+
/**
* Called when the fragment is visible to the user and actively running.
* This is generally
@@ -1198,7 +1370,7 @@
// + " mLoaderManager=" + mLoaderManager);
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);
+ mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
}
if (mLoaderManager != null) {
mLoaderManager.doDestroy();
@@ -1223,7 +1395,7 @@
mBackStackNesting = 0;
mFragmentManager = null;
mChildFragmentManager = null;
- mActivity = null;
+ mHost = null;
mFragmentId = 0;
mContainerId = 0;
mTag = null;
@@ -1335,6 +1507,7 @@
* It is not safe to hold onto the context menu after this method returns.
* {@inheritDoc}
*/
+ @Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
getActivity().onCreateContextMenu(menu, v, menuInfo);
}
@@ -1678,9 +1851,9 @@
writer.print(prefix); writer.print("mFragmentManager=");
writer.println(mFragmentManager);
}
- if (mActivity != null) {
- writer.print(prefix); writer.print("mActivity=");
- writer.println(mActivity);
+ if (mHost != null) {
+ writer.print(prefix); writer.print("mHost=");
+ writer.println(mHost);
}
if (mParentFragment != null) {
writer.print(prefix); writer.print("mParentFragment=");
@@ -1741,10 +1914,10 @@
void instantiateChildFragmentManager() {
mChildFragmentManager = new FragmentManagerImpl();
- mChildFragmentManager.attachActivity(mActivity, new FragmentContainer() {
+ mChildFragmentManager.attachController(mHost, new FragmentContainer() {
@Override
@Nullable
- public View findViewById(int id) {
+ public View onFindViewById(int id) {
if (mView == null) {
throw new IllegalStateException("Fragment does not have a view");
}
@@ -1752,7 +1925,7 @@
}
@Override
- public boolean hasView() {
+ public boolean onHasView() {
return (mView != null);
}
}, this);
@@ -1974,10 +2147,10 @@
mLoadersStarted = false;
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);
+ mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
}
if (mLoaderManager != null) {
- if (!mActivity.mRetaining) {
+ if (!mRetaining) {
mLoaderManager.doStop();
} else {
mLoaderManager.doRetain();
diff -Nur support-v4-22.2.1/android/support/v4/app/FragmentActivity.java support-v4-23.0.0/android/support/v4/app/FragmentActivity.java
--- support-v4-22.2.1/android/support/v4/app/FragmentActivity.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/FragmentActivity.java 2015-08-10 21:01:18.000000000 +0900
@@ -31,6 +31,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -40,6 +41,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
/**
* Base class for activities that want to use the support-based
@@ -73,11 +75,13 @@
* state, this may be a snapshot slightly before what the user last saw.</p>
* </ul>
*/
-public class FragmentActivity extends Activity {
+public class FragmentActivity extends BaseFragmentActivityHoneycomb implements
+ ActivityCompat.OnRequestPermissionsResultCallback,
+ ActivityCompatApi23.RequestPermissionsRequestCodeValidator {
private static final String TAG = "FragmentActivity";
-
+
static final String FRAGMENTS_TAG = "android:support:fragments";
-
+
// This is the SDK API version of Honeycomb (3.0).
private static final int HONEYCOMB = 11;
@@ -103,21 +107,8 @@
}
};
- final FragmentManagerImpl mFragments = new FragmentManagerImpl();
- final FragmentContainer mContainer = new FragmentContainer() {
- @Override
- @Nullable
- public View findViewById(int id) {
- return FragmentActivity.this.findViewById(id);
- }
+ final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
- @Override
- public boolean hasView() {
- Window window = FragmentActivity.this.getWindow();
- return (window != null && window.peekDecorView() != null);
- }
- };
-
boolean mCreated;
boolean mResumed;
boolean mStopped;
@@ -125,24 +116,18 @@
boolean mRetaining;
boolean mOptionsMenuInvalidated;
-
- boolean mCheckedForLoaderManager;
- boolean mLoadersStarted;
- SimpleArrayMap<String, LoaderManagerImpl> mAllLoaderManagers;
- LoaderManagerImpl mLoaderManager;
+ boolean mRequestedPermissionsFromFragment;
static final class NonConfigurationInstances {
- Object activity;
Object custom;
- SimpleArrayMap<String, Object> children;
- ArrayList<Fragment> fragments;
- SimpleArrayMap<String, LoaderManagerImpl> loaders;
+ List<Fragment> fragments;
+ SimpleArrayMap<String, LoaderManager> loaders;
}
-
+
// ------------------------------------------------------------------------
// HOOKS INTO ACTIVITY
// ------------------------------------------------------------------------
-
+
/**
* Dispatch incoming result to the correct fragment.
*/
@@ -152,12 +137,15 @@
int index = requestCode>>16;
if (index != 0) {
index--;
- if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) {
+ final int activeFragmentsCount = mFragments.getActiveFragmentsCount();
+ if (activeFragmentsCount == 0 || index < 0 || index >= activeFragmentsCount) {
Log.w(TAG, "Activity result fragment index out of range: 0x"
+ Integer.toHexString(requestCode));
return;
}
- Fragment frag = mFragments.mActive.get(index);
+ final List<Fragment> activeFragments =
+ mFragments.getActiveFragments(new ArrayList<Fragment>(activeFragmentsCount));
+ Fragment frag = activeFragments.get(index);
if (frag == null) {
Log.w(TAG, "Activity result no fragment exists for index: 0x"
+ Integer.toHexString(requestCode));
@@ -166,7 +154,7 @@
}
return;
}
-
+
super.onActivityResult(requestCode, resultCode, data);
}
@@ -175,7 +163,7 @@
* as appropriate.
*/
public void onBackPressed() {
- if (!mFragments.popBackStackImmediate()) {
+ if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
supportFinishAfterTransition();
}
}
@@ -246,20 +234,17 @@
/**
* Perform initialization of all fragments and loaders.
*/
+ @SuppressWarnings("deprecation")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
- mFragments.attachActivity(this, mContainer, null);
- // Old versions of the platform didn't do this!
- if (getLayoutInflater().getFactory() == null) {
- getLayoutInflater().setFactory(this);
- }
-
+ mFragments.attachHost(null /*parent*/);
+
super.onCreate(savedInstanceState);
-
- NonConfigurationInstances nc = (NonConfigurationInstances)
- getLastNonConfigurationInstance();
+
+ NonConfigurationInstances nc =
+ (NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
- mAllLoaderManagers = nc.loaders;
+ mFragments.restoreLoaderNonConfig(nc.loaders);
}
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
@@ -286,22 +271,11 @@
}
return super.onCreatePanelMenu(featureId, menu);
}
-
- /**
- * Add support for inflating the &lt;fragment> tag.
- */
- @Override
- @Nullable
- public View onCreateView(String name, @NonNull Context context, @NonNull AttributeSet attrs) {
- if (!"fragment".equals(name)) {
- return super.onCreateView(name, context, attrs);
- }
- final View v = mFragments.onCreateView(null, name, context, attrs);
- if (v == null) {
- return super.onCreateView(name, context, attrs);
- }
- return v;
+ @Override
+ final View dispatchFragmentsOnCreateView(View parent, String name, Context context,
+ AttributeSet attrs) {
+ return mFragments.onCreateView(parent, name, context, attrs);
}
/**
@@ -314,9 +288,7 @@
doReallyStop(false);
mFragments.dispatchDestroy();
- if (mLoaderManager != null) {
- mLoaderManager.doDestroy();
- }
+ mFragments.doLoaderDestroy();
}
/**
@@ -410,6 +382,13 @@
}
/**
+ * Hook in to note that fragment state is no longer saved.
+ */
+ public void onStateNotSaved() {
+ mFragments.noteStateNotSaved();
+ }
+
+ /**
* Dispatch onResume() to fragments. Note that for better inter-operation
* with older versions of the platform, at the point of this call the
* fragments attached to the activity are <em>not</em> resumed. This means
@@ -485,36 +464,17 @@
Object custom = onRetainCustomNonConfigurationInstance();
- ArrayList<Fragment> fragments = mFragments.retainNonConfig();
- boolean retainLoaders = false;
- if (mAllLoaderManagers != null) {
- // prune out any loader managers that were already stopped and so
- // have nothing useful to retain.
- final int N = mAllLoaderManagers.size();
- LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
- for (int i=N-1; i>=0; i--) {
- loaders[i] = mAllLoaderManagers.valueAt(i);
- }
- for (int i=0; i<N; i++) {
- LoaderManagerImpl lm = loaders[i];
- if (lm.mRetaining) {
- retainLoaders = true;
- } else {
- lm.doDestroy();
- mAllLoaderManagers.remove(lm.mWho);
- }
- }
- }
- if (fragments == null && !retainLoaders && custom == null) {
+ List<Fragment> fragments = mFragments.retainNonConfig();
+ SimpleArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
+
+ if (fragments == null && loaders == null && custom == null) {
return null;
}
-
+
NonConfigurationInstances nci = new NonConfigurationInstances();
- nci.activity = null;
nci.custom = custom;
- nci.children = null;
nci.fragments = fragments;
- nci.loaders = mAllLoaderManagers;
+ nci.loaders = loaders;
return nci;
}
@@ -549,35 +509,13 @@
mFragments.noteStateNotSaved();
mFragments.execPendingActions();
-
- if (!mLoadersStarted) {
- mLoadersStarted = true;
- if (mLoaderManager != null) {
- mLoaderManager.doStart();
- } else if (!mCheckedForLoaderManager) {
- mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);
- // the returned loader manager may be a new one, so we have to start it
- if ((mLoaderManager != null) && (!mLoaderManager.mStarted)) {
- mLoaderManager.doStart();
- }
- }
- mCheckedForLoaderManager = true;
- }
+
+ mFragments.doLoaderStart();
+
// NOTE: HC onStart goes here.
-
+
mFragments.dispatchStart();
- if (mAllLoaderManagers != null) {
- final int N = mAllLoaderManagers.size();
- LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
- for (int i=N-1; i>=0; i--) {
- loaders[i] = mAllLoaderManagers.valueAt(i);
- }
- for (int i=0; i<N; i++) {
- LoaderManagerImpl lm = loaders[i];
- lm.finishRetain();
- lm.doReportStart();
- }
- }
+ mFragments.reportLoaderStart();
}
/**
@@ -589,14 +527,14 @@
mStopped = true;
mHandler.sendEmptyMessage(MSG_REALLY_STOPPED);
-
+
mFragments.dispatchStop();
}
// ------------------------------------------------------------------------
// NEW METHODS
// ------------------------------------------------------------------------
-
+
/**
* Use this instead of {@link #onRetainNonConfigurationInstance()}.
* Retrieve later with {@link #getLastCustomNonConfigurationInstance()}.
@@ -609,6 +547,7 @@
* Return the value previously returned from
* {@link #onRetainCustomNonConfigurationInstance()}.
*/
+ @SuppressWarnings("deprecation")
public Object getLastCustomNonConfigurationInstance() {
NonConfigurationInstances nc = (NonConfigurationInstances)
getLastNonConfigurationInstance();
@@ -659,15 +598,8 @@
writer.print(mResumed); writer.print(" mStopped=");
writer.print(mStopped); writer.print(" mReallyStopped=");
writer.println(mReallyStopped);
- writer.print(innerPrefix); writer.print("mLoadersStarted=");
- writer.println(mLoadersStarted);
- if (mLoaderManager != null) {
- writer.print(prefix); writer.print("Loader Manager ");
- writer.print(Integer.toHexString(System.identityHashCode(mLoaderManager)));
- writer.println(":");
- mLoaderManager.dump(prefix + " ", fd, writer, args);
- }
- mFragments.dump(prefix, fd, writer, args);
+ mFragments.dumpLoaders(innerPrefix, fd, writer, args);
+ mFragments.getSupportFragmentManager().dump(prefix, fd, writer, args);
writer.print(prefix); writer.println("View Hierarchy:");
dumpViewHierarchy(prefix + " ", writer, getWindow().getDecorView());
}
@@ -776,16 +708,7 @@
* tell us what we need to know.
*/
void onReallyStop() {
- if (mLoadersStarted) {
- mLoadersStarted = false;
- if (mLoaderManager != null) {
- if (!mRetaining) {
- mLoaderManager.doStop();
- } else {
- mLoaderManager.doRetain();
- }
- }
- }
+ mFragments.doLoaderStop(mRetaining);
mFragments.dispatchReallyStop();
}
@@ -793,19 +716,24 @@
// ------------------------------------------------------------------------
// FRAGMENT SUPPORT
// ------------------------------------------------------------------------
-
+
/**
* Called when a fragment is attached to the activity.
*/
+ @SuppressWarnings("unused")
public void onAttachFragment(Fragment fragment) {
}
-
+
/**
* Return the FragmentManager for interacting with fragments associated
* with this activity.
*/
public FragmentManager getSupportFragmentManager() {
- return mFragments;
+ return mFragments.getSupportFragmentManager();
+ }
+
+ public LoaderManager getSupportLoaderManager() {
+ return mFragments.getSupportLoaderManager();
}
/**
@@ -820,10 +748,66 @@
super.startActivityForResult(intent, requestCode);
}
+ @Override
+ public final void validateRequestPermissionsRequestCode(int requestCode) {
+ // We use 8 bits of the request code to encode the fragment id when
+ // requesting permissions from a fragment. Hence, requestPermissions()
+ // should validate the code against that but we cannot override it as
+ // we can not then call super and also the ActivityCompat would call
+ // back to this override. To handle this we use dependency inversion
+ // where we are the validator of request codes when requesting
+ // permissions in ActivityCompat.
+ if (mRequestedPermissionsFromFragment) {
+ mRequestedPermissionsFromFragment = false;
+ } else if ((requestCode & 0xffffff00) != 0) {
+ throw new IllegalArgumentException("Can only use lower 8 bits for requestCode");
+ }
+ }
+
+ /**
+ * Callback for the result from requesting permissions. This method
+ * is invoked for every call on {@link #requestPermissions(String[], int)}.
+ * <p>
+ * <strong>Note:</strong> It is possible that the permissions request interaction
+ * with the user is interrupted. In this case you will receive empty permissions
+ * and results arrays which should be treated as a cancellation.
+ * </p>
+ *
+ * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
+ * @param permissions The requested permissions. Never null.
+ * @param grantResults The grant results for the corresponding permissions
+ * which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
+ * or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
+ *
+ * @see #requestPermissions(String[], int)
+ */
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ int index = (requestCode>>8)&0xff;
+ if (index != 0) {
+ index--;
+ final int activeFragmentsCount = mFragments.getActiveFragmentsCount();
+ if (activeFragmentsCount == 0 || index < 0 || index >= activeFragmentsCount) {
+ Log.w(TAG, "Activity result fragment index out of range: 0x"
+ + Integer.toHexString(requestCode));
+ return;
+ }
+ final List<Fragment> activeFragments =
+ mFragments.getActiveFragments(new ArrayList<Fragment>(activeFragmentsCount));
+ Fragment frag = activeFragments.get(index);
+ if (frag == null) {
+ Log.w(TAG, "Activity result no fragment exists for index: 0x"
+ + Integer.toHexString(requestCode));
+ } else {
+ frag.onRequestPermissionsResult(requestCode&0xff, permissions, grantResults);
+ }
+ }
+ }
+
/**
* Called by Fragment.startActivityForResult() to implement its behavior.
*/
- public void startActivityFromFragment(Fragment fragment, Intent intent,
+ public void startActivityFromFragment(Fragment fragment, Intent intent,
int requestCode) {
if (requestCode == -1) {
super.startActivityForResult(intent, -1);
@@ -834,47 +818,98 @@
}
super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff));
}
-
- void invalidateSupportFragment(String who) {
- //Log.v(TAG, "invalidateSupportFragment: who=" + who);
- if (mAllLoaderManagers != null) {
- LoaderManagerImpl lm = mAllLoaderManagers.get(who);
- if (lm != null && !lm.mRetaining) {
- lm.doDestroy();
- mAllLoaderManagers.remove(who);
- }
- }
- }
-
- // ------------------------------------------------------------------------
- // LOADER SUPPORT
- // ------------------------------------------------------------------------
-
+
/**
- * Return the LoaderManager for this fragment, creating it if needed.
+ * Called by Fragment.requestPermissions() to implement its behavior.
*/
- public LoaderManager getSupportLoaderManager() {
- if (mLoaderManager != null) {
- return mLoaderManager;
+ private void requestPermissionsFromFragment(Fragment fragment, String[] permissions,
+ int requestCode) {
+ if (requestCode == -1) {
+ ActivityCompat.requestPermissions(this, permissions, requestCode);
+ return;
+ }
+ if ((requestCode&0xffffff00) != 0) {
+ throw new IllegalArgumentException("Can only use lower 8 bits for requestCode");
}
- mCheckedForLoaderManager = true;
- mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);
- return mLoaderManager;
+ mRequestedPermissionsFromFragment = true;
+ ActivityCompat.requestPermissions(this, permissions,
+ ((fragment.mIndex + 1) << 8) + (requestCode & 0xff));
}
-
- LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
- if (mAllLoaderManagers == null) {
- mAllLoaderManagers = new SimpleArrayMap<String, LoaderManagerImpl>();
- }
- LoaderManagerImpl lm = mAllLoaderManagers.get(who);
- if (lm == null) {
- if (create) {
- lm = new LoaderManagerImpl(who, this, started);
- mAllLoaderManagers.put(who, lm);
- }
- } else {
- lm.updateActivity(this);
+
+ class HostCallbacks extends FragmentHostCallback<FragmentActivity> {
+ public HostCallbacks() {
+ super(FragmentActivity.this /*fragmentActivity*/);
+ }
+
+ @Override
+ public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ FragmentActivity.this.dump(prefix, fd, writer, args);
+ }
+
+ @Override
+ public boolean onShouldSaveFragmentState(Fragment fragment) {
+ return !isFinishing();
+ }
+
+ @Override
+ public LayoutInflater onGetLayoutInflater() {
+ return FragmentActivity.this.getLayoutInflater().cloneInContext(FragmentActivity.this);
+ }
+
+ @Override
+ public FragmentActivity onGetHost() {
+ return FragmentActivity.this;
+ }
+
+ @Override
+ public void onSupportInvalidateOptionsMenu() {
+ FragmentActivity.this.supportInvalidateOptionsMenu();
+ }
+
+ @Override
+ public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
+ FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode);
+ }
+
+ @Override
+ public void onRequestPermissionsFromFragment(@NonNull Fragment fragment,
+ @NonNull String[] permissions, int requestCode) {
+ FragmentActivity.this.requestPermissionsFromFragment(fragment, permissions,
+ requestCode);
+ }
+
+ @Override
+ public boolean onShouldShowRequestPermissionRationale(@NonNull String permission) {
+ return ActivityCompat.shouldShowRequestPermissionRationale(
+ FragmentActivity.this, permission);
+ }
+
+ @Override
+ public boolean onHasWindowAnimations() {
+ return getWindow() != null;
+ }
+
+ @Override
+ public int onGetWindowAnimations() {
+ final Window w = getWindow();
+ return (w == null) ? 0 : w.getAttributes().windowAnimations;
+ }
+
+ @Override
+ public void onAttachFragment(Fragment fragment) {
+ FragmentActivity.this.onAttachFragment(fragment);
+ }
+
+ @Nullable
+ @Override
+ public View onFindViewById(int id) {
+ return FragmentActivity.this.findViewById(id);
+ }
+
+ @Override
+ public boolean onHasView() {
+ final Window w = getWindow();
+ return (w != null && w.peekDecorView() != null);
}
- return lm;
}
}
diff -Nur support-v4-22.2.1/android/support/v4/app/FragmentContainer.java support-v4-23.0.0/android/support/v4/app/FragmentContainer.java
--- support-v4-22.2.1/android/support/v4/app/FragmentContainer.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/FragmentContainer.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,23 @@
+package android.support.v4.app;
+
+import android.support.annotation.IdRes;
+import android.support.annotation.Nullable;
+import android.view.View;
+
+
+/**
+ * Callbacks to a {@link Fragment}'s container.
+ */
+public abstract class FragmentContainer {
+ /**
+ * Return the view with the given resource ID. May return {@code null} if the
+ * view is not a child of this container.
+ */
+ @Nullable
+ public abstract View onFindViewById(@IdRes int id);
+
+ /**
+ * Return {@code true} if the container holds any view.
+ */
+ public abstract boolean onHasView();
+}
diff -Nur support-v4-22.2.1/android/support/v4/app/FragmentController.java support-v4-23.0.0/android/support/v4/app/FragmentController.java
--- support-v4-22.2.1/android/support/v4/app/FragmentController.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/FragmentController.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,396 @@
+/*
+ * 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.app;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Parcelable;
+import android.support.v4.util.SimpleArrayMap;
+import android.util.AttributeSet;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides integration points with a {@link FragmentManager} for a fragment host.
+ * <p>
+ * It is the responsibility of the host to take care of the Fragment's lifecycle.
+ * The methods provided by {@link FragmentController} are for that purpose.
+ */
+public class FragmentController {
+ private final FragmentHostCallback<?> mHost;
+
+ /**
+ * Returns a {@link FragmentController}.
+ */
+ public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
+ return new FragmentController(callbacks);
+ }
+
+ private FragmentController(FragmentHostCallback<?> callbacks) {
+ mHost = callbacks;
+ }
+
+ /**
+ * Returns a {@link FragmentManager} for this controller.
+ */
+ public FragmentManager getSupportFragmentManager() {
+ return mHost.getFragmentManagerImpl();
+ }
+
+ /**
+ * Returns a {@link LoaderManager}.
+ */
+ public LoaderManager getSupportLoaderManager() {
+ return mHost.getLoaderManagerImpl();
+ }
+
+ /**
+ * Returns the number of active fragments.
+ */
+ public int getActiveFragmentsCount() {
+ final List<Fragment> actives = mHost.mFragmentManager.mActive;
+ return actives == null ? 0 : actives.size();
+ }
+
+ /**
+ * Returns the list of active fragments.
+ */
+ public List<Fragment> getActiveFragments(List<Fragment> actives) {
+ if (mHost.mFragmentManager.mActive == null) {
+ return null;
+ }
+ if (actives == null) {
+ actives = new ArrayList<Fragment>(getActiveFragmentsCount());
+ }
+ actives.addAll(mHost.mFragmentManager.mActive);
+ return actives;
+ }
+
+ /**
+ * Attaches the host to the FragmentManager for this controller. The host must be
+ * attached before the FragmentManager can be used to manage Fragments.
+ */
+ public void attachHost(Fragment parent) {
+ mHost.mFragmentManager.attachController(
+ mHost, mHost /*container*/, parent);
+ }
+
+ /**
+ * Instantiates a Fragment's view.
+ *
+ * @param parent The parent that the created view will be placed
+ * in; <em>note that this may be null</em>.
+ * @param name Tag name to be inflated.
+ * @param context The context the view is being created in.
+ * @param attrs Inflation attributes as specified in XML file.
+ *
+ * @return view the newly created view
+ */
+ public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+ return mHost.mFragmentManager.onCreateView(parent, name, context, attrs);
+ }
+
+ /**
+ * Marks the fragment state as unsaved. This allows for "state loss" detection.
+ */
+ public void noteStateNotSaved() {
+ mHost.mFragmentManager.noteStateNotSaved();
+ }
+
+ /**
+ * Saves the state for all Fragments.
+ */
+ public Parcelable saveAllState() {
+ return mHost.mFragmentManager.saveAllState();
+ }
+
+ /**
+ * Restores the saved state for all Fragments. The given Fragment list are Fragment
+ * instances retained across configuration changes.
+ *
+ * @see #retainNonConfig()
+ */
+ public void restoreAllState(Parcelable state, List<Fragment> nonConfigList) {
+ mHost.mFragmentManager.restoreAllState(state, nonConfigList);
+ }
+
+ /**
+ * Returns a list of Fragments that have opted to retain their instance across
+ * configuration changes.
+ */
+ public List<Fragment> retainNonConfig() {
+ return mHost.mFragmentManager.retainNonConfig();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the create state.
+ * <p>Call when Fragments should be created.
+ *
+ * @see Fragment#onCreate(Bundle)
+ */
+ public void dispatchCreate() {
+ mHost.mFragmentManager.dispatchCreate();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the activity created state.
+ * <p>Call when Fragments should be informed their host has been created.
+ *
+ * @see Fragment#onActivityCreated(Bundle)
+ */
+ public void dispatchActivityCreated() {
+ mHost.mFragmentManager.dispatchActivityCreated();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the start state.
+ * <p>Call when Fragments should be started.
+ *
+ * @see Fragment#onStart()
+ */
+ public void dispatchStart() {
+ mHost.mFragmentManager.dispatchStart();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the resume state.
+ * <p>Call when Fragments should be resumed.
+ *
+ * @see Fragment#onResume()
+ */
+ public void dispatchResume() {
+ mHost.mFragmentManager.dispatchResume();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the pause state.
+ * <p>Call when Fragments should be paused.
+ *
+ * @see Fragment#onPause()
+ */
+ public void dispatchPause() {
+ mHost.mFragmentManager.dispatchPause();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the stop state.
+ * <p>Call when Fragments should be stopped.
+ *
+ * @see Fragment#onStop()
+ */
+ public void dispatchStop() {
+ mHost.mFragmentManager.dispatchStop();
+ }
+
+ public void dispatchReallyStop() {
+ mHost.mFragmentManager.dispatchReallyStop();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the destroy view state.
+ * <p>Call when the Fragment's views should be destroyed.
+ *
+ * @see Fragment#onDestroyView()
+ */
+ public void dispatchDestroyView() {
+ mHost.mFragmentManager.dispatchDestroyView();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the destroy state.
+ * <p>Call when Fragments should be destroyed.
+ *
+ * @see Fragment#onDestroy()
+ */
+ public void dispatchDestroy() {
+ mHost.mFragmentManager.dispatchDestroy();
+ }
+
+ /**
+ * Lets all Fragments managed by the controller's FragmentManager
+ * know a configuration change occurred.
+ * <p>Call when there is a configuration change.
+ *
+ * @see Fragment#onConfigurationChanged(Configuration)
+ */
+ public void dispatchConfigurationChanged(Configuration newConfig) {
+ mHost.mFragmentManager.dispatchConfigurationChanged(newConfig);
+ }
+
+ /**
+ * Lets all Fragments managed by the controller's FragmentManager
+ * know the device is in a low memory condition.
+ * <p>Call when the device is low on memory and Fragment's should trim
+ * their memory usage.
+ *
+ * @see Fragment#onLowMemory()
+ */
+ public void dispatchLowMemory() {
+ mHost.mFragmentManager.dispatchLowMemory();
+ }
+
+ /**
+ * Lets all Fragments managed by the controller's FragmentManager
+ * know they should create an options menu.
+ * <p>Call when the Fragment should create an options menu.
+ *
+ * @return {@code true} if the options menu contains items to display
+ * @see Fragment#onCreateOptionsMenu(Menu, MenuInflater)
+ */
+ public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ return mHost.mFragmentManager.dispatchCreateOptionsMenu(menu, inflater);
+ }
+
+ /**
+ * Lets all Fragments managed by the controller's FragmentManager
+ * know they should prepare their options menu for display.
+ * <p>Call immediately before displaying the Fragment's options menu.
+ *
+ * @return {@code true} if the options menu contains items to display
+ * @see Fragment#onPrepareOptionsMenu(Menu)
+ */
+ public boolean dispatchPrepareOptionsMenu(Menu menu) {
+ return mHost.mFragmentManager.dispatchPrepareOptionsMenu(menu);
+ }
+
+ /**
+ * Sends an option item selection event to the Fragments managed by the
+ * controller's FragmentManager. Once the event has been consumed,
+ * no additional handling will be performed.
+ * <p>Call immediately after an options menu item has been selected
+ *
+ * @return {@code true} if the options menu selection event was consumed
+ * @see Fragment#onOptionsItemSelected(MenuItem)
+ */
+ public boolean dispatchOptionsItemSelected(MenuItem item) {
+ return mHost.mFragmentManager.dispatchOptionsItemSelected(item);
+ }
+
+ /**
+ * Sends a context item selection event to the Fragments managed by the
+ * controller's FragmentManager. Once the event has been consumed,
+ * no additional handling will be performed.
+ * <p>Call immediately after an options menu item has been selected
+ *
+ * @return {@code true} if the context menu selection event was consumed
+ * @see Fragment#onContextItemSelected(MenuItem)
+ */
+ public boolean dispatchContextItemSelected(MenuItem item) {
+ return mHost.mFragmentManager.dispatchContextItemSelected(item);
+ }
+
+ /**
+ * Lets all Fragments managed by the controller's FragmentManager
+ * know their options menu has closed.
+ * <p>Call immediately after closing the Fragment's options menu.
+ *
+ * @see Fragment#onOptionsMenuClosed(Menu)
+ */
+ public void dispatchOptionsMenuClosed(Menu menu) {
+ mHost.mFragmentManager.dispatchOptionsMenuClosed(menu);
+ }
+
+ /**
+ * Execute any pending actions for the Fragments managed by the
+ * controller's FragmentManager.
+ * <p>Call when queued actions can be performed [eg when the
+ * Fragment moves into a start or resume state].
+ * @return {@code true} if queued actions were performed
+ */
+ public boolean execPendingActions() {
+ return mHost.mFragmentManager.execPendingActions();
+ }
+
+ /**
+ * Starts the loaders.
+ */
+ public void doLoaderStart() {
+ mHost.doLoaderStart();
+ }
+
+ /**
+ * Stops the loaders, optionally retaining their state. This is useful for keeping the
+ * loader state across configuration changes.
+ *
+ * @param retain When {@code true}, the loaders aren't stopped, but, their instances
+ * are retained in a started state
+ */
+ public void doLoaderStop(boolean retain) {
+ mHost.doLoaderStop(retain);
+ }
+
+ /**
+ * Retains the state of each of the loaders.
+ */
+ public void doLoaderRetain() {
+ mHost.doLoaderRetain();
+ }
+
+ /**
+ * Destroys the loaders and, if their state is not being retained, removes them.
+ */
+ public void doLoaderDestroy() {
+ mHost.doLoaderDestroy();
+ }
+
+ /**
+ * Lets the loaders know the host is ready to receive notifications.
+ */
+ public void reportLoaderStart() {
+ mHost.reportLoaderStart();
+ }
+
+ /**
+ * Returns a list of LoaderManagers that have opted to retain their instance across
+ * configuration changes.
+ */
+ public SimpleArrayMap<String, LoaderManager> retainLoaderNonConfig() {
+ return mHost.retainLoaderNonConfig();
+ }
+
+ /**
+ * Restores the saved state for all LoaderManagers. The given LoaderManager list are
+ * LoaderManager instances retained across configuration changes.
+ *
+ * @see #retainLoaderNonConfig()
+ */
+ public void restoreLoaderNonConfig(SimpleArrayMap<String, LoaderManager> loaderManagers) {
+ mHost.restoreLoaderNonConfig(loaderManagers);
+ }
+
+ /**
+ * Dumps the current state of the loaders.
+ */
+ public void dumpLoaders(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ mHost.dumpLoaders(prefix, fd, writer, args);
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/app/FragmentHostCallback.java support-v4-23.0.0/android/support/v4/app/FragmentHostCallback.java
--- support-v4-22.2.1/android/support/v4/app/FragmentHostCallback.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/FragmentHostCallback.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,322 @@
+/*
+ * 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.app;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.util.SimpleArrayMap;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Integration points with the Fragment host.
+ * <p>
+ * Fragments may be hosted by any object; such as an {@link Activity}. In order to
+ * host fragments, implement {@link FragmentHostCallback}, overriding the methods
+ * applicable to the host.
+ */
+public abstract class FragmentHostCallback<E> extends FragmentContainer {
+ private final Activity mActivity;
+ final Context mContext;
+ private final Handler mHandler;
+ final int mWindowAnimations;
+ final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
+ private SimpleArrayMap<String, LoaderManager> mAllLoaderManagers;
+ private LoaderManagerImpl mLoaderManager;
+ private boolean mCheckedForLoaderManager;
+ private boolean mLoadersStarted;
+
+ public FragmentHostCallback(Context context, Handler handler, int windowAnimations) {
+ this(null /*activity*/, context, handler, windowAnimations);
+ }
+
+ FragmentHostCallback(FragmentActivity activity) {
+ this(activity, activity /*context*/, activity.mHandler, 0 /*windowAnimations*/);
+ }
+
+ FragmentHostCallback(Activity activity, Context context, Handler handler,
+ int windowAnimations) {
+ mActivity = activity;
+ mContext = context;
+ mHandler = handler;
+ mWindowAnimations = windowAnimations;
+ }
+
+ /**
+ * Print internal state into the given stream.
+ *
+ * @param prefix Desired prefix to prepend at each line of output.
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param writer The PrintWriter to which you should dump your state. This will be closed
+ * for you after you return.
+ * @param args additional arguments to the dump request.
+ */
+ public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ }
+
+ /**
+ * Return {@code true} if the fragment's state needs to be saved.
+ */
+ public boolean onShouldSaveFragmentState(Fragment fragment) {
+ return true;
+ }
+
+ /**
+ * Return a {@link LayoutInflater}.
+ * See {@link Activity#getLayoutInflater()}.
+ */
+ public LayoutInflater onGetLayoutInflater() {
+ return (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ /**
+ * Return the object that's currently hosting the fragment. If a {@link Fragment}
+ * is hosted by a {@link FragmentActivity}, the object returned here should be
+ * the same object returned from {@link Fragment#getActivity()}.
+ */
+ @Nullable
+ public abstract E onGetHost();
+
+ /**
+ * Invalidates the activity's options menu.
+ * See {@link FragmentActivity#supportInvalidateOptionsMenu()}
+ */
+ public void onSupportInvalidateOptionsMenu() {
+ }
+
+ /**
+ * Starts a new {@link Activity} from the given fragment.
+ * See {@link FragmentActivity#startActivityForResult(Intent, int)}.
+ */
+ public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
+ if (requestCode != -1) {
+ throw new IllegalStateException(
+ "Starting activity with a requestCode requires a FragmentActivity host");
+ }
+ mContext.startActivity(intent);
+ }
+
+ /**
+ * Requests permissions from the given fragment.
+ * See {@link FragmentActivity#requestPermissions(String[], int)}
+ */
+ public void onRequestPermissionsFromFragment(@NonNull Fragment fragment,
+ @NonNull String[] permissions, int requestCode) {
+ }
+
+ /**
+ * Checks wehter to show permission rationale UI from a fragment.
+ * See {@link FragmentActivity#shouldShowRequestPermissionRationale(String)}
+ */
+ public boolean onShouldShowRequestPermissionRationale(@NonNull String permission) {
+ return false;
+ }
+
+ /**
+ * Return {@code true} if there are window animations.
+ */
+ public boolean onHasWindowAnimations() {
+ return true;
+ }
+
+ /**
+ * Return the window animations.
+ */
+ public int onGetWindowAnimations() {
+ return mWindowAnimations;
+ }
+
+ @Nullable
+ @Override
+ public View onFindViewById(int id) {
+ return null;
+ }
+
+ @Override
+ public boolean onHasView() {
+ return true;
+ }
+
+ Activity getActivity() {
+ return mActivity;
+ }
+
+ Context getContext() {
+ return mContext;
+ }
+
+ Handler getHandler() {
+ return mHandler;
+ }
+
+ FragmentManagerImpl getFragmentManagerImpl() {
+ return mFragmentManager;
+ }
+
+ LoaderManagerImpl getLoaderManagerImpl() {
+ if (mLoaderManager != null) {
+ return mLoaderManager;
+ }
+ mCheckedForLoaderManager = true;
+ mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true /*create*/);
+ return mLoaderManager;
+ }
+
+ void inactivateFragment(String who) {
+ //Log.v(TAG, "invalidateSupportFragment: who=" + who);
+ if (mAllLoaderManagers != null) {
+ LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who);
+ if (lm != null && !lm.mRetaining) {
+ lm.doDestroy();
+ mAllLoaderManagers.remove(who);
+ }
+ }
+ }
+
+ void onAttachFragment(Fragment fragment) {
+ }
+
+ void doLoaderStart() {
+ if (mLoadersStarted) {
+ return;
+ }
+ mLoadersStarted = true;
+
+ if (mLoaderManager != null) {
+ mLoaderManager.doStart();
+ } else if (!mCheckedForLoaderManager) {
+ mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);
+ // the returned loader manager may be a new one, so we have to start it
+ if ((mLoaderManager != null) && (!mLoaderManager.mStarted)) {
+ mLoaderManager.doStart();
+ }
+ }
+ mCheckedForLoaderManager = true;
+ }
+
+ // retain -- whether to stop the loader or retain it
+ void doLoaderStop(boolean retain) {
+ if (mLoaderManager == null) {
+ return;
+ }
+
+ if (!mLoadersStarted) {
+ return;
+ }
+ mLoadersStarted = false;
+
+ if (retain) {
+ mLoaderManager.doRetain();
+ } else {
+ mLoaderManager.doStop();
+ }
+ }
+
+ void doLoaderRetain() {
+ if (mLoaderManager == null) {
+ return;
+ }
+ mLoaderManager.doRetain();
+ }
+
+ void doLoaderDestroy() {
+ if (mLoaderManager == null) {
+ return;
+ }
+ mLoaderManager.doDestroy();
+ }
+
+ void reportLoaderStart() {
+ if (mAllLoaderManagers != null) {
+ final int N = mAllLoaderManagers.size();
+ LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
+ for (int i=N-1; i>=0; i--) {
+ loaders[i] = (LoaderManagerImpl) mAllLoaderManagers.valueAt(i);
+ }
+ for (int i=0; i<N; i++) {
+ LoaderManagerImpl lm = loaders[i];
+ lm.finishRetain();
+ lm.doReportStart();
+ }
+ }
+ }
+
+ LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
+ if (mAllLoaderManagers == null) {
+ mAllLoaderManagers = new SimpleArrayMap<String, LoaderManager>();
+ }
+ LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who);
+ if (lm == null) {
+ if (create) {
+ lm = new LoaderManagerImpl(who, this, started);
+ mAllLoaderManagers.put(who, lm);
+ }
+ } else {
+ lm.updateHostController(this);
+ }
+ return lm;
+ }
+
+ SimpleArrayMap<String, LoaderManager> retainLoaderNonConfig() {
+ boolean retainLoaders = false;
+ if (mAllLoaderManagers != null) {
+ // prune out any loader managers that were already stopped and so
+ // have nothing useful to retain.
+ final int N = mAllLoaderManagers.size();
+ LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
+ for (int i=N-1; i>=0; i--) {
+ loaders[i] = (LoaderManagerImpl) mAllLoaderManagers.valueAt(i);
+ }
+ for (int i=0; i<N; i++) {
+ LoaderManagerImpl lm = loaders[i];
+ if (lm.mRetaining) {
+ retainLoaders = true;
+ } else {
+ lm.doDestroy();
+ mAllLoaderManagers.remove(lm.mWho);
+ }
+ }
+ }
+
+ if (retainLoaders) {
+ return mAllLoaderManagers;
+ }
+ return null;
+ }
+
+ void restoreLoaderNonConfig(SimpleArrayMap<String, LoaderManager> loaderManagers) {
+ mAllLoaderManagers = loaderManagers;
+ }
+
+ void dumpLoaders(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ writer.print(prefix); writer.print("mLoadersStarted=");
+ writer.println(mLoadersStarted);
+ if (mLoaderManager != null) {
+ writer.print(prefix); writer.print("Loader Manager ");
+ writer.print(Integer.toHexString(System.identityHashCode(mLoaderManager)));
+ writer.println(":");
+ mLoaderManager.dump(prefix + " ", fd, writer, args);
+ }
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/app/FragmentManager.java support-v4-23.0.0/android/support/v4/app/FragmentManager.java
--- support-v4-22.2.1/android/support/v4/app/FragmentManager.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/FragmentManager.java 2015-06-23 08:43:44.000000000 +0900
@@ -25,8 +25,8 @@
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.annotation.CallSuper;
import android.support.annotation.IdRes;
-import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.util.DebugUtils;
import android.support.v4.util.LogWriter;
@@ -399,15 +399,6 @@
}
/**
- * Callbacks from FragmentManagerImpl to its container.
- */
-interface FragmentContainer {
- @Nullable
- public View findViewById(@IdRes int id);
- public boolean hasView();
-}
-
-/**
* Container for fragments associated with an activity.
*/
final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {
@@ -421,6 +412,39 @@
static final String VIEW_STATE_TAG = "android:view_state";
static final String USER_VISIBLE_HINT_TAG = "android:user_visible_hint";
+
+ static class AnimateOnHWLayerIfNeededListener implements AnimationListener {
+ private boolean mShouldRunOnHWLayer = false;
+ private View mView;
+ public AnimateOnHWLayerIfNeededListener(final View v, Animation anim) {
+ if (v == null || anim == null) {
+ return;
+ }
+ mView = v;
+ }
+
+ @Override
+ @CallSuper
+ public void onAnimationStart(Animation animation) {
+ mShouldRunOnHWLayer = shouldRunOnHWLayer(mView, animation);
+ if (mShouldRunOnHWLayer) {
+ ViewCompat.setLayerType(mView, ViewCompat.LAYER_TYPE_HARDWARE, null);
+ }
+ }
+
+ @Override
+ @CallSuper
+ public void onAnimationEnd(Animation animation) {
+ if (mShouldRunOnHWLayer) {
+ ViewCompat.setLayerType(mView, ViewCompat.LAYER_TYPE_NONE, null);
+ }
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ }
+
ArrayList<Runnable> mPendingActions;
Runnable[] mTmpActions;
boolean mExecutingActions;
@@ -438,7 +462,8 @@
ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
int mCurState = Fragment.INITIALIZING;
- FragmentActivity mActivity;
+ FragmentHostCallback mHost;
+ FragmentController mController;
FragmentContainer mContainer;
Fragment mParent;
@@ -459,14 +484,34 @@
}
};
+ static boolean modifiesAlpha(Animation anim) {
+ if (anim instanceof AlphaAnimation) {
+ return true;
+ } else if (anim instanceof AnimationSet) {
+ List<Animation> anims = ((AnimationSet) anim).getAnimations();
+ for (int i = 0; i < anims.size(); i++) {
+ if (anims.get(i) instanceof AlphaAnimation) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ static boolean shouldRunOnHWLayer(View v, Animation anim) {
+ return ViewCompat.getLayerType(v) == ViewCompat.LAYER_TYPE_NONE
+ && ViewCompat.hasOverlappingRendering(v)
+ && modifiesAlpha(anim);
+ }
+
private void throwException(RuntimeException ex) {
Log.e(TAG, ex.getMessage());
Log.e(TAG, "Activity state:");
LogWriter logw = new LogWriter(TAG);
PrintWriter pw = new PrintWriter(logw);
- if (mActivity != null) {
+ if (mHost != null) {
try {
- mActivity.dump(" ", null, pw, new String[] { });
+ mHost.onDump(" ", null, pw, new String[] { });
} catch (Exception e) {
Log.e(TAG, "Failed dumping state", e);
}
@@ -494,7 +539,7 @@
public void popBackStack() {
enqueueAction(new Runnable() {
@Override public void run() {
- popBackStackState(mActivity.mHandler, null, -1, 0);
+ popBackStackState(mHost.getHandler(), null, -1, 0);
}
}, false);
}
@@ -503,14 +548,14 @@
public boolean popBackStackImmediate() {
checkStateLoss();
executePendingTransactions();
- return popBackStackState(mActivity.mHandler, null, -1, 0);
+ return popBackStackState(mHost.getHandler(), null, -1, 0);
}
@Override
public void popBackStack(final String name, final int flags) {
enqueueAction(new Runnable() {
@Override public void run() {
- popBackStackState(mActivity.mHandler, name, -1, flags);
+ popBackStackState(mHost.getHandler(), name, -1, flags);
}
}, false);
}
@@ -519,7 +564,7 @@
public boolean popBackStackImmediate(String name, int flags) {
checkStateLoss();
executePendingTransactions();
- return popBackStackState(mActivity.mHandler, name, -1, flags);
+ return popBackStackState(mHost.getHandler(), name, -1, flags);
}
@Override
@@ -529,7 +574,7 @@
}
enqueueAction(new Runnable() {
@Override public void run() {
- popBackStackState(mActivity.mHandler, null, id, flags);
+ popBackStackState(mHost.getHandler(), null, id, flags);
}
}, false);
}
@@ -541,7 +586,7 @@
if (id < 0) {
throw new IllegalArgumentException("Bad id: " + id);
}
- return popBackStackState(mActivity.mHandler, null, id, flags);
+ return popBackStackState(mHost.getHandler(), null, id, flags);
}
@Override
@@ -628,7 +673,7 @@
if (mParent != null) {
DebugUtils.buildShortClassTag(mParent, sb);
} else {
- DebugUtils.buildShortClassTag(mActivity, sb);
+ DebugUtils.buildShortClassTag(mHost, sb);
}
sb.append("}}");
return sb.toString();
@@ -725,7 +770,7 @@
}
writer.print(prefix); writer.println("FragmentManager misc state:");
- writer.print(prefix); writer.print(" mActivity="); writer.println(mActivity);
+ writer.print(prefix); writer.print(" mHost="); writer.println(mHost);
writer.print(prefix); writer.print(" mContainer="); writer.println(mContainer);
if (mParent != null) {
writer.print(prefix); writer.print(" mParent="); writer.println(mParent);
@@ -775,7 +820,7 @@
anim.setDuration(ANIM_DUR);
return anim;
}
-
+
Animation loadAnimation(Fragment fragment, int transit, boolean enter,
int transitionStyle) {
Animation animObj = fragment.onCreateAnimation(transit, enter,
@@ -785,7 +830,7 @@
}
if (fragment.mNextAnim != 0) {
- Animation anim = AnimationUtils.loadAnimation(mActivity, fragment.mNextAnim);
+ Animation anim = AnimationUtils.loadAnimation(mHost.getContext(), fragment.mNextAnim);
if (anim != null) {
return anim;
}
@@ -799,24 +844,24 @@
if (styleIndex < 0) {
return null;
}
-
+
switch (styleIndex) {
case ANIM_STYLE_OPEN_ENTER:
- return makeOpenCloseAnimation(mActivity, 1.125f, 1.0f, 0, 1);
+ return makeOpenCloseAnimation(mHost.getContext(), 1.125f, 1.0f, 0, 1);
case ANIM_STYLE_OPEN_EXIT:
- return makeOpenCloseAnimation(mActivity, 1.0f, .975f, 1, 0);
+ return makeOpenCloseAnimation(mHost.getContext(), 1.0f, .975f, 1, 0);
case ANIM_STYLE_CLOSE_ENTER:
- return makeOpenCloseAnimation(mActivity, .975f, 1.0f, 0, 1);
+ return makeOpenCloseAnimation(mHost.getContext(), .975f, 1.0f, 0, 1);
case ANIM_STYLE_CLOSE_EXIT:
- return makeOpenCloseAnimation(mActivity, 1.0f, 1.075f, 1, 0);
+ return makeOpenCloseAnimation(mHost.getContext(), 1.0f, 1.075f, 1, 0);
case ANIM_STYLE_FADE_ENTER:
- return makeFadeAnimation(mActivity, 0, 1);
+ return makeFadeAnimation(mHost.getContext(), 0, 1);
case ANIM_STYLE_FADE_EXIT:
- return makeFadeAnimation(mActivity, 1, 0);
+ return makeFadeAnimation(mHost.getContext(), 1, 0);
}
- if (transitionStyle == 0 && mActivity.getWindow() != null) {
- transitionStyle = mActivity.getWindow().getAttributes().windowAnimations;
+ if (transitionStyle == 0 && mHost.onHasWindowAnimations()) {
+ transitionStyle = mHost.onGetWindowAnimations();
}
if (transitionStyle == 0) {
return null;
@@ -847,6 +892,22 @@
}
}
+ /**
+ * Sets the to be animated view on hardware layer during the animation. Note
+ * that calling this will replace any existing animation listener on the animation
+ * with a new one, as animations do not support more than one listeners. Therefore,
+ * animations that already have listeners should do the layer change operations
+ * in their existing listeners, rather than calling this function.
+ */
+ private void setHWLayerAnimListenerIfAlpha(final View v, Animation anim) {
+ if (v == null || anim == null) {
+ return;
+ }
+ if (shouldRunOnHWLayer(v, anim)) {
+ anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(v, anim));
+ }
+ }
+
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
// Fragments that are not currently added will sit in the onCreate() state.
@@ -881,7 +942,7 @@
case Fragment.INITIALIZING:
if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
if (f.mSavedFragmentState != null) {
- f.mSavedFragmentState.setClassLoader(mActivity.getClassLoader());
+ f.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
FragmentManagerImpl.VIEW_STATE_TAG);
f.mTarget = getFragment(f.mSavedFragmentState,
@@ -899,18 +960,18 @@
}
}
}
- f.mActivity = mActivity;
+ f.mHost = mHost;
f.mParentFragment = mParent;
f.mFragmentManager = mParent != null
- ? mParent.mChildFragmentManager : mActivity.mFragments;
+ ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
f.mCalled = false;
- f.onAttach(mActivity);
+ f.onAttach(mHost.getContext());
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onAttach()");
}
if (f.mParentFragment == null) {
- mActivity.onAttachFragment(f);
+ mHost.onAttachFragment(f);
}
if (!f.mRetaining) {
@@ -942,7 +1003,7 @@
if (!f.mFromLayout) {
ViewGroup container = null;
if (f.mContainerId != 0) {
- container = (ViewGroup)mContainer.findViewById(f.mContainerId);
+ container = (ViewGroup)mContainer.onFindViewById(f.mContainerId);
if (container == null && !f.mRestored) {
throwException(new IllegalArgumentException(
"No view found for id 0x"
@@ -965,6 +1026,7 @@
Animation anim = loadAnimation(f, transit, true,
transitionStyle);
if (anim != null) {
+ setHWLayerAnimListenerIfAlpha(f.mView, anim);
f.mView.startAnimation(anim);
}
container.addView(f.mView);
@@ -1021,7 +1083,7 @@
if (f.mView != null) {
// Need to save the current view state if not
// done already.
- if (!mActivity.isFinishing() && f.mSavedViewState == null) {
+ if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {
saveFragmentViewState(f);
}
}
@@ -1036,21 +1098,18 @@
final Fragment fragment = f;
f.mAnimatingAway = f.mView;
f.mStateAfterAnimating = newState;
- anim.setAnimationListener(new AnimationListener() {
+ final View viewToAnimate = f.mView;
+ anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(
+ viewToAnimate, anim) {
@Override
public void onAnimationEnd(Animation animation) {
+ super.onAnimationEnd(animation);
if (fragment.mAnimatingAway != null) {
fragment.mAnimatingAway = null;
moveToState(fragment, fragment.mStateAfterAnimating,
0, 0, false);
}
}
- @Override
- public void onAnimationRepeat(Animation animation) {
- }
- @Override
- public void onAnimationStart(Animation animation) {
- }
});
f.mView.startAnimation(anim);
}
@@ -1098,7 +1157,7 @@
if (!f.mRetaining) {
makeInactive(f);
} else {
- f.mActivity = null;
+ f.mHost = null;
f.mParentFragment = null;
f.mFragmentManager = null;
f.mChildFragmentManager = null;
@@ -1121,8 +1180,8 @@
}
void moveToState(int newState, int transit, int transitStyle, boolean always) {
- if (mActivity == null && newState != Fragment.INITIALIZING) {
- throw new IllegalStateException("No activity");
+ if (mHost == null && newState != Fragment.INITIALIZING) {
+ throw new IllegalStateException("No host");
}
if (!always && mCurState == newState) {
@@ -1146,8 +1205,8 @@
startPendingDeferredFragments();
}
- if (mNeedMenuInvalidate && mActivity != null && mCurState == Fragment.RESUMED) {
- mActivity.supportInvalidateOptionsMenu();
+ if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) {
+ mHost.onSupportInvalidateOptionsMenu();
mNeedMenuInvalidate = false;
}
}
@@ -1194,7 +1253,7 @@
mAvailIndices = new ArrayList<Integer>();
}
mAvailIndices.add(f.mIndex);
- mActivity.invalidateSupportFragment(f.mWho);
+ mHost.inactivateFragment(f.mWho);
f.initState();
}
@@ -1245,6 +1304,7 @@
Animation anim = loadAnimation(fragment, transition, false,
transitionStyle);
if (anim != null) {
+ setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
fragment.mView.startAnimation(anim);
}
fragment.mView.setVisibility(View.GONE);
@@ -1264,6 +1324,7 @@
Animation anim = loadAnimation(fragment, transition, true,
transitionStyle);
if (anim != null) {
+ setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
fragment.mView.startAnimation(anim);
}
fragment.mView.setVisibility(View.VISIBLE);
@@ -1395,7 +1456,7 @@
checkStateLoss();
}
synchronized (this) {
- if (mDestroyed || mActivity == null) {
+ if (mDestroyed || mHost == null) {
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
@@ -1403,8 +1464,8 @@
}
mPendingActions.add(action);
if (mPendingActions.size() == 1) {
- mActivity.mHandler.removeCallbacks(mExecCommit);
- mActivity.mHandler.post(mExecCommit);
+ mHost.getHandler().removeCallbacks(mExecCommit);
+ mHost.getHandler().post(mExecCommit);
}
}
}
@@ -1473,7 +1534,7 @@
throw new IllegalStateException("Recursive entry to executePendingTransactions");
}
- if (Looper.myLooper() != mActivity.mHandler.getLooper()) {
+ if (Looper.myLooper() != mHost.getHandler().getLooper()) {
throw new IllegalStateException("Must be called from main thread of process");
}
@@ -1493,7 +1554,7 @@
}
mPendingActions.toArray(mTmpActions);
mPendingActions.clear();
- mActivity.mHandler.removeCallbacks(mExecCommit);
+ mHost.getHandler().removeCallbacks(mExecCommit);
}
mExecutingActions = true;
@@ -1537,6 +1598,7 @@
reportBackStackChanged();
}
+ @SuppressWarnings("unused")
boolean popBackStackState(Handler handler, String name, int id, int flags) {
if (mBackStack == null) {
return false;
@@ -1780,7 +1842,7 @@
if (N > 0) {
backStack = new BackStackState[N];
for (int i=0; i<N; i++) {
- backStack[i] = new BackStackState(this, mBackStack.get(i));
+ backStack[i] = new BackStackState(mBackStack.get(i));
if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
+ ": " + mBackStack.get(i));
}
@@ -1794,7 +1856,7 @@
return fms;
}
- void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) {
+ void restoreAllState(Parcelable state, List<Fragment> nonConfig) {
// If there is no saved state at all, then there can not be
// any nonConfig fragments either, so that is that.
if (state == null) return;
@@ -1815,7 +1877,7 @@
f.mAdded = false;
f.mTarget = null;
if (fs.mSavedFragmentState != null) {
- fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader());
+ fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
FragmentManagerImpl.VIEW_STATE_TAG);
f.mSavedFragmentState = fs.mSavedFragmentState;
@@ -1832,7 +1894,7 @@
for (int i=0; i<fms.mActive.length; i++) {
FragmentState fs = fms.mActive[i];
if (fs != null) {
- Fragment f = fs.instantiate(mActivity, mParent);
+ Fragment f = fs.instantiate(mHost, mParent);
if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
mActive.add(f);
// Now that the fragment is instantiated (or came from being
@@ -1906,11 +1968,11 @@
mBackStack = null;
}
}
-
- public void attachActivity(FragmentActivity activity,
+
+ public void attachController(FragmentHostCallback host,
FragmentContainer container, Fragment parent) {
- if (mActivity != null) throw new IllegalStateException("Already attached");
- mActivity = activity;
+ if (mHost != null) throw new IllegalStateException("Already attached");
+ mHost = host;
mContainer = container;
mParent = parent;
}
@@ -1964,7 +2026,7 @@
mDestroyed = true;
execPendingActions();
moveToState(Fragment.INITIALIZING, false);
- mActivity = null;
+ mHost = null;
mContainer = null;
mParent = null;
}
@@ -2132,7 +2194,7 @@
String tag = a.getString(FragmentTag.Fragment_tag);
a.recycle();
- if (!Fragment.isSupportFragmentClass(mActivity, fname)) {
+ if (!Fragment.isSupportFragmentClass(mHost.getContext(), fname)) {
// Invalid support lib fragment; let the device's framework handle it.
// This will allow android.app.Fragments to do the right thing.
return null;
@@ -2166,7 +2228,7 @@
fragment.mTag = tag;
fragment.mInLayout = true;
fragment.mFragmentManager = this;
- fragment.onInflate(mActivity, attrs, fragment.mSavedFragmentState);
+ fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
addFragment(fragment, true);
} else if (fragment.mInLayout) {
@@ -2184,7 +2246,7 @@
// from last saved state), then give it the attributes to
// initialize itself.
if (!fragment.mRetaining) {
- fragment.onInflate(mActivity, attrs, fragment.mSavedFragmentState);
+ fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
}
}
diff -Nur support-v4-22.2.1/android/support/v4/app/FragmentTransitionCompat21.java support-v4-23.0.0/android/support/v4/app/FragmentTransitionCompat21.java
--- support-v4-22.2.1/android/support/v4/app/FragmentTransitionCompat21.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/FragmentTransitionCompat21.java 2015-06-29 08:17:36.000000000 +0900
@@ -16,14 +16,10 @@
package android.support.v4.app;
-import android.content.Context;
-import android.content.res.TypedArray;
import android.graphics.Rect;
import android.transition.Transition;
-import android.transition.TransitionInflater;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
-import android.util.ArrayMap;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
@@ -83,6 +79,19 @@
});
}
+ public static Object wrapSharedElementTransition(Object transitionObj) {
+ if (transitionObj == null) {
+ return null;
+ }
+ Transition transition = (Transition) transitionObj;
+ if (transition == null) {
+ return null;
+ }
+ TransitionSet transitionSet = new TransitionSet();
+ transitionSet.addTransition(transition);
+ return transitionSet;
+ }
+
/**
* 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,
@@ -98,16 +107,16 @@
Object sharedElementTransitionObject, final View container,
final ViewRetriever inFragment, final View nonExistentView,
EpicenterView epicenterView, final Map<String, String> nameOverrides,
- final ArrayList<View> enteringViews, final Map<String, View> renamedViews,
- final ArrayList<View> sharedElementTargets) {
+ final ArrayList<View> enteringViews, final Map<String, View> namedViews,
+ final Map<String, View> renamedViews, final ArrayList<View> sharedElementTargets) {
if (enterTransitionObject != null || sharedElementTransitionObject != null) {
final Transition enterTransition = (Transition) enterTransitionObject;
if (enterTransition != null) {
enterTransition.addTarget(nonExistentView);
}
if (sharedElementTransitionObject != null) {
- Transition sharedElementTransition = (Transition) sharedElementTransitionObject;
- addTargets(sharedElementTransition, sharedElementTargets);
+ setSharedElementTargets(sharedElementTransitionObject, nonExistentView,
+ namedViews, sharedElementTargets);
}
if (inFragment != null) {
@@ -115,6 +124,9 @@
new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
container.getViewTreeObserver().removeOnPreDrawListener(this);
+ if (enterTransition != null) {
+ enterTransition.removeTarget(nonExistentView);
+ }
View fragmentView = inFragment.getView();
if (fragmentView != null) {
if (!nameOverrides.isEmpty()) {
@@ -133,7 +145,6 @@
captureTransitioningViews(enteringViews, fragmentView);
enteringViews.removeAll(renamedViews.values());
enteringViews.add(nonExistentView);
- enterTransition.removeTarget(nonExistentView);
addTargets(enterTransition, enteringViews);
}
}
@@ -203,7 +214,67 @@
return transition;
}
+ /**
+ * Finds all children of the shared elements and sets the wrapping TransitionSet
+ * targets to point to those. It also limits transitions that have no targets to the
+ * specific shared elements. This allows developers to target child views of the
+ * shared elements specifically, but this doesn't happen by default.
+ */
+ public static void setSharedElementTargets(Object transitionObj,
+ View nonExistentView, Map<String, View> namedViews,
+ ArrayList<View> sharedElementTargets) {
+ TransitionSet transition = (TransitionSet) transitionObj;
+ sharedElementTargets.clear();
+ sharedElementTargets.addAll(namedViews.values());
+
+ final List<View> views = transition.getTargets();
+ views.clear();
+ final int count = sharedElementTargets.size();
+ for (int i = 0; i < count; i++) {
+ final View view = sharedElementTargets.get(i);
+ bfsAddViewChildren(views, view);
+ }
+ sharedElementTargets.add(nonExistentView);
+ addTargets(transition, sharedElementTargets);
+ }
+
+ /**
+ * Uses a breadth-first scheme to add startView and all of its children to views.
+ * It won't add a child if it is already in views.
+ */
+ private static void bfsAddViewChildren(final List<View> views, final View startView) {
+ final int startIndex = views.size();
+ if (containedBeforeIndex(views, startView, startIndex)) {
+ return; // This child is already in the list, so all its children are also.
+ }
+ views.add(startView);
+ for (int index = startIndex; index < views.size(); index++) {
+ final View view = views.get(index);
+ if (view instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) view;
+ final int childCount = viewGroup.getChildCount();
+ for (int childIndex = 0; childIndex < childCount; childIndex++) {
+ final View child = viewGroup.getChildAt(childIndex);
+ if (!containedBeforeIndex(views, child, startIndex)) {
+ views.add(child);
+ }
+ }
+ }
+ }
+ }
+ /**
+ * Does a linear search through views for view, limited to maxIndex.
+ */
+ private static boolean containedBeforeIndex(final List<View> views, final View view,
+ final int maxIndex) {
+ for (int i = 0; i < maxIndex; i++) {
+ if (views.get(i) == view) {
+ return true;
+ }
+ }
+ return false;
+ }
private static void setSharedElementEpicenter(Transition transition,
final EpicenterView epicenterView) {
@@ -283,7 +354,6 @@
public boolean onPreDraw() {
sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
if (enterTransition != null) {
- enterTransition.removeTarget(nonExistentView);
removeTargets(enterTransition, enteringViews);
}
if (exitTransition != null) {
diff -Nur support-v4-22.2.1/android/support/v4/app/LoaderManager.java support-v4-23.0.0/android/support/v4/app/LoaderManager.java
--- support-v4-22.2.1/android/support/v4/app/LoaderManager.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/LoaderManager.java 2015-06-23 08:43:44.000000000 +0900
@@ -184,6 +184,9 @@
public boolean hasRunningLoaders() { return false; }
}
+/**
+ * @hide
+ */
class LoaderManagerImpl extends LoaderManager {
static final String TAG = "LoaderManager";
static boolean DEBUG = false;
@@ -201,14 +204,15 @@
final String mWho;
- FragmentActivity mActivity;
boolean mStarted;
boolean mRetaining;
boolean mRetainingStarted;
boolean mCreatingLoader;
+ private FragmentHostCallback mHost;
- final class LoaderInfo implements Loader.OnLoadCompleteListener<Object> {
+ final class LoaderInfo implements Loader.OnLoadCompleteListener<Object>,
+ Loader.OnLoadCanceledListener<Object> {
final int mId;
final Bundle mArgs;
LoaderManager.LoaderCallbacks<Object> mCallbacks;
@@ -216,8 +220,11 @@
boolean mHaveData;
boolean mDeliveredData;
Object mData;
+ @SuppressWarnings("hiding")
boolean mStarted;
+ @SuppressWarnings("hiding")
boolean mRetaining;
+ @SuppressWarnings("hiding")
boolean mRetainingStarted;
boolean mReportNextStart;
boolean mDestroyed;
@@ -260,6 +267,7 @@
}
if (!mListenerRegistered) {
mLoader.registerListener(mId, this);
+ mLoader.registerOnLoadCanceledListener(this);
mListenerRegistered = true;
}
mLoader.startLoading();
@@ -318,11 +326,21 @@
// Let the loader know we're done with it
mListenerRegistered = false;
mLoader.unregisterListener(this);
+ mLoader.unregisterOnLoadCanceledListener(this);
mLoader.stopLoading();
}
}
}
-
+
+ void cancel() {
+ if (DEBUG) Log.v(TAG, " Canceling: " + this);
+ if (mStarted && mLoader != null && mListenerRegistered) {
+ if (!mLoader.cancelLoad()) {
+ onLoadCanceled(mLoader);
+ }
+ }
+ }
+
void destroy() {
if (DEBUG) Log.v(TAG, " Destroying: " + this);
mDestroyed = true;
@@ -331,15 +349,15 @@
if (mCallbacks != null && mLoader != null && mHaveData && needReset) {
if (DEBUG) Log.v(TAG, " Reseting: " + this);
String lastBecause = null;
- if (mActivity != null) {
- lastBecause = mActivity.mFragments.mNoTransactionsBecause;
- mActivity.mFragments.mNoTransactionsBecause = "onLoaderReset";
+ if (mHost != null) {
+ lastBecause = mHost.mFragmentManager.mNoTransactionsBecause;
+ mHost.mFragmentManager.mNoTransactionsBecause = "onLoaderReset";
}
try {
mCallbacks.onLoaderReset(mLoader);
} finally {
- if (mActivity != null) {
- mActivity.mFragments.mNoTransactionsBecause = lastBecause;
+ if (mHost != null) {
+ mHost.mFragmentManager.mNoTransactionsBecause = lastBecause;
}
}
}
@@ -350,6 +368,7 @@
if (mListenerRegistered) {
mListenerRegistered = false;
mLoader.unregisterListener(this);
+ mLoader.unregisterOnLoadCanceledListener(this);
}
mLoader.reset();
}
@@ -357,8 +376,38 @@
mPendingLoader.destroy();
}
}
-
- @Override public void onLoadComplete(Loader<Object> loader, Object data) {
+
+ @Override
+ public void onLoadCanceled(Loader<Object> loader) {
+ if (DEBUG) Log.v(TAG, "onLoadCanceled: " + this);
+
+ if (mDestroyed) {
+ if (DEBUG) Log.v(TAG, " Ignoring load canceled -- destroyed");
+ return;
+ }
+
+ if (mLoaders.get(mId) != this) {
+ // This cancellation message is not coming from the current active loader.
+ // We don't care about it.
+ if (DEBUG) Log.v(TAG, " Ignoring load canceled -- not active");
+ return;
+ }
+
+ LoaderInfo pending = mPendingLoader;
+ if (pending != null) {
+ // There is a new request pending and we were just
+ // waiting for the old one to cancel or complete before starting
+ // it. So now it is time, switch over to the new loader.
+ if (DEBUG) Log.v(TAG, " Switching to pending loader: " + pending);
+ mPendingLoader = null;
+ mLoaders.put(mId, null);
+ destroy();
+ installLoader(pending);
+ }
+ }
+
+ @Override
+ public void onLoadComplete(Loader<Object> loader, Object data) {
if (DEBUG) Log.v(TAG, "onLoadComplete: " + this);
if (mDestroyed) {
@@ -409,25 +458,25 @@
mInactiveLoaders.remove(mId);
}
- if (mActivity != null && !hasRunningLoaders()) {
- mActivity.mFragments.startPendingDeferredFragments();
+ if (mHost != null && !hasRunningLoaders()) {
+ mHost.mFragmentManager.startPendingDeferredFragments();
}
}
void callOnLoadFinished(Loader<Object> loader, Object data) {
if (mCallbacks != null) {
String lastBecause = null;
- if (mActivity != null) {
- lastBecause = mActivity.mFragments.mNoTransactionsBecause;
- mActivity.mFragments.mNoTransactionsBecause = "onLoadFinished";
+ if (mHost != null) {
+ lastBecause = mHost.mFragmentManager.mNoTransactionsBecause;
+ mHost.mFragmentManager.mNoTransactionsBecause = "onLoadFinished";
}
try {
if (DEBUG) Log.v(TAG, " onLoadFinished in " + loader + ": "
+ loader.dataToString(data));
mCallbacks.onLoadFinished(loader, data);
} finally {
- if (mActivity != null) {
- mActivity.mFragments.mNoTransactionsBecause = lastBecause;
+ if (mHost != null) {
+ mHost.mFragmentManager.mNoTransactionsBecause = lastBecause;
}
}
mDeliveredData = true;
@@ -474,21 +523,21 @@
}
}
- LoaderManagerImpl(String who, FragmentActivity activity, boolean started) {
+ LoaderManagerImpl(String who, FragmentHostCallback host, boolean started) {
mWho = who;
- mActivity = activity;
+ mHost = host;
mStarted = started;
}
- void updateActivity(FragmentActivity activity) {
- mActivity = activity;
+ void updateHostController(FragmentHostCallback host) {
+ mHost = host;
}
private LoaderInfo createLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
- LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
+ LoaderInfo info = new LoaderInfo(id, args, callback);
Loader<Object> loader = callback.onCreateLoader(id, args);
- info.mLoader = (Loader<Object>)loader;
+ info.mLoader = loader;
return info;
}
@@ -533,7 +582,7 @@
* @param id A unique (to this LoaderManager instance) identifier under
* which to manage the new Loader.
* @param args Optional arguments that will be propagated to
- * {@link LoaderCallbacks#onCreateLoader(int, Bundle) LoaderCallbacks.onCreateLoader()}.
+ * {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader(int, Bundle) LoaderCallbacks.onCreateLoader()}.
* @param callback Interface implementing management of this Loader. Required.
* Its onCreateLoader() method will be called while inside of the function to
* instantiate the Loader object.
@@ -583,7 +632,7 @@
* @param id A unique (to this LoaderManager instance) identifier under
* which to manage the new Loader.
* @param args Optional arguments that will be propagated to
- * {@link LoaderCallbacks#onCreateLoader(int, Bundle) LoaderCallbacks.onCreateLoader()}.
+ * {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader(int, Bundle) LoaderCallbacks.onCreateLoader()}.
* @param callback Interface implementing management of this Loader. Required.
* Its onCreateLoader() method will be called while inside of the function to
* instantiate the Loader object.
@@ -622,7 +671,9 @@
} else {
// Now we have three active loaders... we'll queue
// up this request to be processed once one of the other loaders
- // finishes.
+ // finishes or is canceled.
+ if (DEBUG) Log.v(TAG, " Current loader is running; attempting to cancel");
+ info.cancel();
if (info.mPendingLoader != null) {
if (DEBUG) Log.v(TAG, " Removing pending loader: " + info.mPendingLoader);
info.mPendingLoader.destroy();
@@ -672,8 +723,8 @@
mInactiveLoaders.removeAt(idx);
info.destroy();
}
- if (mActivity != null && !hasRunningLoaders()) {
- mActivity.mFragments.startPendingDeferredFragments();
+ if (mHost != null && !hasRunningLoaders()) {
+ mHost.mFragmentManager.startPendingDeferredFragments();
}
}
@@ -791,7 +842,7 @@
sb.append("LoaderManager{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" in ");
- DebugUtils.buildShortClassTag(mActivity, sb);
+ DebugUtils.buildShortClassTag(mHost, sb);
sb.append("}}");
return sb.toString();
}
diff -Nur support-v4-22.2.1/android/support/v4/app/NotificationCompat.java support-v4-23.0.0/android/support/v4/app/NotificationCompat.java
--- support-v4-22.2.1/android/support/v4/app/NotificationCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/NotificationCompat.java 2015-06-23 08:43:44.000000000 +0900
@@ -26,6 +26,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
+import android.support.annotation.ColorInt;
import android.support.v4.view.GravityCompat;
import android.view.Gravity;
import android.widget.RemoteViews;
@@ -342,6 +343,7 @@
* telling the system not to decorate this notification with any special color but instead use
* default colors when presenting this notification.
*/
+ @ColorInt
public static final int COLOR_DEFAULT = Color.TRANSPARENT;
/**
@@ -1198,7 +1200,7 @@
* rate. The rate is specified in terms of the number of milliseconds to be on
* and then the number of milliseconds to be off.
*/
- public Builder setLights(int argb, int onMs, int offMs) {
+ public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
mNotification.ledARGB = argb;
mNotification.ledOnMS = onMs;
mNotification.ledOffMS = offMs;
@@ -1504,7 +1506,7 @@
*
* @return The same Builder.
*/
- public Builder setColor(int argb) {
+ public Builder setColor(@ColorInt int argb) {
mColor = argb;
return this;
}
@@ -3012,7 +3014,7 @@
* automotive setting. This method can be used to override the color provided in the
* notification in such a situation.
*/
- public CarExtender setColor(int color) {
+ public CarExtender setColor(@ColorInt int color) {
mColor = color;
return this;
}
@@ -3022,6 +3024,7 @@
*
* @see setColor
*/
+ @ColorInt
public int getColor() {
return mColor;
}
diff -Nur support-v4-22.2.1/android/support/v4/app/SharedElementCallback.java support-v4-23.0.0/android/support/v4/app/SharedElementCallback.java
--- support-v4-22.2.1/android/support/v4/app/SharedElementCallback.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/app/SharedElementCallback.java 2015-06-23 08:43:44.000000000 +0900
@@ -48,9 +48,23 @@
private static final String BUNDLE_SNAPSHOT_IMAGE_MATRIX = "sharedElement:snapshot:imageMatrix";
/**
- * Called immediately after the start state is set for the shared element.
- * The shared element will start at the size and position of the shared element
- * in the launching Activity or Fragment.
+ * In Activity Transitions, onSharedElementStart is called immediately before
+ * capturing the start of the shared element state on enter and reenter transitions and
+ * immediately before capturing the end of the shared element state for exit and return
+ * transitions.
+ * <p>
+ * In Fragment Transitions, onSharedElementStart is called immediately before capturing the
+ * start state of all shared element transitions.
+ * <p>
+ * This call can be used to adjust the transition start state by modifying the shared
+ * element Views. Note that no layout step will be executed between onSharedElementStart
+ * and the transition state capture.
+ * <p>
+ * For Activity Transitions, any changes made in {@link #onSharedElementEnd(List, List, List)}
+ * that are not updated during layout should be corrected in onSharedElementStart for exit and
+ * return transitions. For example, rotation or scale will not be affected by layout and
+ * if changed in {@link #onSharedElementEnd(List, List, List)}, it will also have to be reset
+ * in onSharedElementStart again to correct the end state.
*
* @param sharedElementNames The names of the shared elements that were accepted into
* the View hierarchy.
@@ -65,17 +79,23 @@
List<View> sharedElements, List<View> sharedElementSnapshots) {}
/**
- * Called after the end state is set for the shared element, but before the end state
- * is captured by the shared element transition.
+ * In Activity Transitions, onSharedElementEnd is called immediately before
+ * capturing the end of the shared element state on enter and reenter transitions and
+ * immediately before capturing the start of the shared element state for exit and return
+ * transitions.
* <p>
- * Any customization done in
- * {@link #onSharedElementStart(java.util.List, java.util.List, java.util.List)}
- * may need to be modified to the final state of the shared element if it is not
- * automatically corrected by layout. For example, rotation or scale will not
- * be affected by layout and if changed in {@link #onSharedElementStart(java.util.List,
- * java.util.List, java.util.List)}, it will also have to be set here again to correct
- * the end state.
- * </p>
+ * In Fragment Transitions, onSharedElementEnd is called immediately before capturing the
+ * end state of all shared element transitions.
+ * <p>
+ * This call can be used to adjust the transition end state by modifying the shared
+ * element Views. Note that no layout step will be executed between onSharedElementEnd
+ * and the transition state capture.
+ * <p>
+ * Any changes made in {@link #onSharedElementStart(List, List, List)} that are not updated
+ * during layout should be corrected in onSharedElementEnd. For example, rotation or scale
+ * will not be affected by layout and if changed in
+ * {@link #onSharedElementStart(List, List, List)}, it will also have to be reset in
+ * onSharedElementEnd again to correct the end state.
*
* @param sharedElementNames The names of the shared elements that were accepted into
* the View hierarchy.
diff -Nur support-v4-22.2.1/android/support/v4/content/AsyncTaskLoader.java support-v4-23.0.0/android/support/v4/content/AsyncTaskLoader.java
--- support-v4-22.2.1/android/support/v4/content/AsyncTaskLoader.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/content/AsyncTaskLoader.java 2015-06-23 08:43:44.000000000 +0900
@@ -19,12 +19,14 @@
import android.content.Context;
import android.os.Handler;
import android.os.SystemClock;
+import android.support.v4.os.OperationCanceledException;
import android.support.v4.util.TimeUtils;
import android.util.Log;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
/**
* Static library support version of the framework's {@link android.content.AsyncTaskLoader}.
@@ -38,19 +40,33 @@
static final boolean DEBUG = false;
final class LoadTask extends ModernAsyncTask<Void, Void, D> implements Runnable {
+ private final CountDownLatch mDone = new CountDownLatch(1);
- D result;
+ // Set to true to indicate that the task has been posted to a handler for
+ // execution at a later time. Used to throttle updates.
boolean waiting;
- private CountDownLatch done = new CountDownLatch(1);
-
/* Runs on a worker thread */
@Override
protected D doInBackground(Void... params) {
if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
- result = AsyncTaskLoader.this.onLoadInBackground();
- if (DEBUG) Log.v(TAG, this + " <<< doInBackground");
- return result;
+ try {
+ D data = AsyncTaskLoader.this.onLoadInBackground();
+ if (DEBUG) Log.v(TAG, this + " <<< doInBackground");
+ return data;
+ } catch (OperationCanceledException ex) {
+ if (!isCancelled()) {
+ // onLoadInBackground threw a canceled exception spuriously.
+ // This is problematic because it means that the LoaderManager did not
+ // cancel the Loader itself and still expects to receive a result.
+ // Additionally, the Loader's own state will not have been updated to
+ // reflect the fact that the task was being canceled.
+ // So we treat this case as an unhandled exception.
+ throw ex;
+ }
+ if (DEBUG) Log.v(TAG, this + " <<< doInBackground (was canceled)", ex);
+ return null;
+ }
}
/* Runs on the UI thread */
@@ -60,27 +76,41 @@
try {
AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
} finally {
- done.countDown();
+ mDone.countDown();
}
}
+ /* Runs on the UI thread */
@Override
- protected void onCancelled() {
+ protected void onCancelled(D data) {
if (DEBUG) Log.v(TAG, this + " onCancelled");
try {
- AsyncTaskLoader.this.dispatchOnCancelled(this, result);
+ AsyncTaskLoader.this.dispatchOnCancelled(this, data);
} finally {
- done.countDown();
+ mDone.countDown();
}
}
+ /* Runs on the UI thread, when the waiting task is posted to a handler.
+ * This method is only executed when task execution was deferred (waiting was true). */
@Override
public void run() {
waiting = false;
AsyncTaskLoader.this.executePendingTask();
}
+
+ /* Used for testing purposes to wait for the task to complete. */
+ public void waitForLoader() {
+ try {
+ mDone.await();
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
}
+ private final Executor mExecutor;
+
volatile LoadTask mTask;
volatile LoadTask mCancellingTask;
@@ -89,12 +119,17 @@
Handler mHandler;
public AsyncTaskLoader(Context context) {
+ this(context, ModernAsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ private AsyncTaskLoader(Context context, Executor executor) {
super(context);
+ mExecutor = executor;
}
/**
* Set amount to throttle updates by. This is the minimum time from
- * when the last {@link #onLoadInBackground()} call has completed until
+ * when the last {@link #loadInBackground()} call has completed until
* a new load is scheduled.
*
* @param delayMS Amount of delay, in milliseconds.
@@ -115,24 +150,9 @@
executePendingTask();
}
- /**
- * Attempt to cancel the current load task. See {@link android.os.AsyncTask#cancel(boolean)}
- * for more info. Must be called on the main thread of the process.
- *
- * <p>Cancelling is not an immediate operation, since the load is performed
- * in a background thread. If there is currently a load in progress, this
- * method requests that the load be cancelled, and notes this is the case;
- * once the background thread has completed its work its remaining state
- * will be cleared. If another load request comes in during this time,
- * it will be held until the cancelled load is complete.
- *
- * @return Returns <tt>false</tt> if the task could not be cancelled,
- * typically because it has already completed normally, or
- * because {@link #startLoading()} hasn't been called; returns
- * <tt>true</tt> otherwise.
- */
- public boolean cancelLoad() {
- if (DEBUG) Log.v(TAG, "cancelLoad: mTask=" + mTask);
+ @Override
+ protected boolean onCancelLoad() {
+ if (DEBUG) Log.v(TAG, "onCancelLoad: mTask=" + mTask);
if (mTask != null) {
if (mCancellingTask != null) {
// There was a pending task already waiting for a previous
@@ -158,6 +178,7 @@
if (DEBUG) Log.v(TAG, "cancelLoad: cancelled=" + cancelled);
if (cancelled) {
mCancellingTask = mTask;
+ cancelLoadInBackground();
}
mTask = null;
return cancelled;
@@ -168,7 +189,10 @@
/**
* Called if the task was canceled before it was completed. Gives the class a chance
- * to properly dispose of the result.
+ * to clean up post-cancellation and to properly dispose of the result.
+ *
+ * @param data The value that was returned by {@link #loadInBackground}, or null
+ * if the task threw {@link OperationCanceledException}.
*/
public void onCanceled(D data) {
}
@@ -192,7 +216,7 @@
}
}
if (DEBUG) Log.v(TAG, "Executing: " + mTask);
- mTask.executeOnExecutor(ModernAsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+ mTask.executeOnExecutor(mExecutor, (Void[]) null);
}
}
@@ -203,6 +227,8 @@
rollbackContentChanged();
mLastLoadCompleteTime = SystemClock.uptimeMillis();
mCancellingTask = null;
+ if (DEBUG) Log.v(TAG, "Delivering cancellation");
+ deliverCancellation();
executePendingTask();
}
}
@@ -226,23 +252,76 @@
}
/**
+ * Called on a worker thread to perform the actual load and to return
+ * the result of the load operation.
+ *
+ * Implementations should not deliver the result directly, but should return them
+ * from this method, which will eventually end up calling {@link #deliverResult} on
+ * the UI thread. If implementations need to process the results on the UI thread
+ * they may override {@link #deliverResult} and do so there.
+ *
+ * To support cancellation, this method should periodically check the value of
+ * {@link #isLoadInBackgroundCanceled} and terminate when it returns true.
+ * Subclasses may also override {@link #cancelLoadInBackground} to interrupt the load
+ * directly instead of polling {@link #isLoadInBackgroundCanceled}.
+ *
+ * When the load is canceled, this method may either return normally or throw
+ * {@link OperationCanceledException}. In either case, the {@link Loader} will
+ * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the
+ * result object, if any.
+ *
+ * @return The result of the load operation.
+ *
+ * @throws OperationCanceledException if the load is canceled during execution.
+ *
+ * @see #isLoadInBackgroundCanceled
+ * @see #cancelLoadInBackground
+ * @see #onCanceled
*/
public abstract D loadInBackground();
/**
- * Called on a worker thread to perform the actual load. Implementations should not deliver the
- * result directly, but should return them from this method, which will eventually end up
- * calling {@link #deliverResult} on the UI thread. If implementations need to process
- * the results on the UI thread they may override {@link #deliverResult} and do so
- * there.
+ * Calls {@link #loadInBackground()}.
+ *
+ * This method is reserved for use by the loader framework.
+ * Subclasses should override {@link #loadInBackground} instead of this method.
*
- * @return Implementations must return the result of their load operation.
+ * @return The result of the load operation.
+ *
+ * @throws OperationCanceledException if the load is canceled during execution.
+ *
+ * @see #loadInBackground
*/
protected D onLoadInBackground() {
return loadInBackground();
}
/**
+ * Called on the main thread to abort a load in progress.
+ *
+ * Override this method to abort the current invocation of {@link #loadInBackground}
+ * that is running in the background on a worker thread.
+ *
+ * This method should do nothing if {@link #loadInBackground} has not started
+ * running or if it has already finished.
+ *
+ * @see #loadInBackground
+ */
+ public void cancelLoadInBackground() {
+ }
+
+ /**
+ * Returns true if the current invocation of {@link #loadInBackground} is being canceled.
+ *
+ * @return True if the current invocation of {@link #loadInBackground} is being canceled.
+ *
+ * @see #loadInBackground
+ */
+ public boolean isLoadInBackgroundCanceled() {
+ return mCancellingTask != null;
+ }
+
+ /**
* Locks the current thread until the loader completes the current load
* operation. Returns immediately if there is no load operation running.
* Should not be called from the UI thread: calling it from the UI
@@ -255,11 +334,7 @@
public void waitForLoader() {
LoadTask task = mTask;
if (task != null) {
- try {
- task.done.await();
- } catch (InterruptedException e) {
- // Ignore
- }
+ task.waitForLoader();
}
}
diff -Nur support-v4-22.2.1/android/support/v4/content/ContentResolverCompat.java support-v4-23.0.0/android/support/v4/content/ContentResolverCompat.java
--- support-v4-22.2.1/android/support/v4/content/ContentResolverCompat.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/content/ContentResolverCompat.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.content;
+
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.support.v4.os.CancellationSignal;
+import android.support.v4.os.OperationCanceledException;
+
+/**
+ * Helper for accessing features in {@link android.content.ContentResolver}
+ * introduced after API level 4 in a backwards compatible fashion.
+ */
+public class ContentResolverCompat {
+ interface ContentResolverCompatImpl {
+ Cursor query(ContentResolver resolver,
+ Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder, CancellationSignal cancellationSignal);
+ }
+
+ static class ContentResolverCompatImplBase implements ContentResolverCompatImpl {
+ @Override
+ public Cursor query(ContentResolver resolver, Uri uri, String[] projection,
+ String selection, String[] selectionArgs, String sortOrder,
+ CancellationSignal cancellationSignal) {
+ // Note that the cancellation signal cannot cancel the query in progress
+ // prior to Jellybean so we cancel it preemptively here if needed.
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
+ }
+ return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
+ }
+ }
+
+ static class ContentResolverCompatImplJB extends ContentResolverCompatImplBase {
+ @Override
+ public Cursor query(ContentResolver resolver, Uri uri, String[] projection,
+ String selection, String[] selectionArgs, String sortOrder,
+ CancellationSignal cancellationSignal) {
+ return ContentResolverCompatJellybean.query(resolver,
+ uri, projection, selection, selectionArgs, sortOrder,
+ cancellationSignal != null ?
+ cancellationSignal.getCancellationSignalObject() : null);
+ }
+ }
+
+ private static final ContentResolverCompatImpl IMPL;
+ static {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 16) {
+ IMPL = new ContentResolverCompatImplJB();
+ } else {
+ IMPL = new ContentResolverCompatImplBase();
+ }
+ }
+
+ private ContentResolverCompat() {
+ /* Hide constructor */
+ }
+
+ /**
+ * Query the given URI, returning a {@link Cursor} over the result set
+ * with optional support for cancellation.
+ * <p>
+ * For best performance, the caller should follow these guidelines:
+ * <ul>
+ * <li>Provide an explicit projection, to prevent
+ * reading data from storage that aren't going to be used.</li>
+ * <li>Use question mark parameter markers such as 'phone=?' instead of
+ * explicit values in the {@code selection} parameter, so that queries
+ * that differ only by those values will be recognized as the same
+ * for caching purposes.</li>
+ * </ul>
+ * </p>
+ *
+ * @param uri The URI, using the content:// scheme, for the content to
+ * retrieve.
+ * @param projection A list of which columns to return. Passing null will
+ * return all columns, which is inefficient.
+ * @param selection A filter declaring which rows to return, formatted as an
+ * SQL WHERE clause (excluding the WHERE itself). Passing null will
+ * return all rows for the given URI.
+ * @param selectionArgs You may include ?s in selection, which will be
+ * replaced by the values from selectionArgs, in the order that they
+ * appear in the selection. The values will be bound as Strings.
+ * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
+ * clause (excluding the ORDER BY itself). Passing null will use the
+ * default sort order, which may be unordered.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
+ * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+ * when the query is executed.
+ * @return A Cursor object, which is positioned before the first entry, or null
+ * @see Cursor
+ */
+ public static Cursor query(ContentResolver resolver,
+ Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder, CancellationSignal cancellationSignal) {
+ return IMPL.query(resolver, uri, projection, selection, selectionArgs,
+ sortOrder, cancellationSignal);
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/content/ContentResolverCompatJellybean.java support-v4-23.0.0/android/support/v4/content/ContentResolverCompatJellybean.java
--- support-v4-22.2.1/android/support/v4/content/ContentResolverCompatJellybean.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/content/ContentResolverCompatJellybean.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.content;
+
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+
+class ContentResolverCompatJellybean {
+ public static Cursor query(ContentResolver resolver, Uri uri, String[] projection,
+ String selection, String[] selectionArgs, String sortOrder,
+ Object cancellationSignalObj) {
+ return resolver.query(uri, projection, selection, selectionArgs, sortOrder,
+ (android.os.CancellationSignal)cancellationSignalObj);
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/content/ContextCompat.java support-v4-23.0.0/android/support/v4/content/ContextCompat.java
--- support-v4-22.2.1/android/support/v4/content/ContextCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/content/ContextCompat.java 2015-08-03 08:22:34.000000000 +0900
@@ -19,11 +19,13 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
-import android.os.StatFs;
+import android.os.Process;
+import android.support.annotation.NonNull;
import android.support.v4.os.EnvironmentCompat;
import android.util.Log;
@@ -95,7 +97,7 @@
* @param intents Array of intents defining the activities that will be started. The element
* length-1 will correspond to the top activity on the resulting task stack.
* @param options Additional options for how the Activity should be started.
- * See {@link android.content.Context#startActivity(Intent, Bundle)
+ * See {@link android.content.Context#startActivity(Intent, android.os.Bundle)
* @return true if the underlying API was available and the call was successful, false otherwise
*/
public static boolean startActivities(Context context, Intent[] intents,
@@ -133,7 +135,7 @@
* <p>
* An application may store data on any or all of the returned devices. For
* example, an app may choose to store large files on the device with the
- * most available space, as measured by {@link StatFs}.
+ * most available space, as measured by {@link android.os.StatFs}.
* <p>
* Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
* are required to write to the returned paths; they're always accessible to
@@ -192,7 +194,7 @@
* <p>
* An application may store data on any or all of the returned devices. For
* example, an app may choose to store large files on the device with the
- * most available space, as measured by {@link StatFs}.
+ * most available space, as measured by {@link android.os.StatFs}.
* <p>
* Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
* are required to write to the returned paths; they're always accessible to
@@ -252,7 +254,7 @@
* <p>
* An application may store data on any or all of the returned devices. For
* example, an app may choose to store large files on the device with the
- * most available space, as measured by {@link StatFs}.
+ * most available space, as measured by {@link android.os.StatFs}.
* <p>
* Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
* are required to write to the returned paths; they're always accessible to
@@ -323,6 +325,69 @@
}
/**
+ * Returns a color state list associated with a particular resource ID.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#M}, the returned
+ * color state list will be styled for the specified Context's theme.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ * @return A color state list, or {@code null} if the resource could not be
+ * resolved.
+ * @throws android.content.res.Resources.NotFoundException if the given ID
+ * does not exist.
+ */
+ public static final ColorStateList getColorStateList(Context context, int id) {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 23) {
+ return ContextCompatApi23.getColorStateList(context, id);
+ } else {
+ return context.getResources().getColorStateList(id);
+ }
+ }
+
+ /**
+ * Returns a color associated with a particular resource ID
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#M}, the returned
+ * color will be styled for the specified Context's theme.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ * @return A single color value in the form 0xAARRGGBB.
+ * @throws android.content.res.Resources.NotFoundException if the given ID
+ * does not exist.
+ */
+ public static final int getColor(Context context, int id) {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 23) {
+ return ContextCompatApi23.getColor(context, id);
+ } else {
+ return context.getResources().getColor(id);
+ }
+ }
+
+ /**
+ * Determine whether <em>you</em> have been granted a particular permission.
+ *
+ * @param permission The name of the permission being checked.
+ *
+ * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if you have the
+ * permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} if not.
+ *
+ * @see android.content.pm.PackageManager#checkPermission(String, String)
+ */
+ public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
+ if (permission == null) {
+ throw new IllegalArgumentException("permission is null");
+ }
+
+ return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
+ }
+
+ /**
* Returns the absolute path to the directory on the filesystem similar to
* {@link Context#getFilesDir()}. The difference is that files placed under this
* directory will be excluded from automatic backup to remote storage on
diff -Nur support-v4-22.2.1/android/support/v4/content/ContextCompatApi23.java support-v4-23.0.0/android/support/v4/content/ContextCompatApi23.java
--- support-v4-22.2.1/android/support/v4/content/ContextCompatApi23.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/content/ContextCompatApi23.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.content;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+
+import java.io.File;
+
+class ContextCompatApi23 {
+ public static ColorStateList getColorStateList(Context context, int id) {
+ return context.getColorStateList(id);
+ }
+
+ public static int getColor(Context context, int id) {
+ return context.getColor(id);
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/content/CursorLoader.java support-v4-23.0.0/android/support/v4/content/CursorLoader.java
--- support-v4-22.2.1/android/support/v4/content/CursorLoader.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/content/CursorLoader.java 2015-06-23 08:43:44.000000000 +0900
@@ -16,10 +16,12 @@
package android.support.v4.content;
+import android.content.ContentResolver;
import android.content.Context;
-import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
+import android.support.v4.os.CancellationSignal;
+import android.support.v4.os.OperationCanceledException;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -42,18 +44,48 @@
String mSortOrder;
Cursor mCursor;
+ CancellationSignal mCancellationSignal;
/* Runs on a worker thread */
@Override
public Cursor loadInBackground() {
- Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
- mSelectionArgs, mSortOrder);
- if (cursor != null) {
- // Ensure the cursor window is filled
- cursor.getCount();
- cursor.registerContentObserver(mObserver);
+ synchronized (this) {
+ if (isLoadInBackgroundCanceled()) {
+ throw new OperationCanceledException();
+ }
+ mCancellationSignal = new CancellationSignal();
+ }
+ try {
+ Cursor cursor = ContentResolverCompat.query(getContext().getContentResolver(),
+ mUri, mProjection, mSelection, mSelectionArgs, mSortOrder,
+ mCancellationSignal);
+ if (cursor != null) {
+ try {
+ // Ensure the cursor window is filled.
+ cursor.getCount();
+ cursor.registerContentObserver(mObserver);
+ } catch (RuntimeException ex) {
+ cursor.close();
+ throw ex;
+ }
+ }
+ return cursor;
+ } finally {
+ synchronized (this) {
+ mCancellationSignal = null;
+ }
+ }
+ }
+
+ @Override
+ public void cancelLoadInBackground() {
+ super.cancelLoadInBackground();
+
+ synchronized (this) {
+ if (mCancellationSignal != null) {
+ mCancellationSignal.cancel();
+ }
}
- return cursor;
}
/* Runs on the UI thread */
@@ -90,7 +122,7 @@
/**
* Creates a fully-specified CursorLoader. See
- * {@link android.content.ContentResolver#query(Uri, String[], String, String[], String)
+ * {@link ContentResolver#query(Uri, String[], String, String[], String)
* ContentResolver.query()} for documentation on the meaning of the
* parameters. These will be passed as-is to that call.
*/
diff -Nur support-v4-22.2.1/android/support/v4/content/EditorCompatGingerbread.java support-v4-23.0.0/android/support/v4/content/EditorCompatGingerbread.java
--- support-v4-22.2.1/android/support/v4/content/EditorCompatGingerbread.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/content/EditorCompatGingerbread.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,33 @@
+/*
+ * 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.content;
+
+import android.content.SharedPreferences;
+import android.support.annotation.NonNull;
+
+class EditorCompatGingerbread {
+ public static void apply(@NonNull SharedPreferences.Editor editor) {
+ try {
+ editor.apply();
+ } catch (AbstractMethodError unused) {
+ // The app injected its own pre-Gingerbread
+ // SharedPreferences.Editor implementation without
+ // an apply method.
+ editor.commit();
+ }
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/content/Loader.java support-v4-23.0.0/android/support/v4/content/Loader.java
--- support-v4-22.2.1/android/support/v4/content/Loader.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/content/Loader.java 2015-06-23 08:43:44.000000000 +0900
@@ -34,6 +34,7 @@
public class Loader<D> {
int mId;
OnLoadCompleteListener<D> mListener;
+ OnLoadCanceledListener<D> mOnLoadCanceledListener;
Context mContext;
boolean mStarted = false;
boolean mAbandoned = false;
@@ -45,8 +46,8 @@
* An implementation of a ContentObserver that takes care of connecting
* it to the Loader to have the loader re-load its data when the observer
* is told it has changed. You do not normally need to use this yourself;
- * it is used for you by {@link android.support.v4.content.CursorLoader}
- * to take care of executing an update when the cursor's backing data changes.
+ * it is used for you by {@link CursorLoader} to take care of executing
+ * an update when the cursor's backing data changes.
*/
public final class ForceLoadContentObserver extends ContentObserver {
public ForceLoadContentObserver() {
@@ -83,8 +84,29 @@
}
/**
- * Stores away the application context associated with context. Since Loaders can be used
- * across multiple activities it's dangerous to store the context directly.
+ * Interface that is implemented to discover when a Loader has been canceled
+ * before it finished loading its data. You do not normally need to implement
+ * this yourself; it is used in the implementation of {@link android.support.v4.app.LoaderManager}
+ * to find out when a Loader it is managing has been canceled so that it
+ * can schedule the next Loader. This interface should only be used if a
+ * Loader is not being used in conjunction with LoaderManager.
+ */
+ public interface OnLoadCanceledListener<D> {
+ /**
+ * Called on the thread that created the Loader when the load is canceled.
+ *
+ * @param loader the loader that canceled the load
+ */
+ public void onLoadCanceled(Loader<D> loader);
+ }
+
+ /**
+ * Stores away the application context associated with context.
+ * Since Loaders can be used across multiple activities it's dangerous to
+ * store the context directly; always use {@link #getContext()} to retrieve
+ * the Loader's Context, don't use the constructor argument directly.
+ * The Context returned by {@link #getContext} is safe to use across
+ * Activity instances.
*
* @param context used to retrieve the application context.
*/
@@ -106,6 +128,18 @@
}
/**
+ * Informs the registered {@link OnLoadCanceledListener} that the load has been canceled.
+ * Should only be called by subclasses.
+ *
+ * Must be called from the process's main thread.
+ */
+ public void deliverCancellation() {
+ if (mOnLoadCanceledListener != null) {
+ mOnLoadCanceledListener.onLoadCanceled(this);
+ }
+ }
+
+ /**
* @return an application context retrieved from the Context passed to the constructor.
*/
public Context getContext() {
@@ -150,6 +184,40 @@
}
/**
+ * Registers a listener that will receive callbacks when a load is canceled.
+ * The callback will be called on the process's main thread so it's safe to
+ * pass the results to widgets.
+ *
+ * Must be called from the process's main thread.
+ *
+ * @param listener The listener to register.
+ */
+ public void registerOnLoadCanceledListener(OnLoadCanceledListener<D> listener) {
+ if (mOnLoadCanceledListener != null) {
+ throw new IllegalStateException("There is already a listener registered");
+ }
+ mOnLoadCanceledListener = listener;
+ }
+
+ /**
+ * Unregisters a listener that was previously added with
+ * {@link #registerOnLoadCanceledListener}.
+ *
+ * Must be called from the process's main thread.
+ *
+ * @param listener The listener to unregister.
+ */
+ public void unregisterOnLoadCanceledListener(OnLoadCanceledListener<D> listener) {
+ if (mOnLoadCanceledListener == null) {
+ throw new IllegalStateException("No listener register");
+ }
+ if (mOnLoadCanceledListener != listener) {
+ throw new IllegalArgumentException("Attempting to unregister the wrong listener");
+ }
+ mOnLoadCanceledListener = null;
+ }
+
+ /**
* Return whether this load has been started. That is, its {@link #startLoading()}
* has been called and no calls to {@link #stopLoading()} or
* {@link #reset()} have yet been made.
@@ -177,6 +245,12 @@
}
/**
+ * This function will normally be called for you automatically by
+ * {@link android.support.v4.app.LoaderManager} when the associated fragment/activity
+ * is being started. When using a Loader with {@link android.support.v4.app.LoaderManager},
+ * you <em>must not</em> call this method yourself, or you will conflict
+ * with its management of the Loader.
+ *
* Starts an asynchronous load of the Loader's data. When the result
* is ready the callbacks will be called on the process's main thread.
* If a previous load has been completed and is still valid
@@ -207,6 +281,43 @@
}
/**
+ * Attempt to cancel the current load task.
+ * Must be called on the main thread of the process.
+ *
+ * <p>Cancellation is not an immediate operation, since the load is performed
+ * in a background thread. If there is currently a load in progress, this
+ * method requests that the load be canceled, and notes this is the case;
+ * once the background thread has completed its work its remaining state
+ * will be cleared. If another load request comes in during this time,
+ * it will be held until the canceled load is complete.
+ *
+ * @return Returns <tt>false</tt> if the task could not be canceled,
+ * typically because it has already completed normally, or
+ * because {@link #startLoading()} hasn't been called; returns
+ * <tt>true</tt> otherwise. When <tt>true</tt> is returned, the task
+ * is still running and the {@link OnLoadCanceledListener} will be called
+ * when the task completes.
+ */
+ public boolean cancelLoad() {
+ return onCancelLoad();
+ }
+
+ /**
+ * Subclasses must implement this to take care of requests to {@link #cancelLoad()}.
+ * This will always be called from the process's main thread.
+ *
+ * @return Returns <tt>false</tt> if the task could not be canceled,
+ * typically because it has already completed normally, or
+ * because {@link #startLoading()} hasn't been called; returns
+ * <tt>true</tt> otherwise. When <tt>true</tt> is returned, the task
+ * is still running and the {@link OnLoadCanceledListener} will be called
+ * when the task completes.
+ */
+ protected boolean onCancelLoad() {
+ return false;
+ }
+
+ /**
* Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
* loaded data set and load a new one. This simply calls through to the
* implementation's {@link #onForceLoad()}. You generally should only call this
@@ -226,7 +337,13 @@
}
/**
- * Stops delivery of updates until the next time {@link #startLoading()} is called.
+ * This function will normally be called for you automatically by
+ * {@link android.support.v4.app.LoaderManager} when the associated fragment/activity
+ * is being stopped. When using a Loader with {@link android.support.v4.app.LoaderManager},
+ * you <em>must not</em> call this method yourself, or you will conflict
+ * with its management of the Loader.
+ *
+ * <p>Stops delivery of updates until the next time {@link #startLoading()} is called.
* Implementations should <em>not</em> invalidate their data at this point --
* clients are still free to use the last data the loader reported. They will,
* however, typically stop reporting new data if the data changes; they can
@@ -254,6 +371,12 @@
}
/**
+ * This function will normally be called for you automatically by
+ * {@link android.support.v4.app.LoaderManager} when restarting a Loader. When using
+ * a Loader with {@link android.support.v4.app.LoaderManager},
+ * you <em>must not</em> call this method yourself, or you will conflict
+ * with its management of the Loader.
+ *
* Tell the Loader that it is being abandoned. This is called prior
* to {@link #reset} to have it retain its current data but not report
* any new data.
@@ -272,10 +395,16 @@
* {@link #onReset()} happens. You can retrieve the current abandoned
* state with {@link #isAbandoned}.
*/
- protected void onAbandon() {
+ protected void onAbandon() {
}
/**
+ * This function will normally be called for you automatically by
+ * {@link android.support.v4.app.LoaderManager} when destroying a Loader. When using
+ * a Loader with {@link android.support.v4.app.LoaderManager},
+ * you <em>must not</em> call this method yourself, or you will conflict
+ * with its management of the Loader.
+ *
* Resets the state of the Loader. The Loader should at this point free
* all of its resources, since it may never be called again; however, its
* {@link #startLoading()} may later be called at which point it must be
diff -Nur support-v4-22.2.1/android/support/v4/content/ModernAsyncTask.java support-v4-23.0.0/android/support/v4/content/ModernAsyncTask.java
--- support-v4-22.2.1/android/support/v4/content/ModernAsyncTask.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/content/ModernAsyncTask.java 2015-06-29 08:17:36.000000000 +0900
@@ -31,6 +31,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.Process;
@@ -72,7 +73,7 @@
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
- private static final InternalHandler sHandler = new InternalHandler();
+ private static InternalHandler sHandler;
private static volatile Executor sDefaultExecutor = THREAD_POOL_EXECUTOR;
private final WorkerRunnable<Params, Result> mWorker;
@@ -101,9 +102,13 @@
FINISHED,
}
- /** @hide Used to force static handler to be created. */
- public static void init() {
- sHandler.getLooper();
+ private static Handler getHandler() {
+ synchronized (ModernAsyncTask.class) {
+ if (sHandler == null) {
+ sHandler = new InternalHandler();
+ }
+ return sHandler;
+ }
}
/** @hide */
@@ -134,13 +139,13 @@
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
- throw new RuntimeException("An error occured while executing doInBackground()",
- e.getCause());
+ throw new RuntimeException(
+ "An error occurred while executing doInBackground()", e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
} catch (Throwable t) {
- throw new RuntimeException("An error occured while executing "
- + "doInBackground()", t);
+ throw new RuntimeException(
+ "An error occurred while executing doInBackground()", t);
}
}
};
@@ -154,7 +159,7 @@
}
private Result postResult(Result result) {
- Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
+ Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
@@ -449,7 +454,7 @@
*/
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
- sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
+ getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
@@ -464,6 +469,10 @@
}
private static class InternalHandler extends Handler {
+ public InternalHandler() {
+ super(Looper.getMainLooper());
+ }
+
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
diff -Nur support-v4-22.2.1/android/support/v4/content/PermissionChecker.java support-v4-23.0.0/android/support/v4/content/PermissionChecker.java
--- support-v4-22.2.1/android/support/v4/content/PermissionChecker.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/content/PermissionChecker.java 2015-08-06 08:26:24.000000000 +0900
@@ -0,0 +1,165 @@
+/*
+ * 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.content;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Process;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.v4.app.AppOpsManagerCompat;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class provides permission check APIs that verify both the
+ * permission and the associated app op for this permission if
+ * such is defined.
+ * <p>
+ * In the new permission model permissions with protection level
+ * dangerous are runtime permissions. For apps targeting {@link android.os.Build.VERSION_CODES#M}
+ * and above the user may not grant such permissions or revoke
+ * them at any time. For apps targeting API lower than {@link android.os.Build.VERSION_CODES#M}
+ * these permissions are always granted as such apps do not expect
+ * permission revocations and would crash. Therefore, when the
+ * user disables a permission for a legacy app in the UI the
+ * platform disables the APIs guarded by this permission making
+ * them a no-op which is doing nothing or returning an empty
+ * result or default error.
+ * </p>
+ * <p>
+ * It is important that when you perform an operation on behalf of
+ * another app you use these APIs to check for permissions as the
+ * app may be a legacy app that does not participate in the new
+ * permission model for which the user had disabled the "permission"
+ * which is achieved by disallowing the corresponding app op.
+ * </p>
+ */
+public final class PermissionChecker {
+ /** Permission result: The permission is granted. */
+ public static final int PERMISSION_GRANTED = PackageManager.PERMISSION_GRANTED;
+
+ /** Permission result: The permission is denied. */
+ public static final int PERMISSION_DENIED = PackageManager.PERMISSION_DENIED;
+
+ /** Permission result: The permission is denied because the app op is not allowed. */
+ public static final int PERMISSION_DENIED_APP_OP = PackageManager.PERMISSION_DENIED - 1;
+
+ @IntDef({PERMISSION_GRANTED,
+ PERMISSION_DENIED,
+ PERMISSION_DENIED_APP_OP})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PermissionResult {}
+
+ private PermissionChecker() {
+ /* do nothing */
+ }
+
+ /**
+ * Checks whether a given package in a UID and PID has a given permission
+ * and whether the app op that corresponds to this permission is allowed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param pid The process id for which to check.
+ * @param uid The uid for which to check.
+ * @param packageName The package name for which to check. If null the
+ * the first package for the calling UID will be used.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ */
+ public static int checkPermission(@NonNull Context context, @NonNull String permission,
+ int pid, int uid, String packageName) {
+ if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
+ return PERMISSION_DENIED;
+ }
+
+ String op = AppOpsManagerCompat.permissionToOp(permission);
+ if (op == null) {
+ return PERMISSION_GRANTED;
+ }
+
+ if (packageName == null) {
+ String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
+ if (packageNames == null || packageNames.length <= 0) {
+ return PERMISSION_DENIED;
+ }
+ packageName = packageNames[0];
+ }
+
+ if (AppOpsManagerCompat.noteProxyOp(context, op, packageName)
+ != AppOpsManagerCompat.MODE_ALLOWED) {
+ return PERMISSION_DENIED_APP_OP;
+ }
+
+ return PERMISSION_GRANTED;
+ }
+
+ /**
+ * Checks whether your app has a given permission and whether the app op
+ * that corresponds to this permission is allowed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ */
+ public static int checkSelfPermission(@NonNull Context context,
+ @NonNull String permission) {
+ return checkPermission(context, permission, android.os.Process.myPid(),
+ android.os.Process.myUid(), context.getPackageName());
+ }
+
+ /**
+ * Checks whether the IPC you are handling has a given permission and whether
+ * the app op that corresponds to this permission is allowed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param packageName The package name making the IPC. If null the
+ * the first package for the calling UID will be used.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ */
+ public static int checkCallingPermission(@NonNull Context context,
+ @NonNull String permission, String packageName) {
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ return checkPermission(context, permission, Binder.getCallingPid(),
+ Binder.getCallingUid(), packageName);
+ }
+
+ /**
+ * Checks whether the IPC you are handling or your app has a given permission
+ * and whether the app op that corresponds to this permission is allowed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ */
+ public static int checkCallingOrSelfPermission(@NonNull Context context,
+ @NonNull String permission) {
+ String packageName = (Binder.getCallingPid() == Process.myPid())
+ ? context.getPackageName() : null;
+ return checkPermission(context, permission, Binder.getCallingPid(),
+ Binder.getCallingUid(), packageName);
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/content/SharedPreferencesCompat.java support-v4-23.0.0/android/support/v4/content/SharedPreferencesCompat.java
--- support-v4-22.2.1/android/support/v4/content/SharedPreferencesCompat.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/content/SharedPreferencesCompat.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,71 @@
+/*
+ * 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.content;
+
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.support.annotation.NonNull;
+
+public class SharedPreferencesCompat {
+
+ public static class EditorCompat {
+
+ private static EditorCompat sInstance;
+
+ private interface Helper {
+ void apply(@NonNull SharedPreferences.Editor editor);
+ }
+
+ private static class EditorHelperBaseImpl implements Helper {
+
+ @Override
+ public void apply(@NonNull SharedPreferences.Editor editor) {
+ editor.commit();
+ }
+ }
+
+ private static class EditorHelperApi9Impl implements Helper {
+
+ @Override
+ public void apply(@NonNull SharedPreferences.Editor editor) {
+ EditorCompatGingerbread.apply(editor);
+ }
+ }
+
+ private final Helper mHelper;
+
+ private EditorCompat() {
+ if (Build.VERSION.SDK_INT >= 9) {
+ mHelper = new EditorHelperApi9Impl();
+ } else {
+ mHelper = new EditorHelperBaseImpl();
+ }
+ }
+
+ public static EditorCompat getInstance() {
+ if (sInstance == null) {
+ sInstance = new EditorCompat();
+ }
+ return sInstance;
+ }
+
+ public void apply(@NonNull SharedPreferences.Editor editor) {
+ mHelper.apply(editor);
+ }
+ }
+
+}
diff -Nur support-v4-22.2.1/android/support/v4/content/res/TypedArrayUtils.java support-v4-23.0.0/android/support/v4/content/res/TypedArrayUtils.java
--- support-v4-22.2.1/android/support/v4/content/res/TypedArrayUtils.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/content/res/TypedArrayUtils.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,73 @@
+/*
+ * 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.content.res;
+
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.AnyRes;
+import android.support.annotation.StyleableRes;
+
+/**
+ * Compat methods for accessing TypedArray values.
+ *
+ * @hide
+ */
+public class TypedArrayUtils {
+ public static boolean getBoolean(TypedArray a, @StyleableRes int index,
+ @StyleableRes int fallbackIndex, boolean defaultValue) {
+ boolean val = a.getBoolean(fallbackIndex, defaultValue);
+ return a.getBoolean(index, val);
+ }
+
+ public static Drawable getDrawable(TypedArray a, @StyleableRes int index,
+ @StyleableRes int fallbackIndex) {
+ Drawable val = a.getDrawable(index);
+ if (val == null) {
+ val = a.getDrawable(fallbackIndex);
+ }
+ return val;
+ }
+
+ public static int getInt(TypedArray a, @StyleableRes int index,
+ @StyleableRes int fallbackIndex, int defaultValue) {
+ int val = a.getInt(fallbackIndex, defaultValue);
+ return a.getInt(index, val);
+ }
+
+ public static @AnyRes int getResourceId(TypedArray a, @StyleableRes int index,
+ @StyleableRes int fallbackIndex, @AnyRes int defaultValue) {
+ int val = a.getResourceId(fallbackIndex, defaultValue);
+ return a.getResourceId(index, val);
+ }
+
+ public static String getString(TypedArray a, @StyleableRes int index,
+ @StyleableRes int fallbackIndex) {
+ String val = a.getString(index);
+ if (val == null) {
+ val = a.getString(fallbackIndex);
+ }
+ return val;
+ }
+
+ public static CharSequence[] getTextArray(TypedArray a, @StyleableRes int index,
+ @StyleableRes int fallbackIndex) {
+ CharSequence[] val = a.getTextArray(index);
+ if (val == null) {
+ val = a.getTextArray(fallbackIndex);
+ }
+ return val;
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/graphics/drawable/DrawableCompat.java support-v4-23.0.0/android/support/v4/graphics/drawable/DrawableCompat.java
--- support-v4-22.2.1/android/support/v4/graphics/drawable/DrawableCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/graphics/drawable/DrawableCompat.java 2015-07-26 08:18:46.000000000 +0900
@@ -19,6 +19,7 @@
import android.content.res.ColorStateList;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.support.v4.view.ViewCompat;
/**
* Helper for accessing features in {@link android.graphics.drawable.Drawable}
@@ -38,6 +39,8 @@
void setTintList(Drawable drawable, ColorStateList tint);
void setTintMode(Drawable drawable, PorterDuff.Mode tintMode);
Drawable wrap(Drawable drawable);
+ void setLayoutDirection(Drawable drawable, int layoutDirection);
+ int getLayoutDirection(Drawable drawable);
}
/**
@@ -84,6 +87,16 @@
public Drawable wrap(Drawable drawable) {
return DrawableCompatBase.wrapForTinting(drawable);
}
+
+ @Override
+ public void setLayoutDirection(Drawable drawable, int layoutDirection) {
+ // No op for API < 23
+ }
+
+ @Override
+ public int getLayoutDirection(Drawable drawable) {
+ return ViewCompat.LAYOUT_DIRECTION_LTR;
+ }
}
/**
@@ -101,10 +114,23 @@
}
}
+ static class JellybeanMr1DrawableImpl extends HoneycombDrawableImpl {
+ @Override
+ public void setLayoutDirection(Drawable drawable, int layoutDirection) {
+ DrawableCompatJellybeanMr1.setLayoutDirection(drawable, layoutDirection);
+ }
+
+ @Override
+ public int getLayoutDirection(Drawable drawable) {
+ final int dir = DrawableCompatJellybeanMr1.getLayoutDirection(drawable);
+ return dir < 0 ? dir : ViewCompat.LAYOUT_DIRECTION_LTR;
+ }
+ }
+
/**
* Interface implementation for devices with at least KitKat APIs.
*/
- static class KitKatDrawableImpl extends HoneycombDrawableImpl {
+ static class KitKatDrawableImpl extends JellybeanMr1DrawableImpl {
@Override
public void setAutoMirrored(Drawable drawable, boolean mirrored) {
DrawableCompatKitKat.setAutoMirrored(drawable, mirrored);
@@ -167,17 +193,36 @@
}
/**
+ * Interface implementation for devices with at least M APIs.
+ */
+ static class MDrawableImpl extends LollipopMr1DrawableImpl {
+ @Override
+ public void setLayoutDirection(Drawable drawable, int layoutDirection) {
+ DrawableCompatApi23.setLayoutDirection(drawable, layoutDirection);
+ }
+
+ @Override
+ public int getLayoutDirection(Drawable drawable) {
+ return DrawableCompatApi23.getLayoutDirection(drawable);
+ }
+ }
+
+ /**
* Select the correct implementation to use for the current platform.
*/
static final DrawableImpl IMPL;
static {
final int version = android.os.Build.VERSION.SDK_INT;
- if (version >= 22) {
+ if (version >= 23) {
+ IMPL = new MDrawableImpl();
+ } else if (version >= 22) {
IMPL = new LollipopMr1DrawableImpl();
} else if (version >= 21) {
IMPL = new LollipopDrawableImpl();
} else if (version >= 19) {
IMPL = new KitKatDrawableImpl();
+ } else if (version >= 17) {
+ IMPL = new JellybeanMr1DrawableImpl();
} else if (version >= 11) {
IMPL = new HoneycombDrawableImpl();
} else {
@@ -315,4 +360,29 @@
}
return (T) drawable;
}
+
+ /**
+ * Set the layout direction for this drawable. Should be a resolved
+ * layout direction, as the Drawable has no capacity to do the resolution on
+ * its own.
+ *
+ * @param layoutDirection the resolved layout direction for the drawable,
+ * either {@link ViewCompat#LAYOUT_DIRECTION_LTR}
+ * or {@link ViewCompat#LAYOUT_DIRECTION_RTL}
+ * @see #getLayoutDirection(Drawable)
+ */
+ public static void setLayoutDirection(Drawable drawable, int layoutDirection) {
+ IMPL.setLayoutDirection(drawable, layoutDirection);
+ }
+
+ /**
+ * Returns the resolved layout direction for this Drawable.
+ *
+ * @return One of {@link ViewCompat#LAYOUT_DIRECTION_LTR},
+ * {@link ViewCompat#LAYOUT_DIRECTION_RTL}
+ * @see #setLayoutDirection(Drawable, int)
+ */
+ public static int getLayoutDirection(Drawable drawable) {
+ return IMPL.getLayoutDirection(drawable);
+ }
}
diff -Nur support-v4-22.2.1/android/support/v4/graphics/drawable/DrawableCompatApi23.java support-v4-23.0.0/android/support/v4/graphics/drawable/DrawableCompatApi23.java
--- support-v4-22.2.1/android/support/v4/graphics/drawable/DrawableCompatApi23.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/graphics/drawable/DrawableCompatApi23.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,32 @@
+/*
+ * 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.graphics.drawable;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Implementation of drawable compatibility that can call M APIs.
+ */
+class DrawableCompatApi23 {
+ public static void setLayoutDirection(Drawable drawable, int layoutDirection) {
+ drawable.setLayoutDirection(layoutDirection);
+ }
+
+ public static int getLayoutDirection(Drawable drawable) {
+ return drawable.getLayoutDirection();
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java support-v4-23.0.0/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java
--- support-v4-22.2.1/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java 2015-07-26 08:18:46.000000000 +0900
@@ -0,0 +1,84 @@
+/*
+ * 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.graphics.drawable;
+
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.widget.CompoundButton;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Implementation of drawable compatibility that can call Jellybean MR1 APIs.
+ */
+class DrawableCompatJellybeanMr1 {
+
+ private static final String TAG = "DrawableCompatJellybeanMr1";
+
+ private static Method sSetLayoutDirectionMethod;
+ private static boolean sSetLayoutDirectionMethodFetched;
+
+ private static Method sGetLayoutDirectionMethod;
+ private static boolean sGetLayoutDirectionMethodFetched;
+
+ public static void setLayoutDirection(Drawable drawable, int layoutDirection) {
+ if (!sSetLayoutDirectionMethodFetched) {
+ try {
+ sSetLayoutDirectionMethod =
+ Drawable.class.getDeclaredMethod("setLayoutDirection", int.class);
+ sSetLayoutDirectionMethod.setAccessible(true);
+ } catch (NoSuchMethodException e) {
+ Log.i(TAG, "Failed to retrieve setLayoutDirection(int) method", e);
+ }
+ sSetLayoutDirectionMethodFetched = true;
+ }
+
+ if (sSetLayoutDirectionMethod != null) {
+ try {
+ sSetLayoutDirectionMethod.invoke(drawable, layoutDirection);
+ } catch (Exception e) {
+ Log.i(TAG, "Failed to invoke setLayoutDirection(int) via reflection", e);
+ sSetLayoutDirectionMethod = null;
+ }
+ }
+ }
+
+ public static int getLayoutDirection(Drawable drawable) {
+ if (!sGetLayoutDirectionMethodFetched) {
+ try {
+ sGetLayoutDirectionMethod = Drawable.class.getDeclaredMethod("getLayoutDirection");
+ sGetLayoutDirectionMethod.setAccessible(true);
+ } catch (NoSuchMethodException e) {
+ Log.i(TAG, "Failed to retrieve getLayoutDirection() method", e);
+ }
+ sGetLayoutDirectionMethodFetched = true;
+ }
+
+ if (sGetLayoutDirectionMethod != null) {
+ try {
+ return (int) sGetLayoutDirectionMethod.invoke(drawable);
+ } catch (Exception e) {
+ Log.i(TAG, "Failed to invoke getLayoutDirection() via reflection", e);
+ sGetLayoutDirectionMethod = null;
+ }
+ }
+ return -1;
+ }
+
+}
diff -Nur support-v4-22.2.1/android/support/v4/graphics/drawable/DrawableCompatLollipop.java support-v4-23.0.0/android/support/v4/graphics/drawable/DrawableCompatLollipop.java
--- support-v4-22.2.1/android/support/v4/graphics/drawable/DrawableCompatLollipop.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/graphics/drawable/DrawableCompatLollipop.java 2015-06-23 08:43:44.000000000 +0900
@@ -19,6 +19,7 @@
import android.content.res.ColorStateList;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableContainer;
import android.graphics.drawable.GradientDrawable;
/**
@@ -69,9 +70,10 @@
}
public static Drawable wrapForTinting(Drawable drawable) {
- if (drawable instanceof GradientDrawable) {
+ if (drawable instanceof GradientDrawable || drawable instanceof DrawableContainer) {
// GradientDrawable on Lollipop does not support tinting, so we'll use our compatible
- // functionality instead
+ // functionality instead. We also do the same for DrawableContainers since they may
+ // contain GradientDrawable instances.
return new DrawableWrapperLollipop(drawable);
}
return drawable;
diff -Nur support-v4-22.2.1/android/support/v4/graphics/drawable/DrawableWrapperDonut.java support-v4-23.0.0/android/support/v4/graphics/drawable/DrawableWrapperDonut.java
--- support-v4-22.2.1/android/support/v4/graphics/drawable/DrawableWrapperDonut.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/graphics/drawable/DrawableWrapperDonut.java 2015-06-23 08:43:44.000000000 +0900
@@ -215,6 +215,9 @@
mColorFilterSet = true;
return true;
}
+ } else {
+ mColorFilterSet = false;
+ clearColorFilter();
}
return false;
}
diff -Nur support-v4-22.2.1/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java support-v4-23.0.0/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java
--- support-v4-22.2.1/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java 2015-07-30 21:03:08.000000000 +0900
@@ -20,6 +20,7 @@
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -41,20 +42,22 @@
*/
public abstract class RoundedBitmapDrawable extends Drawable {
private static final int DEFAULT_PAINT_FLAGS =
- Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG;
- Bitmap mBitmap;
+ Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG;
+ final Bitmap mBitmap;
private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
private int mGravity = Gravity.FILL;
- private Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS);
- private BitmapShader mBitmapShader;
+ private final Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS);
+ private final BitmapShader mBitmapShader;
+ private final Matrix mShaderMatrix = new Matrix();
private float mCornerRadius;
final Rect mDstRect = new Rect(); // Gravity.apply() sets this
- final RectF mDstRectF = new RectF();
+ private final RectF mDstRectF = new RectF();
private boolean mApplyGravity = true;
+ private boolean mIsCircular;
- // These are scaled to match the target density.
+ // These are scaled to match the target density.
private int mBitmapWidth;
private int mBitmapHeight;
@@ -217,9 +220,32 @@
void updateDstRect() {
if (mApplyGravity) {
- gravityCompatApply(mGravity, mBitmapWidth, mBitmapHeight,
- getBounds(), mDstRect);
+ if (mIsCircular) {
+ final int minDimen = Math.min(mBitmapWidth, mBitmapHeight);
+ gravityCompatApply(mGravity, minDimen, minDimen, getBounds(), mDstRect);
+
+ // inset the drawing rectangle to the largest contained square,
+ // so that a circle will be drawn
+ final int minDrawDimen = Math.min(mDstRect.width(), mDstRect.height());
+ final int insetX = Math.max(0, (mDstRect.width() - minDrawDimen) / 2);
+ final int insetY = Math.max(0, (mDstRect.height() - minDrawDimen) / 2);
+ mDstRect.inset(insetX, insetY);
+ mCornerRadius = 0.5f * minDrawDimen;
+ } else {
+ gravityCompatApply(mGravity, mBitmapWidth, mBitmapHeight, getBounds(), mDstRect);
+ }
mDstRectF.set(mDstRect);
+
+ if (mBitmapShader != null) {
+ // setup shader matrix
+ mShaderMatrix.setTranslate(mDstRectF.left,mDstRectF.top);
+ mShaderMatrix.preScale(
+ mDstRectF.width() / mBitmap.getWidth(),
+ mDstRectF.height() / mBitmap.getHeight());
+ mBitmapShader.setLocalMatrix(mShaderMatrix);
+ mPaint.setShader(mBitmapShader);
+ }
+
mApplyGravity = false;
}
}
@@ -232,13 +258,10 @@
}
updateDstRect();
-
- final Paint paint = mPaint;
- final Shader shader = paint.getShader();
- if (shader == null) {
- canvas.drawBitmap(bitmap, null, mDstRect, paint);
+ if (mPaint.getShader() == null) {
+ canvas.drawBitmap(bitmap, null, mDstRect, mPaint);
} else {
- canvas.drawRoundRect(mDstRectF, mCornerRadius, mCornerRadius, paint);
+ canvas.drawRoundRect(mDstRectF, mCornerRadius, mCornerRadius, mPaint);
}
}
@@ -266,15 +289,57 @@
}
/**
+ * Sets the image shape to circular.
+ * <p>This overwrites any calls made to {@link #setCornerRadius(float)} so far.</p>
+ */
+ public void setCircular(boolean circular) {
+ mIsCircular = circular;
+ mApplyGravity = true;
+ if (circular) {
+ updateCircularCornerRadius();
+ mPaint.setShader(mBitmapShader);
+ invalidateSelf();
+ } else {
+ setCornerRadius(0);
+ }
+ }
+
+ private void updateCircularCornerRadius() {
+ final int minCircularSize = Math.min(mBitmapHeight, mBitmapWidth);
+ mCornerRadius = minCircularSize / 2;
+ }
+
+ /**
+ * @return <code>true</code> if the image is circular, else <code>false</code>.
+ */
+ public boolean isCircular() {
+ return mIsCircular;
+ }
+
+ /**
* Sets the corner radius to be applied when drawing the bitmap.
*/
public void setCornerRadius(float cornerRadius) {
+ if (mCornerRadius == cornerRadius) return;
+
+ mIsCircular = false;
if (isGreaterThanZero(cornerRadius)) {
mPaint.setShader(mBitmapShader);
} else {
mPaint.setShader(null);
}
+
mCornerRadius = cornerRadius;
+ invalidateSelf();
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ if (mIsCircular) {
+ updateCircularCornerRadius();
+ }
+ mApplyGravity = true;
}
/**
@@ -296,7 +361,7 @@
@Override
public int getOpacity() {
- if (mGravity != Gravity.FILL) {
+ if (mGravity != Gravity.FILL || mIsCircular) {
return PixelFormat.TRANSLUCENT;
}
Bitmap bm = mBitmap;
@@ -318,10 +383,11 @@
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
} else {
mBitmapWidth = mBitmapHeight = -1;
+ mBitmapShader = null;
}
}
private static boolean isGreaterThanZero(float toCompare) {
- return Float.compare(toCompare, +0.0f) > 0;
+ return toCompare > 0.05f;
}
}
diff -Nur support-v4-22.2.1/android/support/v4/graphics/drawable/RoundedBitmapDrawableFactory.java support-v4-23.0.0/android/support/v4/graphics/drawable/RoundedBitmapDrawableFactory.java
--- support-v4-22.2.1/android/support/v4/graphics/drawable/RoundedBitmapDrawableFactory.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/graphics/drawable/RoundedBitmapDrawableFactory.java 2015-07-30 21:03:08.000000000 +0900
@@ -77,7 +77,7 @@
String filepath) {
final RoundedBitmapDrawable drawable = create(res, BitmapFactory.decodeFile(filepath));
if (drawable.getBitmap() == null) {
- Log.w(TAG, "BitmapDrawable cannot decode " + filepath);
+ Log.w(TAG, "RoundedBitmapDrawable cannot decode " + filepath);
}
return drawable;
}
@@ -90,7 +90,7 @@
java.io.InputStream is) {
final RoundedBitmapDrawable drawable = create(res, BitmapFactory.decodeStream(is));
if (drawable.getBitmap() == null) {
- Log.w(TAG, "BitmapDrawable cannot decode " + is);
+ Log.w(TAG, "RoundedBitmapDrawable cannot decode " + is);
}
return drawable;
}
diff -Nur support-v4-22.2.1/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java support-v4-23.0.0/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java
--- support-v4-22.2.1/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java 2015-08-03 08:22:34.000000000 +0900
@@ -0,0 +1,312 @@
+/*
+ * 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.hardware.fingerprint;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.os.CancellationSignal;
+
+import java.security.Signature;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+/**
+ * A class that coordinates access to the fingerprint hardware.
+ * <p>
+ * On platforms before {@link android.os.Build.VERSION_CODES#M}, this class behaves as there would
+ * be no fingerprint hardware available.
+ */
+public class FingerprintManagerCompat {
+
+ private Context mContext;
+
+ /** Get a {@link FingerprintManagerCompat} instance for a provided context. */
+ public static FingerprintManagerCompat from(Context context) {
+ return new FingerprintManagerCompat(context);
+ }
+
+ private FingerprintManagerCompat(Context context) {
+ mContext = context;
+ }
+
+ static final FingerprintManagerCompatImpl IMPL;
+ static {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 23) {
+ IMPL = new Api23FingerprintManagerCompatImpl();
+ } else {
+ IMPL = new LegacyFingerprintManagerCompatImpl();
+ }
+ }
+
+ /**
+ * Determine if there is at least one fingerprint enrolled.
+ *
+ * @return true if at least one fingerprint is enrolled, false otherwise
+ */
+ public boolean hasEnrolledFingerprints() {
+ return IMPL.hasEnrolledFingerprints(mContext);
+ }
+
+ /**
+ * Determine if fingerprint hardware is present and functional.
+ *
+ * @return true if hardware is present and functional, false otherwise.
+ */
+ public boolean isHardwareDetected() {
+ return IMPL.isHardwareDetected(mContext);
+ }
+
+ /**
+ * Request authentication of a crypto object. This call warms up the fingerprint hardware
+ * and starts scanning for a fingerprint. It terminates when
+ * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
+ * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult) is called, at
+ * which point the object is no longer valid. The operation can be canceled by using the
+ * provided cancel object.
+ *
+ * @param crypto object associated with the call or null if none required.
+ * @param flags optional flags; should be 0
+ * @param cancel an object that can be used to cancel authentication
+ * @param callback an object to receive authentication events
+ * @param handler an optional handler for events
+ */
+ public void authenticate(@Nullable CryptoObject crypto, int flags,
+ @Nullable CancellationSignal cancel, @NonNull AuthenticationCallback callback,
+ @Nullable Handler handler) {
+ IMPL.authenticate(mContext, crypto, flags, cancel, callback, handler);
+ }
+
+ /**
+ * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
+ * framework supports {@link Signature} and {@link Cipher} objects.
+ */
+ public static class CryptoObject {
+
+ private final Signature mSignature;
+ private final Cipher mCipher;
+ private final Mac mMac;
+
+ public CryptoObject(Signature signature) {
+ mSignature = signature;
+ mCipher = null;
+ mMac = null;
+
+ }
+
+ public CryptoObject(Cipher cipher) {
+ mCipher = cipher;
+ mSignature = null;
+ mMac = null;
+ }
+
+ public CryptoObject(Mac mac) {
+ mMac = mac;
+ mCipher = null;
+ mSignature = null;
+ }
+
+ /**
+ * Get {@link Signature} object.
+ * @return {@link Signature} object or null if this doesn't contain one.
+ */
+ public Signature getSignature() { return mSignature; }
+
+ /**
+ * Get {@link Cipher} object.
+ * @return {@link Cipher} object or null if this doesn't contain one.
+ */
+ public Cipher getCipher() { return mCipher; }
+
+ /**
+ * Get {@link Mac} object.
+ * @return {@link Mac} object or null if this doesn't contain one.
+ */
+ public Mac getMac() { return mMac; }
+ }
+
+ /**
+ * Container for callback data from {@link FingerprintManagerCompat#authenticate(CryptoObject,
+ * int, CancellationSignal, AuthenticationCallback, Handler)}.
+ */
+ public static final class AuthenticationResult {
+ private CryptoObject mCryptoObject;
+
+ public AuthenticationResult(CryptoObject crypto) {
+ mCryptoObject = crypto;
+ }
+
+ /**
+ * Obtain the crypto object associated with this transaction
+ * @return crypto object provided to {@link FingerprintManagerCompat#authenticate(
+ * CryptoObject, int, CancellationSignal, AuthenticationCallback, Handler)}.
+ */
+ public CryptoObject getCryptoObject() { return mCryptoObject; }
+ }
+
+ /**
+ * Callback structure provided to {@link FingerprintManagerCompat#authenticate(CryptoObject,
+ * int, CancellationSignal, AuthenticationCallback, Handler)}. Users of {@link
+ * FingerprintManagerCompat#authenticate(CryptoObject, int, CancellationSignal,
+ * AuthenticationCallback, Handler) } must provide an implementation of this for listening to
+ * fingerprint events.
+ */
+ public static abstract class AuthenticationCallback {
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further callbacks will be made on this object.
+ * @param errMsgId An integer identifying the error message
+ * @param errString A human-readable error string that can be shown in UI
+ */
+ public void onAuthenticationError(int errMsgId, CharSequence errString) { }
+
+ /**
+ * Called when a recoverable error has been encountered during authentication. The help
+ * string is provided to give the user guidance for what went wrong, such as
+ * "Sensor dirty, please clean it."
+ * @param helpMsgId An integer identifying the error message
+ * @param helpString A human-readable string that can be shown in UI
+ */
+ public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { }
+
+ /**
+ * Called when a fingerprint is recognized.
+ * @param result An object containing authentication-related data
+ */
+ public void onAuthenticationSucceeded(AuthenticationResult result) { }
+
+ /**
+ * Called when a fingerprint is valid but not recognized.
+ */
+ public void onAuthenticationFailed() { }
+ }
+
+ private interface FingerprintManagerCompatImpl {
+ boolean hasEnrolledFingerprints(Context context);
+ boolean isHardwareDetected(Context context);
+ void authenticate(Context context, CryptoObject crypto, int flags,
+ CancellationSignal cancel, AuthenticationCallback callback, Handler handler);
+ }
+
+ private static class LegacyFingerprintManagerCompatImpl
+ implements FingerprintManagerCompatImpl {
+
+ public LegacyFingerprintManagerCompatImpl() {
+ }
+
+ @Override
+ public boolean hasEnrolledFingerprints(Context context) {
+ return false;
+ }
+
+ @Override
+ public boolean isHardwareDetected(Context context) {
+ return false;
+ }
+
+ @Override
+ public void authenticate(Context context, CryptoObject crypto, int flags,
+ CancellationSignal cancel, AuthenticationCallback callback, Handler handler) {
+ // TODO: Figure out behavior when there is no fingerprint hardware available
+ }
+ }
+
+ private static class Api23FingerprintManagerCompatImpl implements FingerprintManagerCompatImpl {
+
+ public Api23FingerprintManagerCompatImpl() {
+ }
+
+ @Override
+ public boolean hasEnrolledFingerprints(Context context) {
+ return FingerprintManagerCompatApi23.hasEnrolledFingerprints(context);
+ }
+
+ @Override
+ public boolean isHardwareDetected(Context context) {
+ return FingerprintManagerCompatApi23.isHardwareDetected(context);
+ }
+
+ @Override
+ public void authenticate(Context context, CryptoObject crypto, int flags,
+ CancellationSignal cancel, AuthenticationCallback callback, Handler handler) {
+ FingerprintManagerCompatApi23.authenticate(context, wrapCryptoObject(crypto), flags,
+ cancel != null ? cancel.getCancellationSignalObject() : null,
+ wrapCallback(callback), handler);
+ }
+
+ private static FingerprintManagerCompatApi23.CryptoObject wrapCryptoObject(
+ CryptoObject cryptoObject) {
+ if (cryptoObject == null) {
+ return null;
+ } else if (cryptoObject.getCipher() != null) {
+ return new FingerprintManagerCompatApi23.CryptoObject(cryptoObject.getCipher());
+ } else if (cryptoObject.getSignature() != null) {
+ return new FingerprintManagerCompatApi23.CryptoObject(cryptoObject.getSignature());
+ } else if (cryptoObject.getMac() != null) {
+ return new FingerprintManagerCompatApi23.CryptoObject(cryptoObject.getMac());
+ } else {
+ return null;
+ }
+ }
+
+ private static CryptoObject unwrapCryptoObject(
+ FingerprintManagerCompatApi23.CryptoObject cryptoObject) {
+ if (cryptoObject == null) {
+ return null;
+ } else if (cryptoObject.getCipher() != null) {
+ return new CryptoObject(cryptoObject.getCipher());
+ } else if (cryptoObject.getSignature() != null) {
+ return new CryptoObject(cryptoObject.getSignature());
+ } else if (cryptoObject.getMac() != null) {
+ return new CryptoObject(cryptoObject.getMac());
+ } else {
+ return null;
+ }
+ }
+
+ private static FingerprintManagerCompatApi23.AuthenticationCallback wrapCallback(
+ final AuthenticationCallback callback) {
+ return new FingerprintManagerCompatApi23.AuthenticationCallback() {
+ @Override
+ public void onAuthenticationError(int errMsgId, CharSequence errString) {
+ callback.onAuthenticationError(errMsgId, errString);
+ }
+
+ @Override
+ public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+ callback.onAuthenticationHelp(helpMsgId, helpString);
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(
+ FingerprintManagerCompatApi23.AuthenticationResultInternal result) {
+ callback.onAuthenticationSucceeded(new AuthenticationResult(
+ unwrapCryptoObject(result.getCryptoObject())));
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+ callback.onAuthenticationFailed();
+ }
+ };
+ }
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java support-v4-23.0.0/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java
--- support-v4-22.2.1/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java 2015-08-12 08:31:34.000000000 +0900
@@ -0,0 +1,153 @@
+/*
+ * 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.hardware.fingerprint;
+
+import android.content.Context;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Handler;
+
+import java.security.Signature;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+/**
+ * Actual FingerprintManagerCompat implementation for API level 23 and later.
+ * @hide
+ */
+public final class FingerprintManagerCompatApi23 {
+
+ private static FingerprintManager getFingerprintManager(Context ctx) {
+ return ctx.getSystemService(FingerprintManager.class);
+ }
+
+ public static boolean hasEnrolledFingerprints(Context context) {
+ return getFingerprintManager(context).hasEnrolledFingerprints();
+ }
+
+ public static boolean isHardwareDetected(Context context) {
+ return getFingerprintManager(context).isHardwareDetected();
+ }
+
+ public static void authenticate(Context context, CryptoObject crypto, int flags, Object cancel,
+ AuthenticationCallback callback, Handler handler) {
+ getFingerprintManager(context).authenticate(wrapCryptoObject(crypto),
+ (android.os.CancellationSignal) cancel, flags,
+ wrapCallback(callback), handler);
+ }
+
+ private static FingerprintManager.CryptoObject wrapCryptoObject(CryptoObject cryptoObject) {
+ if (cryptoObject == null) {
+ return null;
+ } else if (cryptoObject.getCipher() != null) {
+ return new FingerprintManager.CryptoObject(cryptoObject.getCipher());
+ } else if (cryptoObject.getSignature() != null) {
+ return new FingerprintManager.CryptoObject(cryptoObject.getSignature());
+ } else if (cryptoObject.getMac() != null) {
+ return new FingerprintManager.CryptoObject(cryptoObject.getMac());
+ } else {
+ return null;
+ }
+ }
+
+ private static CryptoObject unwrapCryptoObject(FingerprintManager.CryptoObject cryptoObject) {
+ if (cryptoObject == null) {
+ return null;
+ } else if (cryptoObject.getCipher() != null) {
+ return new CryptoObject(cryptoObject.getCipher());
+ } else if (cryptoObject.getSignature() != null) {
+ return new CryptoObject(cryptoObject.getSignature());
+ } else if (cryptoObject.getMac() != null) {
+ return new CryptoObject(cryptoObject.getMac());
+ } else {
+ return null;
+ }
+ }
+
+ private static FingerprintManager.AuthenticationCallback wrapCallback(
+ final AuthenticationCallback callback) {
+ return new FingerprintManager.AuthenticationCallback() {
+ @Override
+ public void onAuthenticationError(int errMsgId, CharSequence errString) {
+ callback.onAuthenticationError(errMsgId, errString);
+ }
+
+ @Override
+ public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+ callback.onAuthenticationHelp(helpMsgId, helpString);
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
+ callback.onAuthenticationSucceeded(new AuthenticationResultInternal(
+ unwrapCryptoObject(result.getCryptoObject())));
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+ callback.onAuthenticationFailed();
+ }
+ };
+ }
+
+ public static class CryptoObject {
+
+ private final Signature mSignature;
+ private final Cipher mCipher;
+ private final Mac mMac;
+
+ public CryptoObject(Signature signature) {
+ mSignature = signature;
+ mCipher = null;
+ mMac = null;
+ }
+
+ public CryptoObject(Cipher cipher) {
+ mCipher = cipher;
+ mSignature = null;
+ mMac = null;
+ }
+
+ public CryptoObject(Mac mac) {
+ mMac = mac;
+ mCipher = null;
+ mSignature = null;
+ }
+
+ public Signature getSignature() { return mSignature; }
+ public Cipher getCipher() { return mCipher; }
+ public Mac getMac() { return mMac; }
+ }
+
+ public static final class AuthenticationResultInternal {
+ private CryptoObject mCryptoObject;
+
+ public AuthenticationResultInternal(CryptoObject crypto) {
+ mCryptoObject = crypto;
+ }
+
+ public CryptoObject getCryptoObject() { return mCryptoObject; }
+ }
+
+ public static abstract class AuthenticationCallback {
+
+ public void onAuthenticationError(int errMsgId, CharSequence errString) { }
+ public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { }
+ public void onAuthenticationSucceeded(AuthenticationResultInternal result) { }
+ public void onAuthenticationFailed() { }
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/media/MediaControllerCompatApi23.java support-v4-23.0.0/android/support/v4/media/MediaControllerCompatApi23.java
--- support-v4-22.2.1/android/support/v4/media/MediaControllerCompatApi23.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/media/MediaControllerCompatApi23.java 2015-07-30 21:03:08.000000000 +0900
@@ -0,0 +1,30 @@
+/*
+ * 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.media.session;
+
+import android.media.session.MediaController;
+import android.net.Uri;
+import android.os.Bundle;
+
+class MediaControllerCompatApi23 {
+
+ public static class TransportControls extends MediaControllerCompatApi21.TransportControls {
+ public static void playFromUri(Object controlsObj, Uri uri, Bundle extras) {
+ ((MediaController.TransportControls) controlsObj).playFromUri(uri, extras);
+ }
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/media/MediaDescriptionCompat.java support-v4-23.0.0/android/support/v4/media/MediaDescriptionCompat.java
--- support-v4-22.2.1/android/support/v4/media/MediaDescriptionCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/media/MediaDescriptionCompat.java 2015-07-26 08:18:46.000000000 +0900
@@ -21,6 +21,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.annotation.Nullable;
import android.text.TextUtils;
/**
@@ -57,6 +58,10 @@
* Extras for opaque use by apps/system.
*/
private final Bundle mExtras;
+ /**
+ * A Uri to identify this content.
+ */
+ private final Uri mMediaUri;
/**
* A cached copy of the equivalent framework object.
@@ -64,7 +69,7 @@
private Object mDescriptionObj;
private MediaDescriptionCompat(String mediaId, CharSequence title, CharSequence subtitle,
- CharSequence description, Bitmap icon, Uri iconUri, Bundle extras) {
+ CharSequence description, Bitmap icon, Uri iconUri, Bundle extras, Uri mediaUri) {
mMediaId = mediaId;
mTitle = title;
mSubtitle = subtitle;
@@ -72,6 +77,7 @@
mIcon = icon;
mIconUri = iconUri;
mExtras = extras;
+ mMediaUri = mediaUri;
}
private MediaDescriptionCompat(Parcel in) {
@@ -82,12 +88,14 @@
mIcon = in.readParcelable(null);
mIconUri = in.readParcelable(null);
mExtras = in.readBundle();
+ mMediaUri = in.readParcelable(null);
}
/**
* Returns the media id or null. See
* {@link MediaMetadataCompat#METADATA_KEY_MEDIA_ID}.
*/
+ @Nullable
public String getMediaId() {
return mMediaId;
}
@@ -97,6 +105,7 @@
*
* @return A title or null.
*/
+ @Nullable
public CharSequence getTitle() {
return mTitle;
}
@@ -106,6 +115,7 @@
*
* @return A subtitle or null.
*/
+ @Nullable
public CharSequence getSubtitle() {
return mSubtitle;
}
@@ -115,6 +125,7 @@
*
* @return A description or null.
*/
+ @Nullable
public CharSequence getDescription() {
return mDescription;
}
@@ -124,6 +135,7 @@
*
* @return An icon or null.
*/
+ @Nullable
public Bitmap getIconBitmap() {
return mIcon;
}
@@ -133,6 +145,7 @@
*
* @return An icon uri or null.
*/
+ @Nullable
public Uri getIconUri() {
return mIconUri;
}
@@ -142,10 +155,21 @@
*
* @return A bundle of extras or null.
*/
+ @Nullable
public Bundle getExtras() {
return mExtras;
}
+ /**
+ * Returns a Uri representing this content or null.
+ *
+ * @return A media Uri or null.
+ */
+ @Nullable
+ public Uri getMediaUri() {
+ return mMediaUri;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -194,6 +218,9 @@
MediaDescriptionCompatApi21.Builder.setIconBitmap(bob, mIcon);
MediaDescriptionCompatApi21.Builder.setIconUri(bob, mIconUri);
MediaDescriptionCompatApi21.Builder.setExtras(bob, mExtras);
+ if (Build.VERSION.SDK_INT >= 23) {
+ MediaDescriptionCompatApi23.Builder.setMediaUri(bob, mMediaUri);
+ }
mDescriptionObj = MediaDescriptionCompatApi21.Builder.build(bob);
return mDescriptionObj;
@@ -224,6 +251,9 @@
bob.setIconBitmap(MediaDescriptionCompatApi21.getIconBitmap(descriptionObj));
bob.setIconUri(MediaDescriptionCompatApi21.getIconUri(descriptionObj));
bob.setExtras(MediaDescriptionCompatApi21.getExtras(descriptionObj));
+ if (Build.VERSION.SDK_INT >= 23) {
+ bob.setMediaUri(MediaDescriptionCompatApi23.getMediaUri(descriptionObj));
+ }
MediaDescriptionCompat descriptionCompat = bob.build();
descriptionCompat.mDescriptionObj = descriptionObj;
@@ -258,6 +288,7 @@
private Bitmap mIcon;
private Uri mIconUri;
private Bundle mExtras;
+ private Uri mMediaUri;
/**
* Creates an initially empty builder.
@@ -271,7 +302,7 @@
* @param mediaId The unique id for the item or null.
* @return this
*/
- public Builder setMediaId(String mediaId) {
+ public Builder setMediaId(@Nullable String mediaId) {
mMediaId = mediaId;
return this;
}
@@ -282,7 +313,7 @@
* @param title A title suitable for display to the user or null.
* @return this
*/
- public Builder setTitle(CharSequence title) {
+ public Builder setTitle(@Nullable CharSequence title) {
mTitle = title;
return this;
}
@@ -293,7 +324,7 @@
* @param subtitle A subtitle suitable for display to the user or null.
* @return this
*/
- public Builder setSubtitle(CharSequence subtitle) {
+ public Builder setSubtitle(@Nullable CharSequence subtitle) {
mSubtitle = subtitle;
return this;
}
@@ -305,7 +336,7 @@
* null.
* @return this
*/
- public Builder setDescription(CharSequence description) {
+ public Builder setDescription(@Nullable CharSequence description) {
mDescription = description;
return this;
}
@@ -317,7 +348,7 @@
* null.
* @return this
*/
- public Builder setIconBitmap(Bitmap icon) {
+ public Builder setIconBitmap(@Nullable Bitmap icon) {
mIcon = icon;
return this;
}
@@ -329,7 +360,7 @@
* user or null.
* @return this
*/
- public Builder setIconUri(Uri iconUri) {
+ public Builder setIconUri(@Nullable Uri iconUri) {
mIconUri = iconUri;
return this;
}
@@ -340,12 +371,23 @@
* @param extras The extras to include with this description or null.
* @return this
*/
- public Builder setExtras(Bundle extras) {
+ public Builder setExtras(@Nullable Bundle extras) {
mExtras = extras;
return this;
}
/**
+ * Sets the media uri.
+ *
+ * @param mediaUri The content's {@link Uri} for the item or null.
+ * @return this
+ */
+ public Builder setMediaUri(@Nullable Uri mediaUri) {
+ mMediaUri = mediaUri;
+ return this;
+ }
+
+ /**
* Creates a {@link MediaDescriptionCompat} instance with the specified
* fields.
*
@@ -353,7 +395,7 @@
*/
public MediaDescriptionCompat build() {
return new MediaDescriptionCompat(mMediaId, mTitle, mSubtitle, mDescription, mIcon,
- mIconUri, mExtras);
+ mIconUri, mExtras, mMediaUri);
}
}
}
diff -Nur support-v4-22.2.1/android/support/v4/media/MediaDescriptionCompatApi23.java support-v4-23.0.0/android/support/v4/media/MediaDescriptionCompatApi23.java
--- support-v4-22.2.1/android/support/v4/media/MediaDescriptionCompatApi23.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/media/MediaDescriptionCompatApi23.java 2015-07-26 08:18:46.000000000 +0900
@@ -0,0 +1,34 @@
+/*
+ * 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.media;
+
+import android.graphics.Bitmap;
+import android.media.MediaDescription;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+
+class MediaDescriptionCompatApi23 extends MediaDescriptionCompatApi21 {
+ public static Uri getMediaUri(Object descriptionObj) {
+ return ((MediaDescription) descriptionObj).getMediaUri();
+ }
+
+ static class Builder extends MediaDescriptionCompatApi21.Builder {
+ public static void setMediaUri(Object builderObj, Uri mediaUri) {
+ ((MediaDescription.Builder)builderObj).setMediaUri(mediaUri);
+ }
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/media/MediaMetadataCompat.java support-v4-23.0.0/android/support/v4/media/MediaMetadataCompat.java
--- support-v4-22.2.1/android/support/v4/media/MediaMetadataCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/media/MediaMetadataCompat.java 2015-07-26 08:18:46.000000000 +0900
@@ -21,10 +21,13 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.annotation.StringDef;
import android.support.v4.util.ArrayMap;
import android.text.TextUtils;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Set;
/**
@@ -193,6 +196,40 @@
*/
public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+ /**
+ * @hide
+ */
+ @StringDef({METADATA_KEY_TITLE, METADATA_KEY_ARTIST, METADATA_KEY_ALBUM, METADATA_KEY_AUTHOR,
+ METADATA_KEY_WRITER, METADATA_KEY_COMPOSER, METADATA_KEY_COMPILATION,
+ METADATA_KEY_DATE, METADATA_KEY_GENRE, METADATA_KEY_ALBUM_ARTIST, METADATA_KEY_ART_URI,
+ METADATA_KEY_ALBUM_ART_URI, METADATA_KEY_DISPLAY_TITLE, METADATA_KEY_DISPLAY_SUBTITLE,
+ METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_KEY_DISPLAY_ICON_URI,
+ METADATA_KEY_MEDIA_ID})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TextKey {}
+
+ /**
+ * @hide
+ */
+ @StringDef({METADATA_KEY_DURATION, METADATA_KEY_YEAR, METADATA_KEY_TRACK_NUMBER,
+ METADATA_KEY_NUM_TRACKS, METADATA_KEY_DISC_NUMBER})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LongKey {}
+
+ /**
+ * @hide
+ */
+ @StringDef({METADATA_KEY_ART, METADATA_KEY_ALBUM_ART, METADATA_KEY_DISPLAY_ICON})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BitmapKey {}
+
+ /**
+ * @hide
+ */
+ @StringDef({METADATA_KEY_USER_RATING, METADATA_KEY_RATING})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RatingKey {}
+
private static final int METADATA_TYPE_LONG = 0;
private static final int METADATA_TYPE_TEXT = 1;
private static final int METADATA_TYPE_BITMAP = 2;
@@ -230,7 +267,7 @@
METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT);
}
- private static final String[] PREFERRED_DESCRIPTION_ORDER = {
+ private static final @TextKey String[] PREFERRED_DESCRIPTION_ORDER = {
METADATA_KEY_TITLE,
METADATA_KEY_ARTIST,
METADATA_KEY_ALBUM,
@@ -240,13 +277,13 @@
METADATA_KEY_COMPOSER
};
- private static final String[] PREFERRED_BITMAP_ORDER = {
+ private static final @BitmapKey String[] PREFERRED_BITMAP_ORDER = {
METADATA_KEY_DISPLAY_ICON,
METADATA_KEY_ART,
METADATA_KEY_ALBUM_ART
};
- private static final String[] PREFERRED_URI_ORDER = {
+ private static final @TextKey String[] PREFERRED_URI_ORDER = {
METADATA_KEY_DISPLAY_ICON_URI,
METADATA_KEY_ART_URI,
METADATA_KEY_ALBUM_ART_URI
@@ -282,7 +319,7 @@
* @param key The key the value is stored under
* @return a CharSequence value, or null
*/
- public CharSequence getText(String key) {
+ public CharSequence getText(@TextKey String key) {
return mBundle.getCharSequence(key);
}
@@ -294,7 +331,7 @@
* @param key The key the value is stored under
* @return a String value, or null
*/
- public String getString(String key) {
+ public String getString(@TextKey String key) {
CharSequence text = mBundle.getCharSequence(key);
if (text != null) {
return text.toString();
@@ -309,7 +346,7 @@
* @param key The key the value is stored under
* @return a long value
*/
- public long getLong(String key) {
+ public long getLong(@LongKey String key) {
return mBundle.getLong(key, 0);
}
@@ -320,7 +357,7 @@
* @param key The key the value is stored under
* @return A {@link RatingCompat} or null
*/
- public RatingCompat getRating(String key) {
+ public RatingCompat getRating(@RatingKey String key) {
RatingCompat rating = null;
try {
rating = mBundle.getParcelable(key);
@@ -338,7 +375,7 @@
* @param key The key the value is stored under
* @return A {@link Bitmap} or null
*/
- public Bitmap getBitmap(String key) {
+ public Bitmap getBitmap(@BitmapKey String key) {
Bitmap bmp = null;
try {
bmp = mBundle.getParcelable(key);
@@ -609,7 +646,7 @@
* @param value The CharSequence value to store
* @return The Builder to allow chaining
*/
- public Builder putText(String key, CharSequence value) {
+ public Builder putText(@TextKey String key, CharSequence value) {
if (METADATA_KEYS_TYPE.containsKey(key)) {
if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
throw new IllegalArgumentException("The " + key
@@ -646,7 +683,7 @@
* @param value The String value to store
* @return The Builder to allow chaining
*/
- public Builder putString(String key, String value) {
+ public Builder putString(@TextKey String key, String value) {
if (METADATA_KEYS_TYPE.containsKey(key)) {
if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
throw new IllegalArgumentException("The " + key
@@ -673,7 +710,7 @@
* @param value The String value to store
* @return The Builder to allow chaining
*/
- public Builder putLong(String key, long value) {
+ public Builder putLong(@LongKey String key, long value) {
if (METADATA_KEYS_TYPE.containsKey(key)) {
if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) {
throw new IllegalArgumentException("The " + key
@@ -697,7 +734,7 @@
* @param value The String value to store
* @return The Builder to allow chaining
*/
- public Builder putRating(String key, RatingCompat value) {
+ public Builder putRating(@RatingKey String key, RatingCompat value) {
if (METADATA_KEYS_TYPE.containsKey(key)) {
if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) {
throw new IllegalArgumentException("The " + key
@@ -722,7 +759,7 @@
* @param value The Bitmap to store
* @return The Builder to allow chaining
*/
- public Builder putBitmap(String key, Bitmap value) {
+ public Builder putBitmap(@BitmapKey String key, Bitmap value) {
if (METADATA_KEYS_TYPE.containsKey(key)) {
if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
throw new IllegalArgumentException("The " + key
diff -Nur support-v4-22.2.1/android/support/v4/media/MediaSessionCompatApi23.java support-v4-23.0.0/android/support/v4/media/MediaSessionCompatApi23.java
--- support-v4-22.2.1/android/support/v4/media/MediaSessionCompatApi23.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/media/MediaSessionCompatApi23.java 2015-07-30 21:03:08.000000000 +0900
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.media.session;
+
+import android.net.Uri;
+import android.os.Bundle;
+
+class MediaSessionCompatApi23 {
+
+ public static Object createCallback(Callback callback) {
+ return new CallbackProxy<Callback>(callback);
+ }
+
+ public static interface Callback extends MediaSessionCompatApi21.Callback {
+ public void onPlayFromUri(Uri uri, Bundle extras);
+ }
+
+ static class CallbackProxy<T extends Callback> extends MediaSessionCompatApi21.CallbackProxy<T> {
+ public CallbackProxy(T callback) {
+ super(callback);
+ }
+
+ @Override
+ public void onPlayFromUri(Uri uri, Bundle extras) {
+ mCallback.onPlayFromUri(uri, extras);
+ }
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/media/RatingCompat.java support-v4-23.0.0/android/support/v4/media/RatingCompat.java
--- support-v4-22.2.1/android/support/v4/media/RatingCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/media/RatingCompat.java 2015-07-26 08:18:46.000000000 +0900
@@ -19,8 +19,12 @@
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.annotation.IntDef;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A class to encapsulate rating information used as content metadata.
* A rating is defined by its rating style (see {@link #RATING_HEART},
@@ -33,6 +37,21 @@
private final static String TAG = "Rating";
/**
+ * @hide
+ */
+ @IntDef({RATING_NONE, RATING_HEART, RATING_THUMB_UP_DOWN, RATING_3_STARS, RATING_4_STARS,
+ RATING_5_STARS, RATING_PERCENTAGE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Style {}
+
+ /**
+ * @hide
+ */
+ @IntDef({RATING_3_STARS, RATING_4_STARS, RATING_5_STARS})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StarStyle {}
+
+ /**
* Indicates a rating style is not supported. A Rating will never have this
* type, but can be used by other classes to indicate they do not support
* Rating.
@@ -77,7 +96,7 @@
private Object mRatingObj; // framework Rating object
- private RatingCompat(int ratingStyle, float rating) {
+ private RatingCompat(@Style int ratingStyle, float rating) {
mRatingStyle = ratingStyle;
mRatingValue = rating;
}
@@ -126,7 +145,7 @@
* or {@link #RATING_PERCENTAGE}.
* @return null if an invalid rating style is passed, a new Rating instance otherwise.
*/
- public static RatingCompat newUnratedRating(int ratingStyle) {
+ public static RatingCompat newUnratedRating(@Style int ratingStyle) {
switch(ratingStyle) {
case RATING_HEART:
case RATING_THUMB_UP_DOWN:
@@ -174,7 +193,8 @@
* @return null if the rating style is invalid, or the rating is out of range,
* a new Rating instance otherwise.
*/
- public static RatingCompat newStarRating(int starRatingStyle, float starRating) {
+ public static RatingCompat newStarRating(@StarStyle int starRatingStyle,
+ float starRating) {
float maxRating = -1.0f;
switch(starRatingStyle) {
case RATING_3_STARS:
@@ -227,6 +247,7 @@
* {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
* or {@link #RATING_PERCENTAGE}.
*/
+ @Style
public int getRatingStyle() {
return mRatingStyle;
}
@@ -367,4 +388,4 @@
}
return mRatingObj;
}
-}
\ No newline at end of file
+}
diff -Nur support-v4-22.2.1/android/support/v4/media/VolumeProviderCompat.java support-v4-23.0.0/android/support/v4/media/VolumeProviderCompat.java
--- support-v4-22.2.1/android/support/v4/media/VolumeProviderCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/media/VolumeProviderCompat.java 2015-08-04 08:23:06.000000000 +0900
@@ -17,8 +17,12 @@
package android.support.v4.media;
import android.os.Build;
+import android.support.annotation.IntDef;
import android.support.v4.media.session.MediaSessionCompat;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Handles requests to adjust or set the volume on a session. This is also used
* to push volume updates back to the session after a request has been handled.
@@ -26,6 +30,14 @@
* {@link MediaSessionCompat#setPlaybackToRemote}.
*/
public abstract class VolumeProviderCompat {
+
+ /**
+ * @hide
+ */
+ @IntDef({VOLUME_CONTROL_FIXED, VOLUME_CONTROL_RELATIVE, VOLUME_CONTROL_ABSOLUTE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ControlType {}
+
/**
* The volume is fixed and can not be modified. Requests to change volume
* should be ignored.
@@ -62,7 +74,7 @@
* @param maxVolume The maximum allowed volume.
* @param currentVolume The current volume.
*/
- public VolumeProviderCompat(int volumeControl, int maxVolume, int currentVolume) {
+ public VolumeProviderCompat(@ControlType int volumeControl, int maxVolume, int currentVolume) {
mControlType = volumeControl;
mMaxVolume = maxVolume;
mCurrentVolume = currentVolume;
@@ -82,6 +94,7 @@
*
* @return The volume control type for this volume provider
*/
+ @ControlType
public final int getVolumeControl() {
return mControlType;
}
@@ -102,6 +115,11 @@
* @param currentVolume The current volume of the output.
*/
public final void setCurrentVolume(int currentVolume) {
+ mCurrentVolume = currentVolume;
+ Object volumeProviderObj = getVolumeProvider();
+ if (volumeProviderObj != null) {
+ VolumeProviderCompatApi21.setCurrentVolume(volumeProviderObj, currentVolume);
+ }
if (mCallback != null) {
mCallback.onVolumeChanged(this);
}
@@ -168,4 +186,4 @@
public static abstract class Callback {
public abstract void onVolumeChanged(VolumeProviderCompat volumeProvider);
}
-}
\ No newline at end of file
+}
diff -Nur support-v4-22.2.1/android/support/v4/media/session/IMediaSession.aidl support-v4-23.0.0/android/support/v4/media/session/IMediaSession.aidl
--- support-v4-22.2.1/android/support/v4/media/session/IMediaSession.aidl 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/media/session/IMediaSession.aidl 2015-07-30 21:03:08.000000000 +0900
@@ -50,6 +50,7 @@
void play();
void playFromMediaId(String uri, in Bundle extras);
void playFromSearch(String string, in Bundle extras);
+ void playFromUri(in Uri uri, in Bundle extras);
void skipToQueueItem(long id);
void pause();
void stop();
diff -Nur support-v4-22.2.1/android/support/v4/media/session/MediaControllerCompat.java support-v4-23.0.0/android/support/v4/media/session/MediaControllerCompat.java
--- support-v4-22.2.1/android/support/v4/media/session/MediaControllerCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/media/session/MediaControllerCompat.java 2015-07-30 21:03:08.000000000 +0900
@@ -69,7 +69,9 @@
}
mToken = session.getSessionToken();
- if (android.os.Build.VERSION.SDK_INT >= 21) {
+ if (android.os.Build.VERSION.SDK_INT >= 23) {
+ mImpl = new MediaControllerImplApi23(context, session);
+ } else if (android.os.Build.VERSION.SDK_INT >= 21) {
mImpl = new MediaControllerImplApi21(context, session);
} else {
mImpl = new MediaControllerImplBase(mToken);
@@ -592,6 +594,15 @@
public abstract void playFromSearch(String query, Bundle extras);
/**
+ * Request that the player start playback for a specific {@link Uri}.
+ *
+ * @param uri The URI of the requested media.
+ * @param extras Optional extras that can include extra information about the media item
+ * to be played.
+ */
+ public abstract void playFromUri(Uri uri, Bundle extras);
+
+ /**
* Play an item with a specific id in the play queue. If you specify an
* id that is not in the play queue, the behavior is undefined.
*/
@@ -1019,6 +1030,15 @@
}
@Override
+ public void playFromUri(Uri uri, Bundle extras) {
+ try {
+ mBinder.playFromUri(uri, extras);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in playFromUri. " + e);
+ }
+ }
+
+ @Override
public void skipToQueueItem(long id) {
try {
mBinder.skipToQueueItem(id);
@@ -1115,7 +1135,7 @@
}
static class MediaControllerImplApi21 implements MediaControllerImpl {
- private final Object mControllerObj;
+ protected final Object mControllerObj;
public MediaControllerImplApi21(Context context, MediaSessionCompat session) {
mControllerObj = MediaControllerCompatApi21.fromToken(context,
@@ -1239,7 +1259,7 @@
}
static class TransportControlsApi21 extends TransportControls {
- private final Object mControlsObj;
+ protected final Object mControlsObj;
public TransportControlsApi21(Object controlsObj) {
mControlsObj = controlsObj;
@@ -1304,6 +1324,10 @@
}
@Override
+ public void playFromUri(Uri uri, Bundle extras) {
+ }
+
+ @Override
public void skipToQueueItem(long id) {
MediaControllerCompatApi21.TransportControls.skipToQueueItem(mControlsObj, id);
}
@@ -1320,4 +1344,35 @@
args);
}
}
+
+ static class MediaControllerImplApi23 extends MediaControllerImplApi21 {
+
+ public MediaControllerImplApi23(Context context, MediaSessionCompat session) {
+ super(context, session);
+ }
+
+ public MediaControllerImplApi23(Context context, MediaSessionCompat.Token sessionToken)
+ throws RemoteException {
+ super(context, sessionToken);
+ }
+
+ @Override
+ public TransportControls getTransportControls() {
+ Object controlsObj = MediaControllerCompatApi21.getTransportControls(mControllerObj);
+ return controlsObj != null ? new TransportControlsApi23(controlsObj) : null;
+ }
+ }
+
+ static class TransportControlsApi23 extends TransportControlsApi21 {
+
+ public TransportControlsApi23(Object controlsObj) {
+ super(controlsObj);
+ }
+
+ @Override
+ public void playFromUri(Uri uri, Bundle extras) {
+ MediaControllerCompatApi23.TransportControls.playFromUri(mControlsObj, uri,
+ extras);
+ }
+ }
}
diff -Nur support-v4-22.2.1/android/support/v4/media/session/MediaSessionCompat.java support-v4-23.0.0/android/support/v4/media/session/MediaSessionCompat.java
--- support-v4-22.2.1/android/support/v4/media/session/MediaSessionCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/media/session/MediaSessionCompat.java 2015-08-03 08:22:34.000000000 +0900
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -34,6 +35,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
+import android.support.annotation.IntDef;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.RatingCompat;
@@ -42,6 +44,8 @@
import android.util.Log;
import android.view.KeyEvent;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -79,6 +83,13 @@
mActiveListeners = new ArrayList<OnActiveChangeListener>();
/**
+ * @hide
+ */
+ @IntDef(flag=true, value={FLAG_HANDLES_MEDIA_BUTTONS, FLAG_HANDLES_TRANSPORT_CONTROLS})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SessionFlags {}
+
+ /**
* Set this flag on the session to indicate that it can handle media button
* events.
*/
@@ -191,7 +202,7 @@
*
* @param flags The flags to set for this session.
*/
- public void setFlags(int flags) {
+ public void setFlags(@SessionFlags int flags) {
mImpl.setFlags(flags);
}
@@ -370,7 +381,7 @@
* <li>{@link RatingCompat#RATING_THUMB_UP_DOWN}</li>
* </ul>
*/
- public void setRatingType(int type) {
+ public void setRatingType(@RatingCompat.Style int type) {
mImpl.setRatingType(type);
}
@@ -460,7 +471,9 @@
final Object mCallbackObj;
public Callback() {
- if (android.os.Build.VERSION.SDK_INT >= 21) {
+ if (android.os.Build.VERSION.SDK_INT >= 23) {
+ mCallbackObj = MediaSessionCompatApi23.createCallback(new StubApi23());
+ } else if (android.os.Build.VERSION.SDK_INT >= 21) {
mCallbackObj = MediaSessionCompatApi21.createCallback(new StubApi21());
} else {
mCallbackObj = null;
@@ -512,6 +525,12 @@
}
/**
+ * Override to handle requests to play a specific media item represented by a URI.
+ */
+ public void onPlayFromUri(Uri uri, Bundle extras) {
+ }
+
+ /**
* Override to handle requests to play an item with a given id from the
* play queue.
*/
@@ -659,6 +678,14 @@
Callback.this.onCustomAction(action, extras);
}
}
+
+ private class StubApi23 extends StubApi21 implements MediaSessionCompatApi23.Callback {
+
+ @Override
+ public void onPlayFromUri(Uri uri, Bundle extras) {
+ Callback.this.onPlayFromUri(uri, extras);
+ }
+ }
}
/**
@@ -908,7 +935,7 @@
interface MediaSessionImpl {
void setCallback(Callback callback, Handler handler);
- void setFlags(int flags);
+ void setFlags(@SessionFlags int flags);
void setPlaybackToLocal(int stream);
void setPlaybackToRemote(VolumeProviderCompat volumeProvider);
void setActive(boolean active);
@@ -925,7 +952,7 @@
void setQueue(List<QueueItem> queue);
void setQueueTitle(CharSequence title);
- void setRatingType(int type);
+ void setRatingType(@RatingCompat.Style int type);
void setExtras(Bundle extras);
Object getMediaSession();
@@ -955,14 +982,14 @@
private boolean mIsMbrRegistered = false;
private Callback mCallback;
- private int mFlags;
+ private @SessionFlags int mFlags;
private MediaMetadataCompat mMetadata;
private PlaybackStateCompat mState;
private PendingIntent mSessionActivity;
private List<QueueItem> mQueue;
private CharSequence mQueueTitle;
- private int mRatingType;
+ private @RatingCompat.Style int mRatingType;
private Bundle mExtras;
private int mVolumeType;
@@ -1100,7 +1127,7 @@
}
@Override
- public void setFlags(int flags) {
+ public void setFlags(@SessionFlags int flags) {
synchronized (mLock) {
mFlags = flags;
}
@@ -1263,7 +1290,7 @@
}
@Override
- public void setRatingType(int type) {
+ public void setRatingType(@RatingCompat.Style int type) {
mRatingType = type;
}
@@ -1308,6 +1335,10 @@
registeredRcc = true;
} else if (mIsRccRegistered
&& (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) == 0) {
+ // RCC keeps the state while the system resets its state internally when
+ // we register RCC. Reset the state so that the states in RCC and the system
+ // are in sync when we re-register the RCC.
+ MediaSessionCompatApi14.setState(mRccObj, PlaybackStateCompat.STATE_NONE);
MediaSessionCompatApi14.unregisterRemoteControlClient(mContext, mRccObj);
mIsRccRegistered = false;
}
@@ -1325,6 +1356,10 @@
mIsMbrRegistered = false;
}
if (mIsRccRegistered) {
+ // RCC keeps the state while the system resets its state internally when
+ // we register RCC. Reset the state so that the states in RCC and the system
+ // are in sync when we re-register the RCC.
+ MediaSessionCompatApi14.setState(mRccObj, PlaybackStateCompat.STATE_NONE);
MediaSessionCompatApi14.unregisterRemoteControlClient(mContext, mRccObj);
mIsRccRegistered = false;
}
@@ -1531,6 +1566,7 @@
}
@Override
+ @SessionFlags
public long getFlags() {
synchronized (mLock) {
return mFlags;
@@ -1587,6 +1623,11 @@
}
@Override
+ public void playFromUri(Uri uri, Bundle extras) throws RemoteException {
+ mHandler.post(MessageHandler.MSG_PLAY_URI, uri, extras);
+ }
+
+ @Override
public void skipToQueueItem(long id) {
mHandler.post(MessageHandler.MSG_SKIP_TO_ITEM, id);
}
@@ -1667,6 +1708,7 @@
}
@Override
+ @RatingCompat.Style
public int getRatingType() {
return mRatingType;
}
@@ -1708,6 +1750,11 @@
private static final int MSG_COMMAND = 15;
private static final int MSG_ADJUST_VOLUME = 16;
private static final int MSG_SET_VOLUME = 17;
+ private static final int MSG_PLAY_URI = 18;
+
+ // KeyEvent constants only available on API 11+
+ private static final int KEYCODE_MEDIA_PAUSE = 127;
+ private static final int KEYCODE_MEDIA_PLAY = 126;
public MessageHandler(Looper looper) {
super(looper);
@@ -1746,6 +1793,9 @@
case MSG_PLAY_SEARCH:
mCallback.onPlayFromSearch((String) msg.obj, msg.getData());
break;
+ case MSG_PLAY_URI:
+ mCallback.onPlayFromUri((Uri) msg.obj, msg.getData());
+ break;
case MSG_SKIP_TO_ITEM:
mCallback.onSkipToQueueItem((Long) msg.obj);
break;
@@ -1780,17 +1830,80 @@
KeyEvent keyEvent = (KeyEvent) msg.obj;
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- mCallback.onMediaButtonEvent(intent);
+ // Let the Callback handle events first before using the default behavior
+ if (!mCallback.onMediaButtonEvent(intent)) {
+ onMediaButtonEvent(keyEvent);
+ }
break;
case MSG_COMMAND:
Command cmd = (Command) msg.obj;
mCallback.onCommand(cmd.command, cmd.extras, cmd.stub);
break;
case MSG_ADJUST_VOLUME:
- adjustVolume((Integer) msg.obj, 0);
+ adjustVolume((int) msg.obj, 0);
break;
case MSG_SET_VOLUME:
- setVolumeTo((Integer) msg.obj, 0);
+ setVolumeTo((int) msg.obj, 0);
+ break;
+ }
+ }
+
+ private void onMediaButtonEvent(KeyEvent ke) {
+ if (ke == null || ke.getAction() != KeyEvent.ACTION_DOWN) {
+ return;
+ }
+ long validActions = mState == null ? 0 : mState.getActions();
+ switch (ke.getKeyCode()) {
+ // Note KeyEvent.KEYCODE_MEDIA_PLAY is API 11+
+ case KEYCODE_MEDIA_PLAY:
+ if ((validActions & PlaybackStateCompat.ACTION_PLAY) != 0) {
+ mCallback.onPlay();
+ }
+ break;
+ // Note KeyEvent.KEYCODE_MEDIA_PAUSE is API 11+
+ case KEYCODE_MEDIA_PAUSE:
+ if ((validActions & PlaybackStateCompat.ACTION_PAUSE) != 0) {
+ mCallback.onPause();
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_NEXT:
+ if ((validActions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
+ mCallback.onSkipToNext();
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+ if ((validActions & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) {
+ mCallback.onSkipToPrevious();
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_STOP:
+ if ((validActions & PlaybackStateCompat.ACTION_STOP) != 0) {
+ mCallback.onStop();
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+ if ((validActions & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0) {
+ mCallback.onFastForward();
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ if ((validActions & PlaybackStateCompat.ACTION_REWIND) != 0) {
+ mCallback.onRewind();
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ case KeyEvent.KEYCODE_HEADSETHOOK:
+ boolean isPlaying = mState != null
+ && mState.getState() == PlaybackStateCompat.STATE_PLAYING;
+ boolean canPlay = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE
+ | PlaybackStateCompat.ACTION_PLAY)) != 0;
+ boolean canPause = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE
+ | PlaybackStateCompat.ACTION_PAUSE)) != 0;
+ if (isPlaying && canPause) {
+ mCallback.onPause();
+ } else if (!isPlaying && canPlay) {
+ mCallback.onPlay();
+ }
break;
}
}
@@ -1819,7 +1932,7 @@
}
@Override
- public void setFlags(int flags) {
+ public void setFlags(@SessionFlags int flags) {
MediaSessionCompatApi21.setFlags(mSessionObj, flags);
}
@@ -1898,7 +2011,7 @@
}
@Override
- public void setRatingType(int type) {
+ public void setRatingType(@RatingCompat.Style int type) {
if (android.os.Build.VERSION.SDK_INT < 22) {
// TODO figure out 21 implementation
} else {
diff -Nur support-v4-22.2.1/android/support/v4/media/session/PlaybackStateCompat.java support-v4-23.0.0/android/support/v4/media/session/PlaybackStateCompat.java
--- support-v4-22.2.1/android/support/v4/media/session/PlaybackStateCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/media/session/PlaybackStateCompat.java 2015-07-30 21:03:08.000000000 +0900
@@ -21,9 +21,12 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
+import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.text.TextUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -35,6 +38,16 @@
public final class PlaybackStateCompat implements Parcelable {
/**
+ * @hide
+ */
+ @IntDef(flag=true, value={ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND,
+ ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING,
+ ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH,
+ ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Actions {}
+
+ /**
* Indicates this session supports the stop command.
*
* @see Builder#setActions(long)
@@ -124,6 +137,21 @@
* @see Builder#setActions(long)
*/
public static final long ACTION_SKIP_TO_QUEUE_ITEM = 1 << 12;
+ /**
+ * Indicates this session supports the play from URI command.
+ *
+ * @see Builder#setActions(long)
+ */
+ public static final long ACTION_PLAY_FROM_URI = 1 << 13;
+
+ /**
+ * @hide
+ */
+ @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING,
+ STATE_REWINDING, STATE_BUFFERING, STATE_ERROR, STATE_CONNECTING,
+ STATE_SKIPPING_TO_PREVIOUS, STATE_SKIPPING_TO_NEXT, STATE_SKIPPING_TO_QUEUE_ITEM})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {}
/**
* This is the default playback state and indicates that no media has been
@@ -320,6 +348,7 @@
* <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_NEXT}</li>
* <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_QUEUE_ITEM}</li>
*/
+ @State
public int getState() {
return mState;
}
@@ -368,8 +397,10 @@
* <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_MEDIA_ID}</li>
* <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_SEARCH}</li>
* <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_QUEUE_ITEM}</li>
+ * <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_URI}</li>
* </ul>
*/
+ @Actions
public long getActions() {
return mActions;
}
@@ -796,7 +827,7 @@
* @param playbackSpeed The current rate of playback as a multiple of
* normal playback.
*/
- public Builder setState(int state, long position, float playbackSpeed) {
+ public Builder setState(@State int state, long position, float playbackSpeed) {
return setState(state, position, playbackSpeed, SystemClock.elapsedRealtime());
}
@@ -834,7 +865,8 @@
* timebase that the position was updated at.
* @return this
*/
- public Builder setState(int state, long position, float playbackSpeed, long updateTime) {
+ public Builder setState(@State int state, long position, float playbackSpeed,
+ long updateTime) {
mState = state;
mPosition = position;
mUpdateTime = updateTime;
@@ -871,11 +903,12 @@
* <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_MEDIA_ID}</li>
* <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_SEARCH}</li>
* <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_QUEUE_ITEM}</li>
+ * <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_URI}</li>
* </ul>
*
* @return this
*/
- public Builder setActions(long capabilities) {
+ public Builder setActions(@Actions long capabilities) {
mActions = capabilities;
return this;
}
diff -Nur support-v4-22.2.1/android/support/v4/os/CancellationSignal.java support-v4-23.0.0/android/support/v4/os/CancellationSignal.java
--- support-v4-22.2.1/android/support/v4/os/CancellationSignal.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/os/CancellationSignal.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.os;
+
+import android.os.Build;
+
+/**
+ * Static library support version of the framework's {@link android.os.CancellationSignal}.
+ * Used to write apps that run on platforms prior to Android 4.1. See the framework SDK
+ * documentation for a class overview.
+ */
+public final class CancellationSignal {
+ private boolean mIsCanceled;
+ private OnCancelListener mOnCancelListener;
+ private Object mCancellationSignalObj;
+ private boolean mCancelInProgress;
+
+ /**
+ * Creates a cancellation signal, initially not canceled.
+ */
+ public CancellationSignal() {
+ }
+
+ /**
+ * Returns true if the operation has been canceled.
+ *
+ * @return True if the operation has been canceled.
+ */
+ public boolean isCanceled() {
+ synchronized (this) {
+ return mIsCanceled;
+ }
+ }
+
+ /**
+ * Throws {@link OperationCanceledException} if the operation has been canceled.
+ *
+ * @throws OperationCanceledException if the operation has been canceled.
+ */
+ public void throwIfCanceled() {
+ if (isCanceled()) {
+ throw new OperationCanceledException();
+ }
+ }
+
+ /**
+ * Cancels the operation and signals the cancellation listener.
+ * If the operation has not yet started, then it will be canceled as soon as it does.
+ */
+ public void cancel() {
+ final OnCancelListener listener;
+ final Object obj;
+ synchronized (this) {
+ if (mIsCanceled) {
+ return;
+ }
+ mIsCanceled = true;
+ mCancelInProgress = true;
+ listener = mOnCancelListener;
+ obj = mCancellationSignalObj;
+ }
+
+ try {
+ if (listener != null) {
+ listener.onCancel();
+ }
+ if (obj != null) {
+ CancellationSignalCompatJellybean.cancel(obj);
+ }
+ } finally {
+ synchronized (this) {
+ mCancelInProgress = false;
+ notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Sets the cancellation listener to be called when canceled.
+ *
+ * This method is intended to be used by the recipient of a cancellation signal
+ * such as a database or a content provider to handle cancellation requests
+ * while performing a long-running operation. This method is not intended to be
+ * used by applications themselves.
+ *
+ * If {@link CancellationSignal#cancel} has already been called, then the provided
+ * listener is invoked immediately.
+ *
+ * This method is guaranteed that the listener will not be called after it
+ * has been removed.
+ *
+ * @param listener The cancellation listener, or null to remove the current listener.
+ */
+ public void setOnCancelListener(OnCancelListener listener) {
+ synchronized (this) {
+ waitForCancelFinishedLocked();
+
+ if (mOnCancelListener == listener) {
+ return;
+ }
+ mOnCancelListener = listener;
+ if (!mIsCanceled || listener == null) {
+ return;
+ }
+ }
+ listener.onCancel();
+ }
+
+ /**
+ * Gets the framework {@link android.os.CancellationSignal} associated with this object.
+ * <p>
+ * Framework support for cancellation signals was added in
+ * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} so this method will always
+ * return null on older versions of the platform.
+ * </p>
+ *
+ * @return A framework cancellation signal object, or null on platform versions
+ * prior to Jellybean.
+ */
+ public Object getCancellationSignalObject() {
+ if (Build.VERSION.SDK_INT < 16) {
+ return null;
+ }
+ synchronized (this) {
+ if (mCancellationSignalObj == null) {
+ mCancellationSignalObj = CancellationSignalCompatJellybean.create();
+ if (mIsCanceled) {
+ CancellationSignalCompatJellybean.cancel(mCancellationSignalObj);
+ }
+ }
+ return mCancellationSignalObj;
+ }
+ }
+
+ private void waitForCancelFinishedLocked() {
+ while (mCancelInProgress) {
+ try {
+ wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+
+ /**
+ * Listens for cancellation.
+ */
+ public interface OnCancelListener {
+ /**
+ * Called when {@link CancellationSignal#cancel} is invoked.
+ */
+ void onCancel();
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/os/CancellationSignalCompatJellybean.java support-v4-23.0.0/android/support/v4/os/CancellationSignalCompatJellybean.java
--- support-v4-22.2.1/android/support/v4/os/CancellationSignalCompatJellybean.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/os/CancellationSignalCompatJellybean.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.os;
+
+class CancellationSignalCompatJellybean {
+ public static Object create() {
+ return new android.os.CancellationSignal();
+ }
+
+ public static void cancel(Object cancellationSignalObj) {
+ ((android.os.CancellationSignal)cancellationSignalObj).cancel();
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/os/OperationCanceledException.java support-v4-23.0.0/android/support/v4/os/OperationCanceledException.java
--- support-v4-22.2.1/android/support/v4/os/OperationCanceledException.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/os/OperationCanceledException.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.os;
+
+
+/**
+ * An exception type that is thrown when an operation in progress is canceled.
+ */
+public class OperationCanceledException extends RuntimeException {
+ public OperationCanceledException() {
+ this(null);
+ }
+
+ public OperationCanceledException(String message) {
+ super(message != null ? message : "The operation has been canceled.");
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/print/PrintHelperKitkat.java support-v4-23.0.0/android/support/v4/print/PrintHelperKitkat.java
--- support-v4-22.2.1/android/support/v4/print/PrintHelperKitkat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/print/PrintHelperKitkat.java 2015-06-25 19:03:06.000000000 +0900
@@ -18,8 +18,13 @@
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
+import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.pdf.PdfDocument.Page;
import android.net.Uri;
@@ -208,16 +213,20 @@
WriteResultCallback writeResultCallback) {
PrintedPdfDocument pdfDocument = new PrintedPdfDocument(mContext,
mAttributes);
+
+ Bitmap maybeGrayscale = convertBitmapForColorMode(bitmap,
+ mAttributes.getColorMode());
try {
Page page = pdfDocument.startPage(1);
RectF content = new RectF(page.getInfo().getContentRect());
- Matrix matrix = getMatrix(bitmap.getWidth(), bitmap.getHeight(),
+ Matrix matrix = getMatrix(
+ maybeGrayscale.getWidth(), maybeGrayscale.getHeight(),
content, fittingMode);
// Draw the bitmap.
- page.getCanvas().drawBitmap(bitmap, matrix, null);
+ page.getCanvas().drawBitmap(maybeGrayscale, matrix, null);
// Finish the page.
pdfDocument.finishPage(page);
@@ -245,6 +254,10 @@
/* ignore */
}
}
+ // If we created a new instance for grayscaling, then recycle it here.
+ if (maybeGrayscale != bitmap) {
+ maybeGrayscale.recycle();
+ }
}
}
@@ -401,6 +414,10 @@
if (callback != null) {
callback.onFinish();
}
+ if (mBitmap != null) {
+ mBitmap.recycle();
+ mBitmap = null;
+ }
}
@Override
@@ -409,6 +426,8 @@
WriteResultCallback writeResultCallback) {
PrintedPdfDocument pdfDocument = new PrintedPdfDocument(mContext,
mAttributes);
+ Bitmap maybeGrayscale = convertBitmapForColorMode(mBitmap,
+ mAttributes.getColorMode());
try {
Page page = pdfDocument.startPage(1);
@@ -419,7 +438,7 @@
content, fittingMode);
// Draw the bitmap.
- page.getCanvas().drawBitmap(mBitmap, matrix, null);
+ page.getCanvas().drawBitmap(maybeGrayscale, matrix, null);
// Finish the page.
pdfDocument.finishPage(page);
@@ -447,6 +466,10 @@
/* ignore */
}
}
+ // If we created a new instance for grayscaling, then recycle it here.
+ if (maybeGrayscale != mBitmap) {
+ maybeGrayscale.recycle();
+ }
}
}
};
@@ -541,4 +564,23 @@
}
}
}
+
+ private Bitmap convertBitmapForColorMode(Bitmap original, int colorMode) {
+ if (colorMode != COLOR_MODE_MONOCHROME) {
+ return original;
+ }
+ // Create a grayscale bitmap
+ Bitmap grayscale = Bitmap.createBitmap(original.getWidth(), original.getHeight(),
+ Config.ARGB_8888);
+ Canvas c = new Canvas(grayscale);
+ Paint p = new Paint();
+ ColorMatrix cm = new ColorMatrix();
+ cm.setSaturation(0);
+ ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
+ p.setColorFilter(f);
+ c.drawBitmap(original, 0, 0, p);
+ c.setBitmap(null);
+
+ return grayscale;
+ }
}
\ No newline at end of file
diff -Nur support-v4-22.2.1/android/support/v4/text/BidiFormatter.java support-v4-23.0.0/android/support/v4/text/BidiFormatter.java
--- support-v4-22.2.1/android/support/v4/text/BidiFormatter.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/text/BidiFormatter.java 2015-07-22 08:36:20.000000000 +0900
@@ -169,7 +169,7 @@
/**
* Specifies whether the BidiFormatter to be built should also "reset" directionality before
- * a string being bidi-wrapped, not just after it. The default is false.
+ * a string being bidi-wrapped, not just after it. The default is true.
*/
public Builder stereoReset(boolean stereoReset) {
if (stereoReset) {
@@ -355,12 +355,13 @@
* If {@code isolate}, directionally isolates the string so that it does not garble its
* surroundings. Currently, this is done by "resetting" the directionality after the string by
* appending a trailing Unicode bidi mark matching the context directionality (LRM or RLM) when
- * either the overall directionality or the exit directionality of the string is opposite to that
- * of the context. If the formatter was built using {@link Builder#stereoReset(boolean)} and
- * passing "true" as an argument, also prepends a Unicode bidi mark matching the context
- * directionality when either the overall directionality or the entry directionality of the
- * string is opposite to that of the context. Note that as opposed to the overall
- * directionality, the entry and exit directionalities are determined from the string itself.
+ * either the overall directionality or the exit directionality of the string is opposite to
+ * that of the context. Unless the formatter was built using
+ * {@link Builder#stereoReset(boolean)} with a {@code false} argument, also prepends a Unicode
+ * bidi mark matching the context directionality when either the overall directionality or the
+ * entry directionality of the string is opposite to that of the context. Note that as opposed
+ * to the overall directionality, the entry and exit directionalities are determined from the
+ * string itself.
* <p>
* Does *not* do HTML-escaping.
*
@@ -368,9 +369,11 @@
* @param heuristic The algorithm to be used to estimate the string's overall direction.
* @param isolate Whether to directionally isolate the string to prevent it from garbling the
* content around it
- * @return Input string after applying the above processing.
+ * @return Input string after applying the above processing. {@code null} if {@code str} is
+ * {@code null}.
*/
public String unicodeWrap(String str, TextDirectionHeuristicCompat heuristic, boolean isolate) {
+ if (str == null) return null;
final boolean isRtl = heuristic.isRtl(str, 0, str.length());
StringBuilder result = new StringBuilder();
if (getStereoReset() && isolate) {
diff -Nur support-v4-22.2.1/android/support/v4/text/ICUCompat.java support-v4-23.0.0/android/support/v4/text/ICUCompat.java
--- support-v4-22.2.1/android/support/v4/text/ICUCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/text/ICUCompat.java 2015-06-23 08:43:44.000000000 +0900
@@ -18,34 +18,32 @@
import android.os.Build;
+import java.util.Locale;
+
public class ICUCompat {
interface ICUCompatImpl {
- public String getScript(String locale);
- public String addLikelySubtags(String locale);
+ public String maximizeAndGetScript(Locale locale);
}
static class ICUCompatImplBase implements ICUCompatImpl {
@Override
- public String getScript(String locale) {
+ public String maximizeAndGetScript(Locale locale) {
return null;
}
-
- @Override
- public String addLikelySubtags(String locale) {
- return locale;
- }
}
static class ICUCompatImplIcs implements ICUCompatImpl {
@Override
- public String getScript(String locale) {
- return ICUCompatIcs.getScript(locale);
+ public String maximizeAndGetScript(Locale locale) {
+ return ICUCompatIcs.maximizeAndGetScript(locale);
}
+ }
+ static class ICUCompatImplLollipop implements ICUCompatImpl {
@Override
- public String addLikelySubtags(String locale) {
- return ICUCompatIcs.addLikelySubtags(locale);
+ public String maximizeAndGetScript(Locale locale) {
+ return ICUCompatApi23.maximizeAndGetScript(locale);
}
}
@@ -53,7 +51,9 @@
static {
final int version = Build.VERSION.SDK_INT;
- if (version >= 14) {
+ if (version >= 21) {
+ IMPL = new ICUCompatImplLollipop();
+ } else if (version >= 14) {
IMPL = new ICUCompatImplIcs();
} else {
IMPL = new ICUCompatImplBase();
@@ -61,18 +61,11 @@
}
/**
- * Returns the script (language code) of a script.
+ * Returns the script for a given Locale.
*
- * @param locale The locale.
- * @return a String representing the script (language code) of the locale.
- */
- public static String getScript(String locale) {
- return IMPL.getScript(locale);
- }
-
- /**
- * Add the likely subtags for a provided locale ID, per the algorithm described in the following
- * CLDR technical report:
+ * If the locale isn't already in its maximal form, likely subtags for the provided locale
+ * ID are added before we determine the script. For further details, see the following CLDR
+ * technical report :
*
* http://www.unicode.org/reports/tr35/#Likely_Subtags
*
@@ -87,12 +80,10 @@
* "sr" maximizes to "sr_Cyrl_RS"
* "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.)
* "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.)
-
- * @param locale The locale to maximize
*
- * @return the maximized locale
+ * @return
*/
- public static String addLikelySubtags(String locale) {
- return IMPL.addLikelySubtags(locale);
+ public static String maximizeAndGetScript(Locale locale) {
+ return IMPL.maximizeAndGetScript(locale);
}
}
diff -Nur support-v4-22.2.1/android/support/v4/text/ICUCompatApi23.java support-v4-23.0.0/android/support/v4/text/ICUCompatApi23.java
--- support-v4-22.2.1/android/support/v4/text/ICUCompatApi23.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/text/ICUCompatApi23.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,55 @@
+/*
+ * 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.text;
+
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Locale;
+
+public class ICUCompatApi23 {
+
+ private static final String TAG = "ICUCompatIcs";
+
+ private static Method sAddLikelySubtagsMethod;
+
+ static {
+ try {
+ // This class should always exist on API-23 since it's CTS tested.
+ final Class<?> clazz = Class.forName("libcore.icu.ICU");
+ sAddLikelySubtagsMethod = clazz.getMethod("addLikelySubtags",
+ new Class[]{ Locale.class });
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+
+ public static String maximizeAndGetScript(Locale locale) {
+ try {
+ final Object[] args = new Object[] { locale };
+ return ((Locale) sAddLikelySubtagsMethod.invoke(null, args)).getScript();
+ } catch (InvocationTargetException e) {
+ Log.w(TAG, e);
+ } catch (IllegalAccessException e) {
+ Log.w(TAG, e);
+ }
+
+ return locale.getScript();
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/text/ICUCompatIcs.java support-v4-23.0.0/android/support/v4/text/ICUCompatIcs.java
--- support-v4-22.2.1/android/support/v4/text/ICUCompatIcs.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/text/ICUCompatIcs.java 2015-06-23 08:43:44.000000000 +0900
@@ -20,6 +20,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.Locale;
class ICUCompatIcs {
@@ -38,15 +39,27 @@
new Class[]{ String.class });
}
} catch (Exception e) {
+ sGetScriptMethod = null;
+ sAddLikelySubtagsMethod = null;
+
// Nothing we can do here, we just log the exception
Log.w(TAG, e);
}
}
- public static String getScript(String locale) {
+ public static String maximizeAndGetScript(Locale locale) {
+ final String localeWithSubtags = addLikelySubtags(locale);
+ if (localeWithSubtags != null) {
+ return getScript(localeWithSubtags);
+ }
+
+ return null;
+ }
+
+ private static String getScript(String localeStr) {
try {
if (sGetScriptMethod != null) {
- final Object[] args = new Object[] { locale };
+ final Object[] args = new Object[] { localeStr };
return (String) sGetScriptMethod.invoke(null, args);
}
} catch (IllegalAccessException e) {
@@ -60,10 +73,11 @@
return null;
}
- public static String addLikelySubtags(String locale) {
+ private static String addLikelySubtags(Locale locale) {
+ final String localeStr = locale.toString();
try {
if (sAddLikelySubtagsMethod != null) {
- final Object[] args = new Object[] { locale };
+ final Object[] args = new Object[] { localeStr };
return (String) sAddLikelySubtagsMethod.invoke(null, args);
}
} catch (IllegalAccessException e) {
@@ -74,6 +88,7 @@
// Nothing we can do here, we just log the exception
Log.w(TAG, e);
}
- return locale;
+
+ return localeStr;
}
}
diff -Nur support-v4-22.2.1/android/support/v4/text/TextUtilsCompat.java support-v4-23.0.0/android/support/v4/text/TextUtilsCompat.java
--- support-v4-22.2.1/android/support/v4/text/TextUtilsCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/text/TextUtilsCompat.java 2015-07-15 08:50:04.000000000 +0900
@@ -16,6 +16,7 @@
package android.support.v4.text;
+import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
@@ -23,6 +24,100 @@
import java.util.Locale;
public class TextUtilsCompat {
+ private static class TextUtilsCompatImpl {
+ @NonNull
+ public String htmlEncode(@NonNull String s) {
+ StringBuilder sb = new StringBuilder();
+ char c;
+ for (int i = 0; i < s.length(); i++) {
+ c = s.charAt(i);
+ switch (c) {
+ case '<':
+ sb.append("&lt;"); //$NON-NLS-1$
+ break;
+ case '>':
+ sb.append("&gt;"); //$NON-NLS-1$
+ break;
+ case '&':
+ sb.append("&amp;"); //$NON-NLS-1$
+ break;
+ case '\'':
+ //http://www.w3.org/TR/xhtml1
+ // The named character reference &apos; (the apostrophe, U+0027) was
+ // introduced in XML 1.0 but does not appear in HTML. Authors should
+ // therefore use &#39; instead of &apos; to work as expected in HTML 4
+ // user agents.
+ sb.append("&#39;"); //$NON-NLS-1$
+ break;
+ case '"':
+ sb.append("&quot;"); //$NON-NLS-1$
+ break;
+ default:
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ public int getLayoutDirectionFromLocale(@Nullable Locale locale) {
+ if (locale != null && !locale.equals(ROOT)) {
+ final String scriptSubtag = ICUCompat.maximizeAndGetScript(locale);
+ if (scriptSubtag == null) return getLayoutDirectionFromFirstChar(locale);
+
+ if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) ||
+ scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) {
+ return ViewCompat.LAYOUT_DIRECTION_RTL;
+ }
+ }
+ return ViewCompat.LAYOUT_DIRECTION_LTR;
+ }
+
+ /**
+ * Fallback algorithm to detect the locale direction. Rely on the first char of the
+ * localized locale name. This will not work if the localized locale name is in English
+ * (this is the case for ICU 4.4 and "Urdu" script)
+ *
+ * @param locale
+ * @return the layout direction. This may be one of:
+ * {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
+ * {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
+ *
+ * Be careful: this code will need to be updated when vertical scripts will be supported
+ */
+ private static int getLayoutDirectionFromFirstChar(@NonNull Locale locale) {
+ switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
+ return ViewCompat.LAYOUT_DIRECTION_RTL;
+
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
+ default:
+ return ViewCompat.LAYOUT_DIRECTION_LTR;
+ }
+ }
+ }
+
+ private static class TextUtilsCompatJellybeanMr1Impl extends TextUtilsCompatImpl {
+ @NonNull
+ public String htmlEncode(@NonNull String s) {
+ return TextUtilsCompatJellybeanMr1.htmlEncode(s);
+ }
+
+ @Override
+ public int getLayoutDirectionFromLocale(@Nullable Locale locale) {
+ return TextUtilsCompatJellybeanMr1.getLayoutDirectionFromLocale(locale);
+ }
+ }
+
+ private static final TextUtilsCompatImpl IMPL;
+ static {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 17) { // JellyBean MR1
+ IMPL = new TextUtilsCompatJellybeanMr1Impl();
+ } else {
+ IMPL = new TextUtilsCompatImpl();
+ }
+ }
/**
* Html-encode the string.
@@ -31,35 +126,7 @@
*/
@NonNull
public static String htmlEncode(@NonNull String s) {
- StringBuilder sb = new StringBuilder();
- char c;
- for (int i = 0; i < s.length(); i++) {
- c = s.charAt(i);
- switch (c) {
- case '<':
- sb.append("&lt;"); //$NON-NLS-1$
- break;
- case '>':
- sb.append("&gt;"); //$NON-NLS-1$
- break;
- case '&':
- sb.append("&amp;"); //$NON-NLS-1$
- break;
- case '\'':
- //http://www.w3.org/TR/xhtml1
- // The named character reference &apos; (the apostrophe, U+0027) was introduced in
- // XML 1.0 but does not appear in HTML. Authors should therefore use &#39; instead
- // of &apos; to work as expected in HTML 4 user agents.
- sb.append("&#39;"); //$NON-NLS-1$
- break;
- case '"':
- sb.append("&quot;"); //$NON-NLS-1$
- break;
- default:
- sb.append(c);
- }
- }
- return sb.toString();
+ return IMPL.htmlEncode(s);
}
/**
@@ -73,42 +140,7 @@
* Be careful: this code will need to be updated when vertical scripts will be supported
*/
public static int getLayoutDirectionFromLocale(@Nullable Locale locale) {
- if (locale != null && !locale.equals(ROOT)) {
- final String scriptSubtag = ICUCompat.getScript(
- ICUCompat.addLikelySubtags(locale.toString()));
- if (scriptSubtag == null) return getLayoutDirectionFromFirstChar(locale);
-
- if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) ||
- scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) {
- return ViewCompat.LAYOUT_DIRECTION_RTL;
- }
- }
-
- return ViewCompat.LAYOUT_DIRECTION_LTR;
- }
-
- /**
- * Fallback algorithm to detect the locale direction. Rely on the fist char of the
- * localized locale name. This will not work if the localized locale name is in English
- * (this is the case for ICU 4.4 and "Urdu" script)
- *
- * @param locale
- * @return the layout direction. This may be one of:
- * {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
- * {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
- *
- * Be careful: this code will need to be updated when vertical scripts will be supported
- */
- private static int getLayoutDirectionFromFirstChar(Locale locale) {
- switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
- case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
- case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
- return ViewCompat.LAYOUT_DIRECTION_RTL;
-
- case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
- default:
- return ViewCompat.LAYOUT_DIRECTION_LTR;
- }
+ return IMPL.getLayoutDirectionFromLocale(locale);
}
public static final Locale ROOT = new Locale("", "");
diff -Nur support-v4-22.2.1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java support-v4-23.0.0/android/support/v4/text/TextUtilsCompatJellybeanMr1.java
--- support-v4-22.2.1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/text/TextUtilsCompatJellybeanMr1.java 2015-07-15 08:50:04.000000000 +0900
@@ -0,0 +1,37 @@
+/*
+ * 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.text;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+
+import java.util.Locale;
+
+/**
+ * Jellybean MR1 - specific TextUtils API access.
+ */
+public class TextUtilsCompatJellybeanMr1 {
+ @NonNull
+ public static String htmlEncode(@NonNull String s) {
+ return TextUtils.htmlEncode(s);
+ }
+
+ public static int getLayoutDirectionFromLocale(@Nullable Locale locale) {
+ return TextUtils.getLayoutDirectionFromLocale(locale);
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/util/CircularArray.java support-v4-23.0.0/android/support/v4/util/CircularArray.java
--- support-v4-22.2.1/android/support/v4/util/CircularArray.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/util/CircularArray.java 2015-06-23 08:43:44.000000000 +0900
@@ -93,7 +93,7 @@
/**
* Remove first element from front of the CircularArray and return it.
* @return The element removed.
- * @throws {@link ArrayIndexOutOfBoundsException} if CircularArray is empty.
+ * @throws ArrayIndexOutOfBoundsException if CircularArray is empty.
*/
public E popFirst() {
if (mHead == mTail) {
@@ -108,7 +108,7 @@
/**
* Remove last element from end of the CircularArray and return it.
* @return The element removed.
- * @throws {@link ArrayIndexOutOfBoundsException} if CircularArray is empty.
+ * @throws ArrayIndexOutOfBoundsException if CircularArray is empty.
*/
public E popLast() {
if (mHead == mTail) {
@@ -132,7 +132,7 @@
* Remove multiple elements from front of the CircularArray, ignore when numOfElements
* is less than or equals to 0.
* @param numOfElements Number of elements to remove.
- * @throws {@link ArrayIndexOutOfBoundsException} if numOfElements is larger than
+ * @throws ArrayIndexOutOfBoundsException if numOfElements is larger than
* {@link #size()}
*/
public void removeFromStart(int numOfElements) {
@@ -165,7 +165,7 @@
* Remove multiple elements from end of the CircularArray, ignore when numOfElements
* is less than or equals to 0.
* @param numOfElements Number of elements to remove.
- * @throws {@link ArrayIndexOutOfBoundsException} if numOfElements is larger than
+ * @throws ArrayIndexOutOfBoundsException if numOfElements is larger than
* {@link #size()}
*/
public void removeFromEnd(int numOfElements) {
diff -Nur support-v4-22.2.1/android/support/v4/util/CircularIntArray.java support-v4-23.0.0/android/support/v4/util/CircularIntArray.java
--- support-v4-22.2.1/android/support/v4/util/CircularIntArray.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/util/CircularIntArray.java 2015-06-23 08:43:44.000000000 +0900
@@ -94,7 +94,7 @@
/**
* Remove first integer from front of the CircularIntArray and return it.
* @return The integer removed.
- * @throws {@link ArrayIndexOutOfBoundsException} if CircularIntArray is empty.
+ * @throws ArrayIndexOutOfBoundsException if CircularIntArray is empty.
*/
public int popFirst() {
if (mHead == mTail) throw new ArrayIndexOutOfBoundsException();
@@ -106,7 +106,7 @@
/**
* Remove last integer from end of the CircularIntArray and return it.
* @return The integer removed.
- * @throws {@link ArrayIndexOutOfBoundsException} if CircularIntArray is empty.
+ * @throws ArrayIndexOutOfBoundsException if CircularIntArray is empty.
*/
public int popLast() {
if (mHead == mTail) throw new ArrayIndexOutOfBoundsException();
@@ -127,7 +127,7 @@
* Remove multiple integers from front of the CircularIntArray, ignore when numOfElements
* is less than or equals to 0.
* @param numOfElements Number of integers to remove.
- * @throws {@link ArrayIndexOutOfBoundsException} if numOfElements is larger than
+ * @throws ArrayIndexOutOfBoundsException if numOfElements is larger than
* {@link #size()}
*/
public void removeFromStart(int numOfElements) {
@@ -144,7 +144,7 @@
* Remove multiple elements from end of the CircularIntArray, ignore when numOfElements
* is less than or equals to 0.
* @param numOfElements Number of integers to remove.
- * @throws {@link ArrayIndexOutOfBoundsException} if numOfElements is larger than
+ * @throws ArrayIndexOutOfBoundsException if numOfElements is larger than
* {@link #size()}
*/
public void removeFromEnd(int numOfElements) {
diff -Nur support-v4-22.2.1/android/support/v4/view/ActionProvider.java support-v4-23.0.0/android/support/v4/view/ActionProvider.java
--- support-v4-22.2.1/android/support/v4/view/ActionProvider.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/view/ActionProvider.java 2015-07-02 08:14:40.000000000 +0900
@@ -242,6 +242,14 @@
}
/**
+ * @hide
+ */
+ public void reset() {
+ mVisibilityListener = null;
+ mSubUiVisibilityListener = null;
+ }
+
+ /**
* @hide Internal use only
*/
public interface SubUiVisibilityListener {
diff -Nur support-v4-22.2.1/android/support/v4/view/PagerTabStrip.java support-v4-23.0.0/android/support/v4/view/PagerTabStrip.java
--- support-v4-22.2.1/android/support/v4/view/PagerTabStrip.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/view/PagerTabStrip.java 2015-06-23 08:43:44.000000000 +0900
@@ -21,6 +21,7 @@
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.util.AttributeSet;
@@ -127,7 +128,7 @@
*
* @param color Color to set as an 0xRRGGBB value. The high byte (alpha) is ignored.
*/
- public void setTabIndicatorColor(int color) {
+ public void setTabIndicatorColor(@ColorInt int color) {
mIndicatorColor = color;
mTabPaint.setColor(mIndicatorColor);
invalidate();
@@ -145,6 +146,7 @@
/**
* @return The current tab indicator color as an 0xRRGGBB value.
*/
+ @ColorInt
public int getTabIndicatorColor() {
return mIndicatorColor;
}
@@ -174,7 +176,7 @@
}
@Override
- public void setBackgroundColor(int color) {
+ public void setBackgroundColor(@ColorInt int color) {
super.setBackgroundColor(color);
if (!mDrawFullUnderlineSet) {
mDrawFullUnderline = (color & 0xFF000000) == 0;
diff -Nur support-v4-22.2.1/android/support/v4/view/PagerTitleStrip.java support-v4-23.0.0/android/support/v4/view/PagerTitleStrip.java
--- support-v4-22.2.1/android/support/v4/view/PagerTitleStrip.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/view/PagerTitleStrip.java 2015-07-02 08:14:40.000000000 +0900
@@ -20,6 +20,8 @@
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
+import android.support.annotation.FloatRange;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.TypedValue;
@@ -189,7 +191,7 @@
*
* @param alpha Opacity value in the range 0-1f
*/
- public void setNonPrimaryAlpha(float alpha) {
+ public void setNonPrimaryAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {
mNonPrimaryAlpha = (int) (alpha * 255) & 0xFF;
final int transparentColor = (mNonPrimaryAlpha << 24) | (mTextColor & 0xFFFFFF);
mPrevText.setTextColor(transparentColor);
@@ -202,7 +204,7 @@
*
* @param color Color hex code in 0xAARRGGBB format
*/
- public void setTextColor(int color) {
+ public void setTextColor(@ColorInt int color) {
mTextColor = color;
mCurrText.setTextColor(color);
final int transparentColor = (mNonPrimaryAlpha << 24) | (mTextColor & 0xFFFFFF);
@@ -288,10 +290,11 @@
// Measure everything
final int width = getWidth() - getPaddingLeft() - getPaddingRight();
+ final int maxWidth = Math.max(0, (int) (width * 0.8f));
+ final int childWidthSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST);
final int childHeight = getHeight() - getPaddingTop() - getPaddingBottom();
- final int childWidthSpec = MeasureSpec.makeMeasureSpec((int) (width * 0.8f),
- MeasureSpec.AT_MOST);
- final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST);
+ final int maxHeight = Math.max(0, childHeight);
+ final int childHeightSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
mPrevText.measure(childWidthSpec, childHeightSpec);
mCurrText.measure(childWidthSpec, childHeightSpec);
mNextText.measure(childWidthSpec, childHeightSpec);
@@ -434,9 +437,10 @@
padding = getPaddingTop() + getPaddingBottom();
childHeight -= padding;
- final int childWidthSpec = MeasureSpec.makeMeasureSpec((int) (widthSize * 0.8f),
- MeasureSpec.AT_MOST);
- final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST);
+ final int maxWidth = Math.max(0, (int) (widthSize * 0.8f));
+ final int childWidthSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST);
+ final int maxHeight = Math.min(0, childHeight);
+ final int childHeightSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
mPrevText.measure(childWidthSpec, childHeightSpec);
mCurrText.measure(childWidthSpec, childHeightSpec);
diff -Nur support-v4-22.2.1/android/support/v4/view/ViewCompat.java support-v4-23.0.0/android/support/v4/view/ViewCompat.java
--- support-v4-22.2.1/android/support/v4/view/ViewCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/view/ViewCompat.java 2015-06-23 08:43:44.000000000 +0900
@@ -24,6 +24,7 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
+import android.support.annotation.FloatRange;
import android.support.annotation.IdRes;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
@@ -347,12 +348,15 @@
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);
void setFitsSystemWindows(View view, boolean fitSystemWindows);
void jumpDrawablesToCurrentState(View v);
void setOnApplyWindowInsetsListener(View view, OnApplyWindowInsetsListener listener);
@@ -579,6 +583,11 @@
}
}
+ @Override
+ public boolean hasOverlappingRendering(View view) {
+ return true;
+ }
+
private void bindTempDetach() {
try {
mDispatchStartTemporaryDetach = View.class.getDeclaredMethod(
@@ -758,6 +767,15 @@
}
@Override
+ public void setClipBounds(View view, Rect clipBounds) {
+ }
+
+ @Override
+ public Rect getClipBounds(View view) {
+ return null;
+ }
+
+ @Override
public void setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled) {
// noop
}
@@ -1280,6 +1298,11 @@
public boolean getFitsSystemWindows(View view) {
return ViewCompatJB.getFitsSystemWindows(view);
}
+
+ @Override
+ public boolean hasOverlappingRendering(View view) {
+ return ViewCompatJB.hasOverlappingRendering(view);
+ }
}
static class JbMr1ViewCompatImpl extends JBViewCompatImpl {
@@ -1335,7 +1358,19 @@
}
}
- static class KitKatViewCompatImpl extends JbMr1ViewCompatImpl {
+ static class JbMr2ViewCompatImpl extends JbMr1ViewCompatImpl {
+ @Override
+ public void setClipBounds(View view, Rect clipBounds) {
+ ViewCompatJellybeanMr2.setClipBounds(view, clipBounds);
+ }
+
+ @Override
+ public Rect getClipBounds(View view) {
+ return ViewCompatJellybeanMr2.getClipBounds(view);
+ }
+ }
+
+ static class KitKatViewCompatImpl extends JbMr2ViewCompatImpl {
@Override
public int getAccessibilityLiveRegion(View view) {
return ViewCompatKitKat.getAccessibilityLiveRegion(view);
@@ -2334,7 +2369,7 @@
*
* @param value The opacity of the view.
*/
- public static void setAlpha(View view, float value) {
+ public static void setAlpha(View view, @FloatRange(from=0.0, to=1.0) float value) {
IMPL.setAlpha(view, value);
}
@@ -2690,6 +2725,24 @@
}
/**
+ * Returns whether this View has content which overlaps.
+ *
+ * <p>This function, intended to be overridden by specific View types, is an optimization when
+ * alpha is set on a view. If rendering overlaps in a view with alpha < 1, that view is drawn to
+ * an offscreen buffer and then composited into place, which can be expensive. If the view has
+ * no overlapping rendering, the view can draw each primitive with the appropriate alpha value
+ * directly. An example of overlapping rendering is a TextView with a background image, such as
+ * a Button. An example of non-overlapping rendering is a TextView with no background, or an
+ * ImageView with only the foreground image. The default implementation returns true; subclasses
+ * should override if they have cases which can be optimized.</p>
+ *
+ * @return true if the content in this view might overlap, false otherwise.
+ */
+ public static boolean hasOverlappingRendering(View view) {
+ return IMPL.hasOverlappingRendering(view);
+ }
+
+ /**
* Return if the padding as been set through relative values
* {@code View.setPaddingRelative(int, int, int, int)} or thru
*
@@ -3000,6 +3053,33 @@
}
/**
+ * Sets a rectangular area on this view to which the view will be clipped
+ * when it is drawn. Setting the value to null will remove the clip bounds
+ * and the view will draw normally, using its full bounds.
+ *
+ * <p>Prior to API 18 this does nothing.</p>
+ *
+ * @param view The view to set clipBounds.
+ * @param clipBounds The rectangular area, in the local coordinates of
+ * this view, to which future drawing operations will be clipped.
+ */
+ public static void setClipBounds(View view, Rect clipBounds) {
+ IMPL.setClipBounds(view, clipBounds);
+ }
+
+ /**
+ * Returns a copy of the current {@link #setClipBounds(View, Rect)}.
+ *
+ * <p>Prior to API 18 this will return null.</p>
+ *
+ * @return A copy of the current clip bounds if clip bounds are set,
+ * otherwise null.
+ */
+ public static Rect getClipBounds(View view) {
+ return IMPL.getClipBounds(view);
+ }
+
+ /**
* Returns true if the provided view is currently attached to a window.
*/
public static boolean isAttachedToWindow(View view) {
diff -Nur support-v4-22.2.1/android/support/v4/view/ViewCompatJB.java support-v4-23.0.0/android/support/v4/view/ViewCompatJB.java
--- support-v4-22.2.1/android/support/v4/view/ViewCompatJB.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/view/ViewCompatJB.java 2015-06-23 08:43:44.000000000 +0900
@@ -85,4 +85,8 @@
public static boolean getFitsSystemWindows(View view) {
return view.getFitsSystemWindows();
}
+
+ public static boolean hasOverlappingRendering(View view) {
+ return view.hasOverlappingRendering();
+ }
}
diff -Nur support-v4-22.2.1/android/support/v4/view/ViewCompatJellybeanMr2.java support-v4-23.0.0/android/support/v4/view/ViewCompatJellybeanMr2.java
--- support-v4-22.2.1/android/support/v4/view/ViewCompatJellybeanMr2.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/view/ViewCompatJellybeanMr2.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,35 @@
+/*
+ * 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.graphics.Rect;
+
+/**
+ * Jellybean MR2 - specific View API access.
+ */
+class ViewCompatJellybeanMr2 {
+
+ public static Rect getClipBounds(View view) {
+ return view.getClipBounds();
+ }
+
+ public static void setClipBounds(View view, Rect clipBounds) {
+ view.setClipBounds(clipBounds);
+ }
+
+}
diff -Nur support-v4-22.2.1/android/support/v4/view/ViewPager.java support-v4-23.0.0/android/support/v4/view/ViewPager.java
--- support-v4-22.2.1/android/support/v4/view/ViewPager.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/view/ViewPager.java 2015-06-23 08:43:44.000000000 +0900
@@ -28,6 +28,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
+import android.support.annotation.CallSuper;
import android.support.annotation.DrawableRes;
import android.support.v4.os.ParcelableCompat;
import android.support.v4.os.ParcelableCompatCreatorCallbacks;
@@ -1704,6 +1705,7 @@
* @param offset Value from [0, 1) indicating the offset from the page at position.
* @param offsetPixels Value in pixels indicating the offset from position.
*/
+ @CallSuper
protected void onPageScrolled(int position, float offset, int offsetPixels) {
// Offset any decor views if needed - keep them on-screen at all times.
if (mDecorChildCount > 0) {
diff -Nur support-v4-22.2.1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java support-v4-23.0.0/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
--- support-v4-22.2.1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java 2015-08-03 08:22:34.000000000 +0900
@@ -21,6 +21,7 @@
import android.os.Bundle;
import android.support.v4.accessibilityservice.AccessibilityServiceInfoCompat;
import android.support.v4.view.ViewCompat;
+import android.text.InputType;
import android.view.View;
import java.util.ArrayList;
@@ -34,6 +35,304 @@
public class AccessibilityNodeInfoCompat {
public static class AccessibilityActionCompat {
+
+ /**
+ * Action that gives input focus to the node.
+ */
+ public static final AccessibilityActionCompat ACTION_FOCUS =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_FOCUS, null);
+
+ /**
+ * Action that clears input focus of the node.
+ */
+ public static final AccessibilityActionCompat ACTION_CLEAR_FOCUS =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS, null);
+
+ /**
+ * Action that selects the node.
+ */
+ public static final AccessibilityActionCompat ACTION_SELECT =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_SELECT, null);
+
+ /**
+ * Action that deselects the node.
+ */
+ public static final AccessibilityActionCompat ACTION_CLEAR_SELECTION =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION, null);
+
+ /**
+ * Action that clicks on the node info.
+ */
+ public static final AccessibilityActionCompat ACTION_CLICK =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_CLICK, null);
+
+ /**
+ * Action that long clicks on the node.
+ */
+ public static final AccessibilityActionCompat ACTION_LONG_CLICK =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_LONG_CLICK, null);
+
+ /**
+ * Action that gives accessibility focus to the node.
+ */
+ public static final AccessibilityActionCompat ACTION_ACCESSIBILITY_FOCUS =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null);
+
+ /**
+ * Action that clears accessibility focus of the node.
+ */
+ public static final AccessibilityActionCompat ACTION_CLEAR_ACCESSIBILITY_FOCUS =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
+
+ /**
+ * Action that requests to go to the next entity in this node's text
+ * at a given movement granularity. For example, move to the next character,
+ * word, etc.
+ * <p>
+ * <strong>Arguments:</strong>
+ * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
+ * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
+ * <strong>Example:</strong> Move to the previous character and do not extend selection.
+ * <code><pre><p>
+ * Bundle arguments = new Bundle();
+ * arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
+ * AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER);
+ * arguments.putBoolean(
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
+ * info.performAction(
+ * AccessibilityActionCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY.getId(),
+ * arguments);
+ * </code></pre></p>
+ * </p>
+ *
+ * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
+ * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
+ *
+ * @see AccessibilityNodeInfoCompat#setMovementGranularities(int)
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
+ * @see AccessibilityNodeInfoCompat#getMovementGranularities()
+ * AccessibilityNodeInfoCompat.getMovementGranularities()
+ *
+ * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_CHARACTER
+ * AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER
+ * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_WORD
+ * AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_WORD
+ * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_LINE
+ * AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_LINE
+ * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PARAGRAPH
+ * AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PARAGRAPH
+ * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PAGE
+ * AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PAGE
+ */
+ public static final AccessibilityActionCompat ACTION_NEXT_AT_MOVEMENT_GRANULARITY =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, null);
+
+ /**
+ * Action that requests to go to the previous entity in this node's text
+ * at a given movement granularity. For example, move to the next character,
+ * word, etc.
+ * <p>
+ * <strong>Arguments:</strong>
+ * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
+ * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
+ * <strong>Example:</strong> Move to the next character and do not extend selection.
+ * <code><pre><p>
+ * Bundle arguments = new Bundle();
+ * arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
+ * AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER);
+ * arguments.putBoolean(
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
+ * info.performAction(
+ * AccessibilityActionCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY.getId(),
+ * arguments);
+ * </code></pre></p>
+ * </p>
+ *
+ * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
+ * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
+ *
+ * @see AccessibilityNodeInfoCompat#setMovementGranularities(int)
+ * AccessibilityNodeInfoCompat.setMovementGranularities(int)
+ * @see AccessibilityNodeInfoCompat#getMovementGranularities()
+ * AccessibilityNodeInfoCompat.getMovementGranularities()
+ *
+ * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_CHARACTER
+ * AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER
+ * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_WORD
+ * AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_WORD
+ * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_LINE
+ * AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_LINE
+ * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PARAGRAPH
+ * AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PARAGRAPH
+ * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PAGE
+ * AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PAGE
+ */
+ public static final AccessibilityActionCompat ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, null);
+
+ /**
+ * Action to move to the next HTML element of a given type. For example, move
+ * to the BUTTON, INPUT, TABLE, etc.
+ * <p>
+ * <strong>Arguments:</strong>
+ * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_HTML_ELEMENT_STRING
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
+ * <strong>Example:</strong>
+ * <code><pre><p>
+ * Bundle arguments = new Bundle();
+ * arguments.putString(
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
+ * info.performAction(
+ * AccessibilityActionCompat.ACTION_NEXT_HTML_ELEMENT.getId(), arguments);
+ * </code></pre></p>
+ * </p>
+ */
+ public static final AccessibilityActionCompat ACTION_NEXT_HTML_ELEMENT =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_NEXT_HTML_ELEMENT, null);
+
+ /**
+ * Action to move to the previous HTML element of a given type. For example, move
+ * to the BUTTON, INPUT, TABLE, etc.
+ * <p>
+ * <strong>Arguments:</strong>
+ * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_HTML_ELEMENT_STRING
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
+ * <strong>Example:</strong>
+ * <code><pre><p>
+ * Bundle arguments = new Bundle();
+ * arguments.putString(
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
+ * info.performAction(
+ * AccessibilityActionCompat.ACTION_PREVIOUS_HTML_ELEMENT.getId(), arguments);
+ * </code></pre></p>
+ * </p>
+ */
+ public static final AccessibilityActionCompat ACTION_PREVIOUS_HTML_ELEMENT =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_PREVIOUS_HTML_ELEMENT, null);
+
+ /**
+ * Action to scroll the node content forward.
+ */
+ public static final AccessibilityActionCompat ACTION_SCROLL_FORWARD =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, null);
+
+ /**
+ * Action to scroll the node content backward.
+ */
+ public static final AccessibilityActionCompat ACTION_SCROLL_BACKWARD =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, null);
+
+ /**
+ * Action to copy the current selection to the clipboard.
+ */
+ public static final AccessibilityActionCompat ACTION_COPY =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_COPY, null);
+
+ /**
+ * Action to paste the current clipboard content.
+ */
+ public static final AccessibilityActionCompat ACTION_PASTE =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_PASTE, null);
+
+ /**
+ * Action to cut the current selection and place it to the clipboard.
+ */
+ public static final AccessibilityActionCompat ACTION_CUT =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_CUT, null);
+
+ /**
+ * Action to set the selection. Performing this action with no arguments
+ * clears the selection.
+ * <p>
+ * <strong>Arguments:</strong>
+ * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_START_INT
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT},
+ * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_END_INT
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT}<br>
+ * <strong>Example:</strong>
+ * <code><pre><p>
+ * Bundle arguments = new Bundle();
+ * arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT, 1);
+ * arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT, 2);
+ * info.performAction(AccessibilityActionCompat.ACTION_SET_SELECTION.getId(), arguments);
+ * </code></pre></p>
+ * </p>
+ *
+ * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_START_INT
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT
+ * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_END_INT
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT
+ */
+ public static final AccessibilityActionCompat ACTION_SET_SELECTION =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_SET_SELECTION, null);
+
+ /**
+ * Action to expand an expandable node.
+ */
+ public static final AccessibilityActionCompat ACTION_EXPAND =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_EXPAND, null);
+
+ /**
+ * Action to collapse an expandable node.
+ */
+ public static final AccessibilityActionCompat ACTION_COLLAPSE =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_COLLAPSE, null);
+
+ /**
+ * Action to dismiss a dismissable node.
+ */
+ public static final AccessibilityActionCompat ACTION_DISMISS =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_DISMISS, null);
+
+ /**
+ * Action that sets the text of the node. Performing the action without argument,
+ * using <code> null</code> or empty {@link CharSequence} will clear the text. This
+ * action will also put the cursor at the end of text.
+ * <p>
+ * <strong>Arguments:</strong>
+ * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE
+ * AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
+ * <strong>Example:</strong>
+ * <code><pre><p>
+ * Bundle arguments = new Bundle();
+ * arguments.putCharSequence(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
+ * "android");
+ * info.performAction(AccessibilityActionCompat.ACTION_SET_TEXT.getId(), arguments);
+ * </code></pre></p>
+ */
+ public static final AccessibilityActionCompat ACTION_SET_TEXT =
+ new AccessibilityActionCompat(
+ AccessibilityNodeInfoCompat.ACTION_SET_TEXT, null);
+
private final Object mAction;
/**
@@ -200,9 +499,12 @@
public Object getChild(Object info, int index);
public void addChild(Object info, View child);
public void addChild(Object info, View child, int virtualDescendantId);
+ public boolean removeChild(Object info, View child);
+ public boolean removeChild(Object info, View root, int virtualDescendantId);
public int getActions(Object info);
public void addAction(Object info, int action);
public void addAction(Object info, Object action);
+ public boolean removeAction(Object info, Object action);
public int getAccessibilityActionId(Object action);
public CharSequence getAccessibilityActionLabel(Object action);
public boolean performAction(Object info, int action);
@@ -259,6 +561,7 @@
public Object getCollectionItemInfo(Object info);
public void setCollectionItemInfo(Object info, Object collectionItemInfo);
public Object getRangeInfo(Object info);
+ public void setRangeInfo(Object info, Object rangeInfo);
public List<Object> getActionList(Object info);
public Object obtainCollectionInfo(int rowCount, int columnCount, boolean hierarchical,
int selectionMode);
@@ -273,10 +576,10 @@
public int getCollectionItemRowSpan(Object info);
public boolean isCollectionItemHeading(Object info);
public boolean isCollectionItemSelected(Object info);
- public AccessibilityNodeInfoCompat getTraversalBefore(Object info);
+ public Object getTraversalBefore(Object info);
public void setTraversalBefore(Object info, View view);
public void setTraversalBefore(Object info, View root, int virtualDescendantId);
- public AccessibilityNodeInfoCompat getTraversalAfter(Object info);
+ public Object getTraversalAfter(Object info);
public void setTraversalAfter(Object info, View view);
public void setTraversalAfter(Object info, View root, int virtualDescendantId);
public void setContentInvalid(Object info, boolean contentInvalid);
@@ -285,6 +588,29 @@
public CharSequence getError(Object info);
public void setLabelFor(Object info, View labeled);
public void setLabelFor(Object info, View root, int virtualDescendantId);
+ public Object getLabelFor(Object info);
+ public void setLabeledBy(Object info, View labeled);
+ public void setLabeledBy(Object info, View root, int virtualDescendantId);
+ public Object getLabeledBy(Object info);
+ public boolean canOpenPopup(Object info);
+ public void setCanOpenPopup(Object info, boolean opensPopup);
+ public List<Object> findAccessibilityNodeInfosByViewId(Object info, String viewId);
+ public Bundle getExtras(Object info);
+ public int getInputType(Object info);
+ public void setInputType(Object info, int inputType);
+ public void setMaxTextLength(Object info, int max);
+ public int getMaxTextLength(Object info);
+ public void setTextSelection(Object info, int start, int end);
+ public int getTextSelectionStart(Object info);
+ public int getTextSelectionEnd(Object info);
+ public Object getWindow(Object info);
+ public boolean isDismissable(Object info);
+ public void setDismissable(Object info, boolean dismissable);
+ public boolean isEditable(Object info);
+ public void setEditable(Object info, boolean editable);
+ public boolean isMultiLine(Object info);
+ public void setMultiLine(Object info, boolean multiLine);
+ public boolean refresh(Object info);
}
static class AccessibilityNodeInfoStubImpl implements AccessibilityNodeInfoImpl {
@@ -324,6 +650,11 @@
}
@Override
+ public boolean removeAction(Object info, Object action) {
+ return false;
+ }
+
+ @Override
public int getAccessibilityActionId(Object action) {
return 0;
}
@@ -344,6 +675,16 @@
}
@Override
+ public boolean removeChild(Object info, View child) {
+ return false;
+ }
+
+ @Override
+ public boolean removeChild(Object info, View root, int virtualDescendantId) {
+ return false;
+ }
+
+ @Override
public List<Object> findAccessibilityNodeInfosByText(Object info, String text) {
return Collections.emptyList();
}
@@ -652,6 +993,10 @@
}
@Override
+ public void setRangeInfo(Object info, Object rangeInfo) {
+ }
+
+ @Override
public List<Object> getActionList(Object info) {
return null;
}
@@ -714,7 +1059,7 @@
}
@Override
- public AccessibilityNodeInfoCompat getTraversalBefore(Object info) {
+ public Object getTraversalBefore(Object info) {
return null;
}
@@ -727,7 +1072,7 @@
}
@Override
- public AccessibilityNodeInfoCompat getTraversalAfter(Object info) {
+ public Object getTraversalAfter(Object info) {
return null;
}
@@ -764,6 +1109,112 @@
@Override
public void setLabelFor(Object info, View root, int virtualDescendantId) {
}
+
+ @Override
+ public Object getLabelFor(Object info) {
+ return null;
+ }
+
+ @Override
+ public void setLabeledBy(Object info, View labeled) {
+ }
+
+ @Override
+ public void setLabeledBy(Object info, View root, int virtualDescendantId) {
+ }
+
+ @Override
+ public Object getLabeledBy(Object info){
+ return null;
+ }
+
+ @Override
+ public boolean canOpenPopup(Object info) {
+ return false;
+ }
+
+ @Override
+ public void setCanOpenPopup(Object info, boolean opensPopup) {
+ }
+
+ @Override
+ public List<Object> findAccessibilityNodeInfosByViewId(Object info, String viewId) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Bundle getExtras(Object info) {
+ return new Bundle();
+ }
+
+ @Override
+ public int getInputType(Object info) {
+ return InputType.TYPE_NULL;
+ }
+
+ @Override
+ public void setInputType(Object info, int inputType) {
+ }
+
+ @Override
+ public void setMaxTextLength(Object info, int max) {
+ }
+
+ @Override
+ public int getMaxTextLength(Object info) {
+ return -1;
+ }
+
+ @Override
+ public void setTextSelection(Object info, int start, int end) {
+ }
+
+ @Override
+ public int getTextSelectionStart(Object info) {
+ return -1;
+ }
+
+ @Override
+ public int getTextSelectionEnd(Object info) {
+ return -1;
+ }
+
+ @Override
+ public Object getWindow(Object info) {
+ return null;
+ }
+
+ @Override
+ public boolean isDismissable(Object info) {
+ return false;
+ }
+
+ @Override
+ public void setDismissable(Object info, boolean dismissable) {
+ }
+
+ @Override
+ public boolean isEditable(Object info) {
+ return false;
+ }
+
+ @Override
+ public void setEditable(Object info, boolean editable) {
+ }
+
+ @Override
+ public boolean isMultiLine(Object info) {
+ return false;
+ }
+
+ @Override
+ public void setMultiLine(Object info, boolean multiLine) {
+ }
+
+ @Override
+ public boolean refresh(Object info) {
+ return false;
+ }
}
static class AccessibilityNodeInfoIcsImpl extends AccessibilityNodeInfoStubImpl {
@@ -1070,7 +1521,41 @@
}
}
- static class AccessibilityNodeInfoJellybeanMr2Impl extends AccessibilityNodeInfoJellybeanImpl {
+ static class AccessibilityNodeInfoJellybeanMr1Impl extends AccessibilityNodeInfoJellybeanImpl {
+
+ @Override
+ public void setLabelFor(Object info, View labeled) {
+ AccessibilityNodeInfoCompatJellybeanMr1.setLabelFor(info, labeled);
+ }
+
+ @Override
+ public void setLabelFor(Object info, View root, int virtualDescendantId) {
+ AccessibilityNodeInfoCompatJellybeanMr1.setLabelFor(info, root, virtualDescendantId);
+ }
+
+ @Override
+ public Object getLabelFor(Object info) {
+ return AccessibilityNodeInfoCompatJellybeanMr1.getLabelFor(info);
+ }
+
+ @Override
+ public void setLabeledBy(Object info, View labeled) {
+ AccessibilityNodeInfoCompatJellybeanMr1.setLabeledBy(info, labeled);
+ }
+
+ @Override
+ public void setLabeledBy(Object info, View root, int virtualDescendantId) {
+ AccessibilityNodeInfoCompatJellybeanMr1.setLabeledBy(info, root, virtualDescendantId);
+ }
+
+ @Override
+ public Object getLabeledBy(Object info) {
+ return AccessibilityNodeInfoCompatJellybeanMr1.getLabeledBy(info);
+ }
+ }
+
+ static class AccessibilityNodeInfoJellybeanMr2Impl extends
+ AccessibilityNodeInfoJellybeanMr1Impl {
@Override
public String getViewIdResourceName(Object info) {
@@ -1081,6 +1566,42 @@
public void setViewIdResourceName(Object info, String viewId) {
AccessibilityNodeInfoCompatJellybeanMr2.setViewIdResourceName(info, viewId);
}
+
+ @Override
+ public List<Object> findAccessibilityNodeInfosByViewId(Object info, String viewId) {
+ return AccessibilityNodeInfoCompatJellybeanMr2.findAccessibilityNodeInfosByViewId(info,
+ viewId);
+ }
+
+ @Override
+ public void setTextSelection(Object info, int start, int end) {
+ AccessibilityNodeInfoCompatJellybeanMr2.setTextSelection(info, start, end);
+ }
+
+ @Override
+ public int getTextSelectionStart(Object info) {
+ return AccessibilityNodeInfoCompatJellybeanMr2.getTextSelectionStart(info);
+ }
+
+ @Override
+ public int getTextSelectionEnd(Object info) {
+ return AccessibilityNodeInfoCompatJellybeanMr2.getTextSelectionEnd(info);
+ }
+
+ @Override
+ public boolean isEditable(Object info) {
+ return AccessibilityNodeInfoCompatJellybeanMr2.isEditable(info);
+ }
+
+ @Override
+ public void setEditable(Object info, boolean editable) {
+ AccessibilityNodeInfoCompatJellybeanMr2.setEditable(info, editable);
+ }
+
+ @Override
+ public boolean refresh(Object info) {
+ return AccessibilityNodeInfoCompatJellybeanMr2.refresh(info);
+ }
}
static class AccessibilityNodeInfoKitKatImpl extends AccessibilityNodeInfoJellybeanMr2Impl {
@@ -1144,6 +1665,11 @@
}
@Override
+ public void setRangeInfo(Object info, Object rangeInfo) {
+ AccessibilityNodeInfoCompatKitKat.setRangeInfo(info, rangeInfo);
+ }
+
+ @Override
public int getCollectionItemColumnIndex(Object info) {
return AccessibilityNodeInfoCompatKitKat.CollectionItemInfo.getColumnIndex(info);
}
@@ -1182,6 +1708,51 @@
public boolean isContentInvalid(Object info) {
return AccessibilityNodeInfoCompatKitKat.isContentInvalid(info);
}
+
+ @Override
+ public boolean canOpenPopup(Object info) {
+ return AccessibilityNodeInfoCompatKitKat.canOpenPopup(info);
+ }
+
+ @Override
+ public void setCanOpenPopup(Object info, boolean opensPopup) {
+ AccessibilityNodeInfoCompatKitKat.setCanOpenPopup(info, opensPopup);
+ }
+
+ @Override
+ public Bundle getExtras(Object info) {
+ return AccessibilityNodeInfoCompatKitKat.getExtras(info);
+ }
+
+ @Override
+ public int getInputType(Object info) {
+ return AccessibilityNodeInfoCompatKitKat.getInputType(info);
+ }
+
+ @Override
+ public void setInputType(Object info, int inputType) {
+ AccessibilityNodeInfoCompatKitKat.setInputType(info, inputType);
+ }
+
+ @Override
+ public boolean isDismissable(Object info) {
+ return AccessibilityNodeInfoCompatKitKat.isDismissable(info);
+ }
+
+ @Override
+ public void setDismissable(Object info, boolean dismissable) {
+ AccessibilityNodeInfoCompatKitKat.setDismissable(info, dismissable);
+ }
+
+ @Override
+ public boolean isMultiLine(Object info) {
+ return AccessibilityNodeInfoCompatKitKat.isMultiLine(info);
+ }
+
+ @Override
+ public void setMultiLine(Object info, boolean multiLine) {
+ AccessibilityNodeInfoCompatKitKat.setMultiLine(info, multiLine);
+ }
}
static class AccessibilityNodeInfoApi21Impl extends AccessibilityNodeInfoKitKatImpl {
@@ -1208,6 +1779,11 @@
}
@Override
+ public boolean removeAction(Object info, Object action) {
+ return AccessibilityNodeInfoCompatApi21.removeAction(info, action);
+ }
+
+ @Override
public int getAccessibilityActionId(Object action) {
return AccessibilityNodeInfoCompatApi21.getAccessibilityActionId(action);
}
@@ -1240,25 +1816,35 @@
}
@Override
- public void setLabelFor(Object info, View labeled) {
- AccessibilityNodeInfoCompatApi21.setLabelFor(info, labeled);
+ public void setMaxTextLength(Object info, int max) {
+ AccessibilityNodeInfoCompatApi21.setMaxTextLength(info, max);
}
@Override
- public void setLabelFor(Object info, View root, int virtualDescendantId) {
- AccessibilityNodeInfoCompatApi21.setLabelFor(info, root, virtualDescendantId);
+ public int getMaxTextLength(Object info) {
+ return AccessibilityNodeInfoCompatApi21.getMaxTextLength(info);
+ }
+
+ @Override
+ public Object getWindow(Object info) {
+ return AccessibilityNodeInfoCompatApi21.getWindow(info);
+ }
+
+ @Override
+ public boolean removeChild(Object info, View child) {
+ return AccessibilityNodeInfoCompatApi21.removeChild(info, child);
+ }
+
+ @Override
+ public boolean removeChild(Object info, View root, int virtualDescendantId) {
+ return AccessibilityNodeInfoCompatApi21.removeChild(info, root, virtualDescendantId);
}
}
static class AccessibilityNodeInfoApi22Impl extends AccessibilityNodeInfoApi21Impl {
@Override
- public AccessibilityNodeInfoCompat getTraversalBefore(Object info) {
- Object nodeInfo = AccessibilityNodeInfoCompatApi22.getTraversalBefore(info);
- if (nodeInfo == null) {
- return null;
- }
-
- return new AccessibilityNodeInfoCompat(nodeInfo);
+ public Object getTraversalBefore(Object info) {
+ return AccessibilityNodeInfoCompatApi22.getTraversalBefore(info);
}
@Override
@@ -1272,13 +1858,8 @@
}
@Override
- public AccessibilityNodeInfoCompat getTraversalAfter(Object info) {
- Object nodeInfo = AccessibilityNodeInfoCompatApi22.getTraversalAfter(info);
- if (nodeInfo == null) {
- return null;
- }
-
- return new AccessibilityNodeInfoCompat(nodeInfo);
+ public Object getTraversalAfter(Object info) {
+ return AccessibilityNodeInfoCompatApi22.getTraversalAfter(info);
}
@Override
@@ -1301,6 +1882,8 @@
IMPL = new AccessibilityNodeInfoKitKatImpl();
} else if (Build.VERSION.SDK_INT >= 18) { // JellyBean MR2
IMPL = new AccessibilityNodeInfoJellybeanMr2Impl();
+ } else if (Build.VERSION.SDK_INT >= 17) { // JellyBean MR1
+ IMPL = new AccessibilityNodeInfoJellybeanMr1Impl();
} else if (Build.VERSION.SDK_INT >= 16) { // JellyBean
IMPL = new AccessibilityNodeInfoJellybeanImpl();
} else if (Build.VERSION.SDK_INT >= 14) { // ICS
@@ -1501,6 +2084,21 @@
public static final int ACTION_SET_SELECTION = 0x00020000;
/**
+ * Action to expand an expandable node.
+ */
+ public static final int ACTION_EXPAND = 0x00040000;
+
+ /**
+ * Action to collapse an expandable node.
+ */
+ public static final int ACTION_COLLAPSE = 0x00080000;
+
+ /**
+ * Action to dismiss a dismissable node.
+ */
+ public static final int ACTION_DISMISS = 0x00100000;
+
+ /**
* Action that sets the text of the node. Performing the action without argument, using <code>
* null</code> or empty {@link CharSequence} will clear the text. This action will also put the
* cursor at the end of text.
@@ -1840,6 +2438,38 @@
}
/**
+ * Removes a child. If the child was not previously added to the node,
+ * calling this method has no effect.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param child The child.
+ * @return true if the child was present
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public boolean removeChild(View child) {
+ return IMPL.removeChild(mInfo, child);
+ }
+
+ /**
+ * Removes a virtual child which is a descendant of the given
+ * <code>root</code>. If the child was not previously added to the node,
+ * calling this method has no effect.
+ *
+ * @param root The root of the virtual subtree.
+ * @param virtualDescendantId The id of the virtual child.
+ * @return true if the child was present
+ * @see #addChild(View, int)
+ */
+ public boolean removeChild(View root, int virtualDescendantId) {
+ return IMPL.removeChild(mInfo, root, virtualDescendantId);
+ }
+
+ /**
* Gets the actions that can be performed on the node.
*
* @return The bit mask of with actions.
@@ -1883,6 +2513,24 @@
}
/**
+ * Removes an action that can be performed on the node. If the action was
+ * not already added to the node, calling this method has no effect.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param action The action to be removed.
+ * @return The action removed from the list of actions.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public boolean removeAction(AccessibilityActionCompat action) {
+ return IMPL.removeAction(mInfo, action.mAction);
+ }
+
+ /**
* Performs an action on the node.
* <p>
* <strong>Note:</strong> An action can be performed only if the request is
@@ -2574,6 +3222,20 @@
}
/**
+ * Sets the range info if this node is a range.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param rangeInfo The range info.
+ */
+ public void setRangeInfo(RangeInfoCompat rangeInfo) {
+ IMPL.setRangeInfo(mInfo, rangeInfo.mInfo);
+ }
+
+ /**
* Gets the actions that can be performed on the node.
*
* @return A list of AccessibilityActions.
@@ -2671,6 +3333,441 @@
IMPL.setLabelFor(mInfo, root, virtualDescendantId);
}
+ /**
+ * Gets the node info for which the view represented by this info serves as
+ * a label for accessibility purposes.
+ * <p>
+ * <strong>Note:</strong> It is a client responsibility to recycle the
+ * received info by calling {@link AccessibilityNodeInfoCompat#recycle()}
+ * to avoid creating of multiple instances.
+ * </p>
+ *
+ * @return The labeled info.
+ */
+ public AccessibilityNodeInfoCompat getLabelFor() {
+ return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getLabelFor(mInfo));
+ }
+
+ /**
+ * Sets the view which serves as the label of the view represented by
+ * this info for accessibility purposes.
+ *
+ * @param label The view that labels this node's source.
+ */
+ public void setLabeledBy(View label) {
+ IMPL.setLabeledBy(mInfo, label);
+ }
+
+ /**
+ * Sets the view which serves as the label of the view represented by
+ * this info for accessibility purposes. If <code>virtualDescendantId</code>
+ * is {@link View#NO_ID} the root is set as the label.
+ * <p>
+ * A virtual descendant is an imaginary View that is reported as a part of the view
+ * hierarchy for accessibility purposes. This enables custom views that draw complex
+ * content to report themselves as a tree of virtual views, thus conveying their
+ * logical structure.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param root The root whose virtual descendant labels this node's source.
+ * @param virtualDescendantId The id of the virtual descendant.
+ */
+ public void setLabeledBy(View root, int virtualDescendantId) {
+ IMPL.setLabeledBy(mInfo, root, virtualDescendantId);
+ }
+
+ /**
+ * Gets the node info which serves as the label of the view represented by
+ * this info for accessibility purposes.
+ * <p>
+ * <strong>Note:</strong> It is a client responsibility to recycle the
+ * received info by calling {@link AccessibilityNodeInfoCompat#recycle()}
+ * to avoid creating of multiple instances.
+ * </p>
+ *
+ * @return The label.
+ */
+ public AccessibilityNodeInfoCompat getLabeledBy() {
+ return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getLabeledBy(mInfo));
+ }
+
+ /**
+ * Gets if this node opens a popup or a dialog.
+ *
+ * @return If the the node opens a popup.
+ */
+ public boolean canOpenPopup() {
+ return IMPL.canOpenPopup(mInfo);
+ }
+
+ /**
+ * Sets if this node opens a popup or a dialog.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param opensPopup If the the node opens a popup.
+ */
+ public void setCanOpenPopup(boolean opensPopup) {
+ IMPL.setCanOpenPopup(mInfo, opensPopup);
+ }
+
+ /**
+ * Finds {@link AccessibilityNodeInfoCompat}s by the fully qualified view id's resource
+ * name where a fully qualified id is of the from "package:id/id_resource_name".
+ * For example, if the target application's package is "foo.bar" and the id
+ * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz".
+ *
+ * <p>
+ * <strong>Note:</strong> It is a client responsibility to recycle the
+ * received info by calling {@link AccessibilityNodeInfoCompat#recycle()}
+ * to avoid creating of multiple instances.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> The primary usage of this API is for UI test automation
+ * and in order to report the fully qualified view id if an
+ * {@link AccessibilityNodeInfoCompat} the client has to set the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
+ * flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
+ * </p>
+ *
+ * @param viewId The fully qualified resource name of the view id to find.
+ * @return A list of node info.
+ */
+ public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(String viewId) {
+ List<Object> nodes = IMPL.findAccessibilityNodeInfosByViewId(mInfo, viewId);
+ if (nodes != null) {
+ List<AccessibilityNodeInfoCompat> result = new ArrayList<AccessibilityNodeInfoCompat>();
+ for (Object node : nodes) {
+ result.add(new AccessibilityNodeInfoCompat(node));
+ }
+ return result;
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ /**
+ * Gets an optional bundle with extra data. The bundle
+ * is lazily created and never <code>null</code>.
+ * <p>
+ * <strong>Note:</strong> It is recommended to use the package
+ * name of your application as a prefix for the keys to avoid
+ * collisions which may confuse an accessibility service if the
+ * same key has different meaning when emitted from different
+ * applications.
+ * </p>
+ *
+ * @return The bundle.
+ */
+ public Bundle getExtras() {
+ return IMPL.getExtras(mInfo);
+ }
+
+ /**
+ * Gets the input type of the source as defined by {@link InputType}.
+ *
+ * @return The input type.
+ */
+ public int getInputType() {
+ return IMPL.getInputType(mInfo);
+ }
+
+ /**
+ * Sets the input type of the source as defined by {@link InputType}.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an
+ * AccessibilityService.
+ * </p>
+ *
+ * @param inputType The input type.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setInputType(int inputType) {
+ IMPL.setInputType(mInfo, inputType);
+ }
+
+ /**
+ * Sets the maximum text length, or -1 for no limit.
+ * <p>
+ * Typically used to indicate that an editable text field has a limit on
+ * the number of characters entered.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ *
+ * @param max The maximum text length.
+ * @see #getMaxTextLength()
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setMaxTextLength(int max) {
+ IMPL.setMaxTextLength(mInfo, max);
+ }
+
+ /**
+ * Returns the maximum text length for this node.
+ *
+ * @return The maximum text length, or -1 for no limit.
+ * @see #setMaxTextLength(int)
+ */
+ public int getMaxTextLength() {
+ return IMPL.getMaxTextLength(mInfo);
+ }
+
+ /**
+ * Sets the text selection start and end.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param start The text selection start.
+ * @param end The text selection end.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setTextSelection(int start, int end) {
+ IMPL.setTextSelection(mInfo, start, end);
+ }
+
+ /**
+ * Gets the text selection start.
+ *
+ * @return The text selection start if there is selection or -1.
+ */
+ public int getTextSelectionStart() {
+ return IMPL.getTextSelectionStart(mInfo);
+ }
+
+ /**
+ * Gets the text selection end.
+ *
+ * @return The text selection end if there is selection or -1.
+ */
+ public int getTextSelectionEnd() {
+ return IMPL.getTextSelectionEnd(mInfo);
+ }
+
+ /**
+ * Gets the node before which this one is visited during traversal. A screen-reader
+ * must visit the content of this node before the content of the one it precedes.
+ *
+ * @return The succeeding node if such or <code>null</code>.
+ *
+ * @see #setTraversalBefore(android.view.View)
+ * @see #setTraversalBefore(android.view.View, int)
+ */
+ public AccessibilityNodeInfoCompat getTraversalBefore() {
+ return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getTraversalBefore(mInfo));
+ }
+
+ /**
+ * Sets the view before whose node this one should be visited during traversal. A
+ * screen-reader must visit the content of this node before the content of the one
+ * it precedes.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param view The view providing the preceding node.
+ *
+ * @see #getTraversalBefore()
+ */
+ public void setTraversalBefore(View view) {
+ IMPL.setTraversalBefore(mInfo, view);
+ }
+
+ /**
+ * Sets the node before which this one is visited during traversal. A screen-reader
+ * must visit the content of this node before the content of the one it precedes.
+ * The successor is a virtual descendant of the given <code>root</code>. If
+ * <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root is set
+ * as the successor.
+ * <p>
+ * A virtual descendant is an imaginary View that is reported as a part of the view
+ * hierarchy for accessibility purposes. This enables custom views that draw complex
+ * content to report them selves as a tree of virtual views, thus conveying their
+ * logical structure.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param root The root of the virtual subtree.
+ * @param virtualDescendantId The id of the virtual descendant.
+ */
+ public void setTraversalBefore(View root, int virtualDescendantId) {
+ IMPL.setTraversalBefore(mInfo, root, virtualDescendantId);
+ }
+
+ /**
+ * Gets the node after which this one is visited in accessibility traversal.
+ * A screen-reader must visit the content of the other node before the content
+ * of this one.
+ *
+ * @return The succeeding node if such or <code>null</code>.
+ *
+ * @see #setTraversalAfter(android.view.View)
+ * @see #setTraversalAfter(android.view.View, int)
+ */
+ public AccessibilityNodeInfoCompat getTraversalAfter() {
+ return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getTraversalAfter(mInfo));
+ }
+
+ /**
+ * Sets the view whose node is visited after this one in accessibility traversal.
+ * A screen-reader must visit the content of the other node before the content
+ * of this one.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param view The previous view.
+ *
+ * @see #getTraversalAfter()
+ */
+ public void setTraversalAfter(View view) {
+ IMPL.setTraversalAfter(mInfo, view);
+ }
+
+ /**
+ * Sets the node after which this one is visited in accessibility traversal.
+ * A screen-reader must visit the content of the other node before the content
+ * of this one. If <code>virtualDescendantId</code> equals to {@link View#NO_ID}
+ * the root is set as the predecessor.
+ * <p>
+ * A virtual descendant is an imaginary View that is reported as a part of the view
+ * hierarchy for accessibility purposes. This enables custom views that draw complex
+ * content to report them selves as a tree of virtual views, thus conveying their
+ * logical structure.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param root The root of the virtual subtree.
+ * @param virtualDescendantId The id of the virtual descendant.
+ */
+ public void setTraversalAfter(View root, int virtualDescendantId) {
+ IMPL.setTraversalAfter(mInfo, root, virtualDescendantId);
+ }
+
+ /**
+ * Gets the window to which this node belongs.
+ *
+ * @return The window.
+ *
+ * @see android.accessibilityservice.AccessibilityService#getWindows()
+ */
+ public AccessibilityWindowInfoCompat getWindow() {
+ return AccessibilityWindowInfoCompat.wrapNonNullInstance(IMPL.getWindow(mInfo));
+ }
+
+ /**
+ * Gets if the node can be dismissed.
+ *
+ * @return If the node can be dismissed.
+ */
+ public boolean isDismissable() {
+ return IMPL.isDismissable(mInfo);
+ }
+
+ /**
+ * Sets if the node can be dismissed.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param dismissable If the node can be dismissed.
+ */
+ public void setDismissable(boolean dismissable) {
+ IMPL.setDismissable(mInfo, dismissable);
+ }
+
+ /**
+ * Gets if the node is editable.
+ *
+ * @return True if the node is editable, false otherwise.
+ */
+ public boolean isEditable() {
+ return IMPL.isEditable(mInfo);
+ }
+
+ /**
+ * Sets whether this node is editable.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param editable True if the node is editable.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setEditable(boolean editable) {
+ IMPL.setEditable(mInfo, editable);
+ }
+
+ /**
+ * Gets if the node is a multi line editable text.
+ *
+ * @return True if the node is multi line.
+ */
+ public boolean isMultiLine() {
+ return IMPL.isMultiLine(mInfo);
+ }
+
+ /**
+ * Sets if the node is a multi line editable text.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param multiLine True if the node is multi line.
+ */
+ public void setMultiLine(boolean multiLine) {
+ IMPL.setMultiLine(mInfo, multiLine);
+ }
+
+ /**
+ * Refreshes this info with the latest state of the view it represents.
+ * <p>
+ * <strong>Note:</strong> If this method returns false this info is obsolete
+ * since it represents a view that is no longer in the view tree and should
+ * be recycled.
+ * </p>
+ * @return Whether the refresh succeeded.
+ */
+ public boolean refresh() {
+ return IMPL.refresh(mInfo);
+ }
+
@Override
public int hashCode() {
return (mInfo == null) ? 0 : mInfo.hashCode();
diff -Nur support-v4-22.2.1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java support-v4-23.0.0/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java
--- support-v4-22.2.1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java 2015-06-23 08:43:44.000000000 +0900
@@ -35,6 +35,10 @@
((AccessibilityNodeInfo) info).addAction((AccessibilityAction) action);
}
+ public static boolean removeAction(Object info, Object action) {
+ return ((AccessibilityNodeInfo) info).removeAction((AccessibilityAction) action);
+ }
+
public static Object obtainCollectionInfo(int rowCount, int columnCount,
boolean hierarchical, int selectionMode) {
return AccessibilityNodeInfo.CollectionInfo.obtain(rowCount, columnCount, hierarchical,
@@ -55,12 +59,24 @@
((AccessibilityNodeInfo) info).setError(error);
}
- public static void setLabelFor(Object info, View labeled) {
- ((AccessibilityNodeInfo) info).setLabelFor(labeled);
+ public static void setMaxTextLength(Object info, int max) {
+ ((AccessibilityNodeInfo) info).setMaxTextLength(max);
+ }
+
+ public static int getMaxTextLength(Object info) {
+ return ((AccessibilityNodeInfo) info).getMaxTextLength();
+ }
+
+ public static Object getWindow(Object info) {
+ return ((AccessibilityNodeInfo) info).getWindow();
+ }
+
+ public static boolean removeChild(Object info, View child) {
+ return ((AccessibilityNodeInfo) info).removeChild(child);
}
- public static void setLabelFor(Object info, View root, int virtualDescendantId) {
- ((AccessibilityNodeInfo) info).setLabelFor(root, virtualDescendantId);
+ public static boolean removeChild(Object info, View root, int virtualDescendantId) {
+ return ((AccessibilityNodeInfo) info).removeChild(root, virtualDescendantId);
}
static class CollectionItemInfo {
diff -Nur support-v4-22.2.1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java support-v4-23.0.0/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java
--- support-v4-22.2.1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.view.accessibility;
+
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+class AccessibilityNodeInfoCompatJellybeanMr1 {
+
+ public static void setLabelFor(Object info, View labeled) {
+ ((AccessibilityNodeInfo) info).setLabelFor(labeled);
+ }
+
+ public static void setLabelFor(Object info, View root, int virtualDescendantId) {
+ ((AccessibilityNodeInfo) info).setLabelFor(root, virtualDescendantId);
+ }
+
+ public static Object getLabelFor(Object info) {
+ return ((AccessibilityNodeInfo) info).getLabelFor();
+ }
+
+ public static void setLabeledBy(Object info, View labeled) {
+ ((AccessibilityNodeInfo) info).setLabeledBy(labeled);
+ }
+
+ public static void setLabeledBy(Object info, View root, int virtualDescendantId) {
+ ((AccessibilityNodeInfo) info).setLabeledBy(root, virtualDescendantId);
+ }
+
+ public static Object getLabeledBy(Object info) {
+ return ((AccessibilityNodeInfo) info).getLabeledBy();
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java support-v4-23.0.0/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java
--- support-v4-22.2.1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java 2015-06-23 08:43:44.000000000 +0900
@@ -18,6 +18,8 @@
import android.view.accessibility.AccessibilityNodeInfo;
+import java.util.List;
+
class AccessibilityNodeInfoCompatJellybeanMr2 {
public static void setViewIdResourceName(Object info, String viewId) {
@@ -27,4 +29,34 @@
public static String getViewIdResourceName(Object info) {
return ((AccessibilityNodeInfo) info).getViewIdResourceName();
}
+
+ @SuppressWarnings("unchecked")
+ public static List<Object> findAccessibilityNodeInfosByViewId(Object info, String viewId) {
+ Object result = ((AccessibilityNodeInfo) info).findAccessibilityNodeInfosByViewId(viewId);
+ return (List<Object>) result;
+ }
+
+ public static void setTextSelection(Object info, int start, int end) {
+ ((AccessibilityNodeInfo) info).setTextSelection(start, end);
+ }
+
+ public static int getTextSelectionStart(Object info) {
+ return ((AccessibilityNodeInfo) info).getTextSelectionStart();
+ }
+
+ public static int getTextSelectionEnd(Object info) {
+ return ((AccessibilityNodeInfo) info).getTextSelectionEnd();
+ }
+
+ public static boolean isEditable(Object info) {
+ return ((AccessibilityNodeInfo) info).isEditable();
+ }
+
+ public static void setEditable(Object info, boolean editable) {
+ ((AccessibilityNodeInfo) info).setEditable(editable);
+ }
+
+ public static boolean refresh(Object info) {
+ return ((AccessibilityNodeInfo) info).refresh();
+ }
}
diff -Nur support-v4-22.2.1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java support-v4-23.0.0/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
--- support-v4-22.2.1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java 2015-06-23 08:43:44.000000000 +0900
@@ -16,6 +16,7 @@
package android.support.v4.view.accessibility;
+import android.os.Bundle;
import android.view.accessibility.AccessibilityNodeInfo;
/**
@@ -52,6 +53,10 @@
return ((AccessibilityNodeInfo) info).getRangeInfo();
}
+ public static void setRangeInfo(Object info, Object rangeInfo) {
+ ((AccessibilityNodeInfo) info).setRangeInfo((AccessibilityNodeInfo.RangeInfo) rangeInfo);
+ }
+
public static Object obtainCollectionInfo(int rowCount, int columnCount,
boolean hierarchical, int selectionMode) {
return AccessibilityNodeInfo.CollectionInfo.obtain(rowCount, columnCount, hierarchical);
@@ -71,6 +76,42 @@
return ((AccessibilityNodeInfo) info).isContentInvalid();
}
+ public static boolean canOpenPopup(Object info) {
+ return ((AccessibilityNodeInfo) info).canOpenPopup();
+ }
+
+ public static void setCanOpenPopup(Object info, boolean opensPopup) {
+ ((AccessibilityNodeInfo) info).setCanOpenPopup(opensPopup);
+ }
+
+ public static Bundle getExtras(Object info) {
+ return ((AccessibilityNodeInfo) info).getExtras();
+ }
+
+ public static int getInputType(Object info) {
+ return ((AccessibilityNodeInfo) info).getInputType();
+ }
+
+ public static void setInputType(Object info, int inputType) {
+ ((AccessibilityNodeInfo) info).setInputType(inputType);
+ }
+
+ public static boolean isDismissable(Object info) {
+ return ((AccessibilityNodeInfo) info).isDismissable();
+ }
+
+ public static void setDismissable(Object info, boolean dismissable) {
+ ((AccessibilityNodeInfo) info).setDismissable(dismissable);
+ }
+
+ public static boolean isMultiLine(Object info) {
+ return ((AccessibilityNodeInfo) info).isMultiLine();
+ }
+
+ public static void setMultiLine(Object info, boolean multiLine) {
+ ((AccessibilityNodeInfo) info).setMultiLine(multiLine);
+ }
+
static class CollectionInfo {
static int getColumnCount(Object info) {
return ((AccessibilityNodeInfo.CollectionInfo) info).getColumnCount();
diff -Nur support-v4-22.2.1/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java support-v4-23.0.0/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java
--- support-v4-22.2.1/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java 2015-06-23 08:43:44.000000000 +0900
@@ -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.view.accessibility;
+
+import android.graphics.Rect;
+import android.os.Build;
+
+/**
+ * Helper for accessing {@link android.view.accessibility.AccessibilityWindowInfo}
+ * introduced after API level 4 in a backwards compatible fashion.
+ */
+public class AccessibilityWindowInfoCompat {
+
+ private static interface AccessibilityWindowInfoImpl {
+ public Object obtain();
+ public Object obtain(Object info);
+ public int getType(Object info);
+ public int getLayer(Object info);
+ public Object getRoot(Object info);
+ public Object getParent(Object info);
+ public int getId(Object info);
+ public void getBoundsInScreen(Object info, Rect outBounds);
+ public boolean isActive(Object info);
+ public boolean isFocused(Object info);
+ public boolean isAccessibilityFocused(Object info);
+ public int getChildCount(Object info);
+ public Object getChild(Object info, int index);
+ public void recycle(Object info);
+ }
+
+ private static class AccessibilityWindowInfoStubImpl implements AccessibilityWindowInfoImpl {
+
+ @Override
+ public Object obtain() {
+ return null;
+ }
+
+ @Override
+ public Object obtain(Object info) {
+ return null;
+ }
+
+ @Override
+ public int getType(Object info) {
+ return UNDEFINED;
+ }
+
+ @Override
+ public int getLayer(Object info) {
+ return UNDEFINED;
+ }
+
+ @Override
+ public Object getRoot(Object info) {
+ return null;
+ }
+
+ @Override
+ public Object getParent(Object info) {
+ return null;
+ }
+
+ @Override
+ public int getId(Object info) {
+ return UNDEFINED;
+ }
+
+ @Override
+ public void getBoundsInScreen(Object info, Rect outBounds) {
+ }
+
+ @Override
+ public boolean isActive(Object info) {
+ return true;
+ }
+
+ @Override
+ public boolean isFocused(Object info) {
+ return true;
+ }
+
+ @Override
+ public boolean isAccessibilityFocused(Object info) {
+ return true;
+ }
+
+ @Override
+ public int getChildCount(Object info) {
+ return 0;
+ }
+
+ @Override
+ public Object getChild(Object info, int index) {
+ return null;
+ }
+
+ @Override
+ public void recycle(Object info) {
+ }
+ }
+
+ private static class AccessibilityWindowInfoApi21Impl extends AccessibilityWindowInfoStubImpl {
+ @Override
+ public Object obtain() {
+ return AccessibilityWindowInfoCompatApi21.obtain();
+ }
+
+ @Override
+ public Object obtain(Object info) {
+ return AccessibilityWindowInfoCompatApi21.obtain(info);
+ }
+
+ @Override
+ public int getType(Object info) {
+ return AccessibilityWindowInfoCompatApi21.getType(info);
+ }
+
+ @Override
+ public int getLayer(Object info) {
+ return AccessibilityWindowInfoCompatApi21.getLayer(info);
+ }
+
+ @Override
+ public Object getRoot(Object info) {
+ return AccessibilityWindowInfoCompatApi21.getRoot(info);
+ }
+
+ @Override
+ public Object getParent(Object info) {
+ return AccessibilityWindowInfoCompatApi21.getParent(info);
+ }
+
+ @Override
+ public int getId(Object info) {
+ return AccessibilityWindowInfoCompatApi21.getId(info);
+ }
+
+ @Override
+ public void getBoundsInScreen(Object info, Rect outBounds) {
+ AccessibilityWindowInfoCompatApi21.getBoundsInScreen(info, outBounds);
+ }
+
+ @Override
+ public boolean isActive(Object info) {
+ return AccessibilityWindowInfoCompatApi21.isActive(info);
+ }
+
+ @Override
+ public boolean isFocused(Object info) {
+ return AccessibilityWindowInfoCompatApi21.isFocused(info);
+ }
+
+ @Override
+ public boolean isAccessibilityFocused(Object info) {
+ return AccessibilityWindowInfoCompatApi21.isAccessibilityFocused(info);
+ }
+
+ @Override
+ public int getChildCount(Object info) {
+ return AccessibilityWindowInfoCompatApi21.getChildCount(info);
+ }
+
+ @Override
+ public Object getChild(Object info, int index) {
+ return AccessibilityWindowInfoCompatApi21.getChild(info, index);
+ }
+
+ @Override
+ public void recycle(Object info) {
+ AccessibilityWindowInfoCompatApi21.recycle(info);
+ }
+ }
+
+ static {
+ if (Build.VERSION.SDK_INT >= 21) {
+ IMPL = new AccessibilityWindowInfoApi21Impl();
+ } else {
+ IMPL = new AccessibilityWindowInfoStubImpl();
+ }
+ }
+
+ private static final AccessibilityWindowInfoImpl IMPL;
+ private Object mInfo;
+
+ private static final int UNDEFINED = -1;
+
+ /**
+ * Window type: This is an application window. Such a window shows UI for
+ * interacting with an application.
+ */
+ public static final int TYPE_APPLICATION = 1;
+
+ /**
+ * Window type: This is an input method window. Such a window shows UI for
+ * inputting text such as keyboard, suggestions, etc.
+ */
+ public static final int TYPE_INPUT_METHOD = 2;
+
+ /**
+ * Window type: This is an system window. Such a window shows UI for
+ * interacting with the system.
+ */
+ public static final int TYPE_SYSTEM = 3;
+
+ /**
+ * Window type: Windows that are overlaid <em>only</em> by an {@link
+ * android.accessibilityservice.AccessibilityService} for interception of
+ * user interactions without changing the windows an accessibility service
+ * can introspect. In particular, an accessibility service can introspect
+ * only windows that a sighted user can interact with which they can touch
+ * these windows or can type into these windows. For example, if there
+ * is a full screen accessibility overlay that is touchable, the windows
+ * below it will be introspectable by an accessibility service regardless
+ * they are covered by a touchable window.
+ */
+ public static final int TYPE_ACCESSIBILITY_OVERLAY = 4;
+
+ /**
+ * Creates a wrapper for info implementation.
+ *
+ * @param object The info to wrap.
+ * @return A wrapper for if the object is not null, null otherwise.
+ */
+ static AccessibilityWindowInfoCompat wrapNonNullInstance(Object object) {
+ if (object != null) {
+ return new AccessibilityWindowInfoCompat(object);
+ }
+ return null;
+ }
+
+ private AccessibilityWindowInfoCompat(Object info) {
+ mInfo = info;
+ }
+
+ /**
+ * Gets the type of the window.
+ *
+ * @return The type.
+ *
+ * @see #TYPE_APPLICATION
+ * @see #TYPE_INPUT_METHOD
+ * @see #TYPE_SYSTEM
+ * @see #TYPE_ACCESSIBILITY_OVERLAY
+ */
+ public int getType() {
+ return IMPL.getType(mInfo);
+ }
+
+ /**
+ * Gets the layer which determines the Z-order of the window. Windows
+ * with greater layer appear on top of windows with lesser layer.
+ *
+ * @return The window layer.
+ */
+ public int getLayer() {
+ return IMPL.getLayer(mInfo);
+ }
+
+ /**
+ * Gets the root node in the window's hierarchy.
+ *
+ * @return The root node.
+ */
+ public AccessibilityNodeInfoCompat getRoot() {
+ return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getRoot(mInfo));
+ }
+
+ /**
+ * Gets the parent window if such.
+ *
+ * @return The parent window.
+ */
+ public AccessibilityWindowInfoCompat getParent() {
+ return wrapNonNullInstance(IMPL.getParent(mInfo));
+ }
+
+ /**
+ * Gets the unique window id.
+ *
+ * @return windowId The window id.
+ */
+ public int getId() {
+ return IMPL.getId(mInfo);
+ }
+
+ /**
+ * Gets the bounds of this window in the screen.
+ *
+ * @param outBounds The out window bounds.
+ */
+ public void getBoundsInScreen(Rect outBounds) {
+ IMPL.getBoundsInScreen(mInfo, outBounds);
+ }
+
+ /**
+ * Gets if this window is active. An active window is the one
+ * the user is currently touching or the window has input focus
+ * and the user is not touching any window.
+ *
+ * @return Whether this is the active window.
+ */
+ public boolean isActive() {
+ return IMPL.isActive(mInfo);
+ }
+
+ /**
+ * Gets if this window has input focus.
+ *
+ * @return Whether has input focus.
+ */
+ public boolean isFocused() {
+ return IMPL.isFocused(mInfo);
+ }
+
+ /**
+ * Gets if this window has accessibility focus.
+ *
+ * @return Whether has accessibility focus.
+ */
+ public boolean isAccessibilityFocused() {
+ return IMPL.isAccessibilityFocused(mInfo);
+ }
+
+ /**
+ * Gets the number of child windows.
+ *
+ * @return The child count.
+ */
+ public int getChildCount() {
+ return IMPL.getChildCount(mInfo);
+ }
+
+ /**
+ * Gets the child window at a given index.
+ *
+ * @param index The index.
+ * @return The child.
+ */
+ public AccessibilityWindowInfoCompat getChild(int index) {
+ return wrapNonNullInstance(IMPL.getChild(mInfo, index));
+ }
+
+ /**
+ * Returns a cached instance if such is available or a new one is
+ * created.
+ *
+ * @return An instance.
+ */
+ public static AccessibilityWindowInfoCompat obtain() {
+ return wrapNonNullInstance(IMPL.obtain());
+ }
+
+ /**
+ * Returns a cached instance if such is available or a new one is
+ * created. The returned instance is initialized from the given
+ * <code>info</code>.
+ *
+ * @param info The other info.
+ * @return An instance.
+ */
+ public static AccessibilityWindowInfoCompat obtain(AccessibilityWindowInfoCompat info) {
+ return wrapNonNullInstance(IMPL.obtain(info.mInfo));
+ }
+
+ /**
+ * Return an instance back to be reused.
+ * <p>
+ * <strong>Note:</strong> You must not touch the object after calling this function.
+ * </p>
+ *
+ * @throws IllegalStateException If the info is already recycled.
+ */
+ public void recycle() {
+ IMPL.recycle(mInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return (mInfo == null) ? 0 : mInfo.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ AccessibilityWindowInfoCompat other = (AccessibilityWindowInfoCompat) obj;
+ if (mInfo == null) {
+ if (other.mInfo != null) {
+ return false;
+ }
+ } else if (!mInfo.equals(other.mInfo)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ Rect bounds = new Rect();
+ getBoundsInScreen(bounds);
+ builder.append("AccessibilityWindowInfo[");
+ builder.append("id=").append(getId());
+ builder.append(", type=").append(typeToString(getType()));
+ builder.append(", layer=").append(getLayer());
+ builder.append(", bounds=").append(bounds);
+ builder.append(", focused=").append(isFocused());
+ builder.append(", active=").append(isActive());
+ builder.append(", hasParent=").append(getParent() != null);
+ builder.append(", hasChildren=").append(getChildCount() > 0);
+ builder.append(']');
+ return builder.toString();
+ }
+
+ private static String typeToString(int type) {
+ switch (type) {
+ case TYPE_APPLICATION: {
+ return "TYPE_APPLICATION";
+ }
+ case TYPE_INPUT_METHOD: {
+ return "TYPE_INPUT_METHOD";
+ }
+ case TYPE_SYSTEM: {
+ return "TYPE_SYSTEM";
+ }
+ case TYPE_ACCESSIBILITY_OVERLAY: {
+ return "TYPE_ACCESSIBILITY_OVERLAY";
+ }
+ default:
+ return "<UNKNOWN>";
+ }
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java support-v4-23.0.0/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java
--- support-v4-22.2.1/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.view.accessibility;
+
+import android.graphics.Rect;
+import android.view.accessibility.AccessibilityWindowInfo;
+
+/**
+ * Api21-specific AccessibilityWindowInfo API implementation.
+ */
+class AccessibilityWindowInfoCompatApi21 {
+
+ public static Object obtain() {
+ return AccessibilityWindowInfo.obtain();
+ }
+
+ public static Object obtain(Object info) {
+ return AccessibilityWindowInfo.obtain((AccessibilityWindowInfo) info);
+
+ }
+
+ public static int getType(Object info) {
+ return ((AccessibilityWindowInfo) info).getType();
+ }
+
+ public static int getLayer(Object info) {
+ return ((AccessibilityWindowInfo) info).getLayer();
+ }
+
+ public static Object getRoot(Object info) {
+ return ((AccessibilityWindowInfo) info).getRoot();
+ }
+
+ public static Object getParent(Object info) {
+ return ((AccessibilityWindowInfo) info).getParent();
+ }
+
+ public static int getId(Object info) {
+ return ((AccessibilityWindowInfo) info).getId();
+ }
+
+ public static void getBoundsInScreen(Object info, Rect outBounds) {
+ ((AccessibilityWindowInfo) info).getBoundsInScreen(outBounds);
+ }
+
+ public static boolean isActive(Object info) {
+ return ((AccessibilityWindowInfo) info).isActive();
+ }
+
+ public static boolean isFocused(Object info) {
+ return ((AccessibilityWindowInfo) info).isFocused();
+ }
+
+ public static boolean isAccessibilityFocused(Object info) {
+ return ((AccessibilityWindowInfo) info).isAccessibilityFocused();
+ }
+
+ public static int getChildCount(Object info) {
+ return ((AccessibilityWindowInfo) info).getChildCount();
+ }
+
+ public static Object getChild(Object info, int index) {
+ return ((AccessibilityWindowInfo) info).getChild(index);
+ }
+
+ public static void recycle(Object info) {
+ ((AccessibilityWindowInfo) info).recycle();
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/widget/CompoundButtonCompat.java support-v4-23.0.0/android/support/v4/widget/CompoundButtonCompat.java
--- support-v4-22.2.1/android/support/v4/widget/CompoundButtonCompat.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/widget/CompoundButtonCompat.java 2015-07-15 08:50:04.000000000 +0900
@@ -0,0 +1,176 @@
+/*
+ * 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.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.widget.CompoundButton;
+
+/**
+ * Helper for accessing {@link android.widget.CompoundButton} methods introduced after
+ * API level 4 in a backwards compatible fashion.
+ */
+public final class CompoundButtonCompat {
+
+ private static final CompoundButtonCompatImpl IMPL;
+
+ static {
+ final int sdk = Build.VERSION.SDK_INT;
+ if (sdk >= 23) {
+ IMPL = new Api23CompoundButtonImpl();
+ } else if (sdk >= 21) {
+ IMPL = new LollipopCompoundButtonImpl();
+ } else {
+ IMPL = new BaseCompoundButtonCompat();
+ }
+ }
+
+ interface CompoundButtonCompatImpl {
+ void setButtonTintList(CompoundButton button, ColorStateList tint);
+ ColorStateList getButtonTintList(CompoundButton button);
+ void setButtonTintMode(CompoundButton button, PorterDuff.Mode tintMode);
+ PorterDuff.Mode getButtonTintMode(CompoundButton button);
+ Drawable getButtonDrawable(CompoundButton button);
+ }
+
+ static class BaseCompoundButtonCompat implements CompoundButtonCompatImpl {
+ @Override
+ public void setButtonTintList(CompoundButton button, ColorStateList tint) {
+ CompoundButtonCompatDonut.setButtonTintList(button, tint);
+ }
+
+ @Override
+ public ColorStateList getButtonTintList(CompoundButton button) {
+ return CompoundButtonCompatDonut.getButtonTintList(button);
+ }
+
+ @Override
+ public void setButtonTintMode(CompoundButton button, PorterDuff.Mode tintMode) {
+ CompoundButtonCompatDonut.setButtonTintMode(button, tintMode);
+ }
+
+ @Override
+ public PorterDuff.Mode getButtonTintMode(CompoundButton button) {
+ return CompoundButtonCompatDonut.getButtonTintMode(button);
+ }
+
+ @Override
+ public Drawable getButtonDrawable(CompoundButton button) {
+ return CompoundButtonCompatDonut.getButtonDrawable(button);
+ }
+ }
+
+ static class LollipopCompoundButtonImpl extends BaseCompoundButtonCompat {
+ @Override
+ public void setButtonTintList(CompoundButton button, ColorStateList tint) {
+ CompoundButtonCompatLollipop.setButtonTintList(button, tint);
+ }
+
+ @Override
+ public ColorStateList getButtonTintList(CompoundButton button) {
+ return CompoundButtonCompatLollipop.getButtonTintList(button);
+ }
+
+ @Override
+ public void setButtonTintMode(CompoundButton button, PorterDuff.Mode tintMode) {
+ CompoundButtonCompatLollipop.setButtonTintMode(button, tintMode);
+ }
+
+ @Override
+ public PorterDuff.Mode getButtonTintMode(CompoundButton button) {
+ return CompoundButtonCompatLollipop.getButtonTintMode(button);
+ }
+ }
+
+ static class Api23CompoundButtonImpl extends LollipopCompoundButtonImpl {
+ @Override
+ public Drawable getButtonDrawable(CompoundButton button) {
+ return CompoundButtonCompatApi23.getButtonDrawable(button);
+ }
+ }
+
+ private CompoundButtonCompat() {}
+
+ /**
+ * Applies a tint to the button drawable. Does not modify the current tint
+ * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+ * <p>
+ * Subsequent calls to {@link CompoundButton#setButtonDrawable(Drawable)} should
+ * automatically mutate the drawable and apply the specified tint and tint
+ * mode using {@link DrawableCompat#setTintList(Drawable, ColorStateList)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @see #setButtonTintList(CompoundButton, ColorStateList)
+ */
+ public static void setButtonTintList(@NonNull CompoundButton button, @Nullable ColorStateList tint) {
+ IMPL.setButtonTintList(button, tint);
+ }
+
+ /**
+ * Returns the tint applied to the button drawable
+ *
+ * @see #setButtonTintList(CompoundButton, ColorStateList)
+ */
+ @Nullable
+ public static ColorStateList getButtonTintList(@NonNull CompoundButton button) {
+ return IMPL.getButtonTintList(button);
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setButtonTintList(CompoundButton, ColorStateList)}} to the button drawable. The
+ * default mode is {@link PorterDuff.Mode#SRC_IN}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @see #getButtonTintMode(CompoundButton)
+ * @see DrawableCompat#setTintMode(Drawable, PorterDuff.Mode)
+ */
+ public static void setButtonTintMode(@NonNull CompoundButton button,
+ @Nullable PorterDuff.Mode tintMode) {
+ IMPL.setButtonTintMode(button, tintMode);
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the button drawable
+ * @attr ref android.R.styleable#CompoundButton_buttonTintMode
+ * @see #setButtonTintMode(PorterDuff.Mode)
+ */
+ @Nullable
+ public static PorterDuff.Mode getButtonTintMode(@NonNull CompoundButton button) {
+ return IMPL.getButtonTintMode(button);
+ }
+
+ /**
+ * Returns the drawable used as the compound button image
+ *
+ * @see CompoundButton#setButtonDrawable(Drawable)
+ */
+ @Nullable
+ public static Drawable getButtonDrawable(@NonNull CompoundButton button) {
+ return IMPL.getButtonDrawable(button);
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/widget/CompoundButtonCompatApi23.java support-v4-23.0.0/android/support/v4/widget/CompoundButtonCompatApi23.java
--- support-v4-22.2.1/android/support/v4/widget/CompoundButtonCompatApi23.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/widget/CompoundButtonCompatApi23.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,30 @@
+/*
+ * 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.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import android.widget.CompoundButton;
+
+class CompoundButtonCompatApi23 {
+
+ static Drawable getButtonDrawable(CompoundButton button) {
+ return button.getButtonDrawable();
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/widget/CompoundButtonCompatDonut.java support-v4-23.0.0/android/support/v4/widget/CompoundButtonCompatDonut.java
--- support-v4-22.2.1/android/support/v4/widget/CompoundButtonCompatDonut.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/widget/CompoundButtonCompatDonut.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,82 @@
+/*
+ * 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.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.widget.CompoundButton;
+
+import java.lang.reflect.Field;
+
+class CompoundButtonCompatDonut {
+
+ private static final String TAG = "CompoundButtonCompatDonut";
+
+ private static Field sButtonDrawableField;
+ private static boolean sButtonDrawableFieldFetched;
+
+ static void setButtonTintList(CompoundButton button, ColorStateList tint) {
+ if (button instanceof TintableCompoundButton) {
+ ((TintableCompoundButton) button).setSupportButtonTintList(tint);
+ }
+ }
+
+ static ColorStateList getButtonTintList(CompoundButton button) {
+ if (button instanceof TintableCompoundButton) {
+ return((TintableCompoundButton) button).getSupportButtonTintList();
+ }
+ return null;
+ }
+
+ static void setButtonTintMode(CompoundButton button, PorterDuff.Mode tintMode) {
+ if (button instanceof TintableCompoundButton) {
+ ((TintableCompoundButton) button).setSupportButtonTintMode(tintMode);
+ }
+ }
+
+ static PorterDuff.Mode getButtonTintMode(CompoundButton button) {
+ if (button instanceof TintableCompoundButton) {
+ return ((TintableCompoundButton) button).getSupportButtonTintMode();
+ }
+ return null;
+ }
+
+ static Drawable getButtonDrawable(CompoundButton button) {
+ if (!sButtonDrawableFieldFetched) {
+ try {
+ sButtonDrawableField = CompoundButton.class.getDeclaredField("mButtonDrawable");
+ sButtonDrawableField.setAccessible(true);
+ } catch (NoSuchFieldException e) {
+ Log.i(TAG, "Failed to retrieve mButtonDrawable field", e);
+ }
+ sButtonDrawableFieldFetched = true;
+ }
+
+ if (sButtonDrawableField != null) {
+ try {
+ return (Drawable) sButtonDrawableField.get(button);
+ } catch (IllegalAccessException e) {
+ Log.i(TAG, "Failed to get button drawable via reflection", e);
+ sButtonDrawableField = null;
+ }
+ }
+ return null;
+ }
+
+}
diff -Nur support-v4-22.2.1/android/support/v4/widget/CompoundButtonCompatLollipop.java support-v4-23.0.0/android/support/v4/widget/CompoundButtonCompatLollipop.java
--- support-v4-22.2.1/android/support/v4/widget/CompoundButtonCompatLollipop.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/widget/CompoundButtonCompatLollipop.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,40 @@
+/*
+ * 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.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.widget.CompoundButton;
+
+class CompoundButtonCompatLollipop {
+
+ static void setButtonTintList(CompoundButton button, ColorStateList tint) {
+ button.setButtonTintList(tint);
+ }
+
+ static ColorStateList getButtonTintList(CompoundButton button) {
+ return button.getButtonTintList();
+ }
+
+ static void setButtonTintMode(CompoundButton button, PorterDuff.Mode tintMode) {
+ button.setButtonTintMode(tintMode);
+ }
+
+ static PorterDuff.Mode getButtonTintMode(CompoundButton button) {
+ return button.getButtonTintMode();
+ }
+}
diff -Nur support-v4-22.2.1/android/support/v4/widget/DrawerLayout.java support-v4-23.0.0/android/support/v4/widget/DrawerLayout.java
--- support-v4-22.2.1/android/support/v4/widget/DrawerLayout.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/widget/DrawerLayout.java 2015-08-03 08:22:34.000000000 +0900
@@ -29,10 +29,12 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
+import android.support.annotation.ColorInt;
import android.support.annotation.DrawableRes;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.view.AccessibilityDelegateCompat;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.KeyEventCompat;
@@ -40,6 +42,7 @@
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewGroupCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -51,6 +54,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -134,6 +138,7 @@
private static final int MIN_DRAWER_MARGIN = 64; // dp
+ private static final int DRAWER_ELEVATION = 10; //dp
private static final int DEFAULT_SCRIM_COLOR = 0x99000000;
@@ -163,8 +168,13 @@
/** Whether we can use NO_HIDE_DESCENDANTS accessibility importance. */
private static final boolean CAN_HIDE_DESCENDANTS = Build.VERSION.SDK_INT >= 19;
+ /** Whether the drawer shadow comes from setting elevation on the drawer. */
+ private static final boolean SET_DRAWER_SHADOW_FROM_ELEVATION =
+ Build.VERSION.SDK_INT >= 21;
+
private final ChildAccessibilityDelegate mChildAccessibilityDelegate =
new ChildAccessibilityDelegate();
+ private float mDrawerElevation;
private int mMinDrawerMargin;
@@ -189,9 +199,9 @@
private float mInitialMotionX;
private float mInitialMotionY;
- private Drawable mShadowLeft;
- private Drawable mShadowRight;
private Drawable mStatusBarBackground;
+ private Drawable mShadowLeftResolved;
+ private Drawable mShadowRightResolved;
private CharSequence mTitleLeft;
private CharSequence mTitleRight;
@@ -199,6 +209,14 @@
private Object mLastInsets;
private boolean mDrawStatusBarBackground;
+ /** Shadow drawables for different gravity */
+ private Drawable mShadowStart = null;
+ private Drawable mShadowEnd = null;
+ private Drawable mShadowLeft = null;
+ private Drawable mShadowRight = null;
+
+ private final ArrayList<View> mNonDrawerViews;
+
/**
* Listener for monitoring events about drawers.
*/
@@ -361,6 +379,40 @@
IMPL.configureApplyInsets(this);
mStatusBarBackground = IMPL.getDefaultStatusBarBackground(context);
}
+
+ mDrawerElevation = DRAWER_ELEVATION * density;
+
+ mNonDrawerViews = new ArrayList<View>();
+ }
+
+ /**
+ * Sets the base elevation of the drawer(s) relative to the parent, in pixels. Note that the
+ * elevation change is only supported in API 21 and above.
+ *
+ * @param elevation The base depth position of the view, in pixels.
+ */
+ public void setDrawerElevation(float elevation) {
+ mDrawerElevation = elevation;
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (isDrawerView(child)) {
+ ViewCompat.setElevation(child, mDrawerElevation);
+ }
+ }
+ }
+
+ /**
+ * The base elevation of the drawer(s) relative to the parent, in pixels. Note that the
+ * elevation change is only supported in API 21 and above. For unsupported API levels, 0 will
+ * be returned as the elevation.
+ *
+ * @return The base depth position of the view, in pixels.
+ */
+ public float getDrawerElevation() {
+ if (SET_DRAWER_SHADOW_FROM_ELEVATION) {
+ return mDrawerElevation;
+ }
+ return 0f;
}
/**
@@ -376,8 +428,15 @@
}
/**
- * Set a simple drawable used for the left or right shadow.
- * The drawable provided must have a nonzero intrinsic width.
+ * Set a simple drawable used for the left or right shadow. The drawable provided must have a
+ * nonzero intrinsic width. For API 21 and above, an elevation will be set on the drawer
+ * instead of the drawable provided.
+ *
+ * <p>Note that for better support for both left-to-right and right-to-left layout
+ * directions, a drawable for RTL layout (in additional to the one in LTR layout) can be
+ * defined with a resource qualifier "ldrtl" for API 17 and above with the gravity
+ * {@link GravityCompat#START}. Alternatively, for API 23 and above, the drawable can
+ * auto-mirrored such that the drawable will be mirrored in RTL layout.</p>
*
* @param shadowDrawable Shadow drawable to use at the edge of a drawer
* @param gravity Which drawer the shadow should apply to
@@ -388,22 +447,35 @@
* They're probably nuts, but we might want to consider registering callbacks,
* setting states, etc. properly.
*/
-
- final int absGravity = GravityCompat.getAbsoluteGravity(gravity,
- ViewCompat.getLayoutDirection(this));
- if ((absGravity & Gravity.LEFT) == Gravity.LEFT) {
- mShadowLeft = shadowDrawable;
- invalidate();
+ if (SET_DRAWER_SHADOW_FROM_ELEVATION) {
+ // No op. Drawer shadow will come from setting an elevation on the drawer.
+ return;
}
- if ((absGravity & Gravity.RIGHT) == Gravity.RIGHT) {
+ if ((gravity & GravityCompat.START) == GravityCompat.START) {
+ mShadowStart = shadowDrawable;
+ } else if ((gravity & GravityCompat.END) == GravityCompat.END) {
+ mShadowEnd = shadowDrawable;
+ } else if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
+ mShadowLeft = shadowDrawable;
+ } else if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
mShadowRight = shadowDrawable;
- invalidate();
+ } else {
+ return;
}
+ resolveShadowDrawables();
+ invalidate();
}
/**
- * Set a simple drawable used for the left or right shadow.
- * The drawable provided must have a nonzero intrinsic width.
+ * Set a simple drawable used for the left or right shadow. The drawable provided must have a
+ * nonzero intrinsic width. For API 21 and above, an elevation will be set on the drawer
+ * instead of the drawable provided.
+ *
+ * <p>Note that for better support for both left-to-right and right-to-left layout
+ * directions, a drawable for RTL layout (in additional to the one in LTR layout) can be
+ * defined with a resource qualifier "ldrtl" for API 17 and above with the gravity
+ * {@link GravityCompat#START}. Alternatively, for API 23 and above, the drawable can
+ * auto-mirrored such that the drawable will be mirrored in RTL layout.</p>
*
* @param resId Resource id of a shadow drawable to use at the edge of a drawer
* @param gravity Which drawer the shadow should apply to
@@ -417,7 +489,7 @@
*
* @param color Color to use in 0xAARRGGBB format.
*/
- public void setScrimColor(int color) {
+ public void setScrimColor(@ColorInt int color) {
mScrimColor = color;
invalidate();
}
@@ -867,6 +939,11 @@
heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
child.measure(contentWidthSpec, contentHeightSpec);
} else if (isDrawerView(child)) {
+ if (SET_DRAWER_SHADOW_FROM_ELEVATION) {
+ if (ViewCompat.getElevation(child) != mDrawerElevation) {
+ ViewCompat.setElevation(child, mDrawerElevation);
+ }
+ }
final int childGravity =
getDrawerViewAbsoluteGravity(child) & Gravity.HORIZONTAL_GRAVITY_MASK;
if ((foundDrawers & childGravity) != 0) {
@@ -889,6 +966,65 @@
}
}
+ private void resolveShadowDrawables() {
+ if (SET_DRAWER_SHADOW_FROM_ELEVATION) {
+ return;
+ }
+ mShadowLeftResolved = resolveLeftShadow();
+ mShadowRightResolved = resolveRightShadow();
+ }
+
+ private Drawable resolveLeftShadow() {
+ int layoutDirection = ViewCompat.getLayoutDirection(this);
+ // Prefer shadows defined with start/end gravity over left and right.
+ if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {
+ if (mShadowStart != null) {
+ // Correct drawable layout direction, if needed.
+ mirror(mShadowStart, layoutDirection);
+ return mShadowStart;
+ }
+ } else {
+ if (mShadowEnd != null) {
+ // Correct drawable layout direction, if needed.
+ mirror(mShadowEnd, layoutDirection);
+ return mShadowEnd;
+ }
+ }
+ return mShadowLeft;
+ }
+
+ private Drawable resolveRightShadow() {
+ int layoutDirection = ViewCompat.getLayoutDirection(this);
+ if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {
+ if (mShadowEnd != null) {
+ // Correct drawable layout direction, if needed.
+ mirror(mShadowEnd, layoutDirection);
+ return mShadowEnd;
+ }
+ } else {
+ if (mShadowStart != null) {
+ // Correct drawable layout direction, if needed.
+ mirror(mShadowStart, layoutDirection);
+ return mShadowStart;
+ }
+ }
+ return mShadowRight;
+ }
+
+ /**
+ * Change the layout direction of the given drawable.
+ * Return true if auto-mirror is supported and drawable's layout direction can be changed.
+ * Otherwise, return false.
+ */
+ private boolean mirror(Drawable drawable, int layoutDirection) {
+ if (drawable == null || !DrawableCompat.isAutoMirrored(drawable)) {
+ return false;
+ }
+
+ DrawableCompat.setLayoutDirection(drawable, layoutDirection);
+ return true;
+ }
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mInLayout = true;
@@ -1042,11 +1178,15 @@
* @param color Color to use as a background drawable to draw behind the status bar
* in 0xAARRGGBB format.
*/
- public void setStatusBarBackgroundColor(int color) {
+ public void setStatusBarBackgroundColor(@ColorInt int color) {
mStatusBarBackground = new ColorDrawable(color);
invalidate();
}
+ public void onRtlPropertiesChanged(int layoutDirection) {
+ resolveShadowDrawables();
+ }
+
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
@@ -1096,27 +1236,29 @@
mScrimPaint.setColor(color);
canvas.drawRect(clipLeft, 0, clipRight, getHeight(), mScrimPaint);
- } else if (mShadowLeft != null && checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {
- final int shadowWidth = mShadowLeft.getIntrinsicWidth();
+ } else if (mShadowLeftResolved != null
+ && checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {
+ final int shadowWidth = mShadowLeftResolved.getIntrinsicWidth();
final int childRight = child.getRight();
final int drawerPeekDistance = mLeftDragger.getEdgeSize();
final float alpha =
Math.max(0, Math.min((float) childRight / drawerPeekDistance, 1.f));
- mShadowLeft.setBounds(childRight, child.getTop(),
+ mShadowLeftResolved.setBounds(childRight, child.getTop(),
childRight + shadowWidth, child.getBottom());
- mShadowLeft.setAlpha((int) (0xff * alpha));
- mShadowLeft.draw(canvas);
- } else if (mShadowRight != null && checkDrawerViewAbsoluteGravity(child, Gravity.RIGHT)) {
- final int shadowWidth = mShadowRight.getIntrinsicWidth();
+ mShadowLeftResolved.setAlpha((int) (0xff * alpha));
+ mShadowLeftResolved.draw(canvas);
+ } else if (mShadowRightResolved != null
+ && checkDrawerViewAbsoluteGravity(child, Gravity.RIGHT)) {
+ final int shadowWidth = mShadowRightResolved.getIntrinsicWidth();
final int childLeft = child.getLeft();
final int showing = getWidth() - childLeft;
final int drawerPeekDistance = mRightDragger.getEdgeSize();
final float alpha =
Math.max(0, Math.min((float) showing / drawerPeekDistance, 1.f));
- mShadowRight.setBounds(childLeft - shadowWidth, child.getTop(),
+ mShadowRightResolved.setBounds(childLeft - shadowWidth, child.getTop(),
childLeft, child.getBottom());
- mShadowRight.setAlpha((int) (0xff * alpha));
- mShadowRight.draw(canvas);
+ mShadowRightResolved.setAlpha((int) (0xff * alpha));
+ mShadowRightResolved.draw(canvas);
}
return result;
}
@@ -1465,6 +1607,41 @@
return new LayoutParams(getContext(), attrs);
}
+ @Override
+ public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+ if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
+ return;
+ }
+
+ // Only the views in the open drawers are focusables. Add normal child views when
+ // no drawers are opened.
+ final int childCount = getChildCount();
+ boolean isDrawerOpen = false;
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ if (isDrawerView(child)) {
+ if (isDrawerOpen(child)) {
+ isDrawerOpen = true;
+ child.addFocusables(views, direction, focusableMode);
+ }
+ } else {
+ mNonDrawerViews.add(child);
+ }
+ }
+
+ if (!isDrawerOpen) {
+ final int nonDrawerViewsCount = mNonDrawerViews.size();
+ for (int i = 0; i < nonDrawerViewsCount; ++i) {
+ final View child = mNonDrawerViews.get(i);
+ if (child.getVisibility() == View.VISIBLE) {
+ child.addFocusables(views, direction, focusableMode);
+ }
+ }
+ }
+
+ mNonDrawerViews.clear();
+ }
+
private boolean hasVisibleDrawer() {
return findVisibleDrawer() != null;
}
@@ -1857,6 +2034,8 @@
// itself as focusable to accessibility services.
info.setFocusable(false);
info.setFocused(false);
+ info.removeAction(AccessibilityActionCompat.ACTION_FOCUS);
+ info.removeAction(AccessibilityActionCompat.ACTION_CLEAR_FOCUS);
}
@Override
diff -Nur support-v4-22.2.1/android/support/v4/widget/ExploreByTouchHelper.java support-v4-23.0.0/android/support/v4/widget/ExploreByTouchHelper.java
--- support-v4-22.2.1/android/support/v4/widget/ExploreByTouchHelper.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/widget/ExploreByTouchHelper.java 2015-06-23 08:43:44.000000000 +0900
@@ -45,11 +45,11 @@
* and managing accessibility focus. This class does not currently support
* hierarchies of logical items.
* <p>
- * This should be applied to the parent view using
- * {@link ViewCompat#setAccessibilityDelegate}:
+ * Clients should override abstract methods on this class and attach it to the
+ * host view using {@link ViewCompat#setAccessibilityDelegate}:
*
* <pre>
- * mAccessHelper = ExploreByTouchHelper.create(someView, mAccessHelperCallback);
+ * mAccessHelper = new MyExploreByTouchHelper(someView);
* ViewCompat.setAccessibilityDelegate(someView, mAccessHelper);
* </pre>
*/
@@ -57,6 +57,9 @@
/** Virtual node identifier value for invalid nodes. */
public static final int INVALID_ID = Integer.MIN_VALUE;
+ /** Virtual node identifier value for the host view's node. */
+ 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();
@@ -191,7 +194,7 @@
* parent view.
*/
public void invalidateRoot() {
- invalidateVirtualView(View.NO_ID);
+ invalidateVirtualView(HOST_ID);
}
/**
@@ -243,7 +246,7 @@
/**
* Constructs and returns an {@link AccessibilityEvent} for the specified
- * virtual view id, which includes the host view ({@link View#NO_ID}).
+ * 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.
@@ -253,7 +256,7 @@
*/
private AccessibilityEvent createEvent(int virtualViewId, int eventType) {
switch (virtualViewId) {
- case View.NO_ID:
+ case HOST_ID:
return createEventForHost(eventType);
default:
return createEventForChild(virtualViewId, eventType);
@@ -309,7 +312,7 @@
/**
* Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
* specified virtual view id, which includes the host view
- * ({@link View#NO_ID}).
+ * ({@link #HOST_ID}).
*
* @param virtualViewId The virtual view id for the item for which to
* construct a node.
@@ -318,7 +321,7 @@
*/
private AccessibilityNodeInfoCompat createNode(int virtualViewId) {
switch (virtualViewId) {
- case View.NO_ID:
+ case HOST_ID:
return createNodeForHost();
default:
return createNodeForChild(virtualViewId);
@@ -335,6 +338,9 @@
final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain(mView);
ViewCompat.onInitializeAccessibilityNodeInfo(mView, node);
+ // Allow the client to populate the host node.
+ onPopulateNodeForHost(node);
+
// Add the virtual descendants.
final LinkedList<Integer> virtualViewIds = new LinkedList<Integer>();
getVisibleVirtualViews(virtualViewIds);
@@ -439,7 +445,7 @@
private boolean performAction(int virtualViewId, int action, Bundle arguments) {
switch (virtualViewId) {
- case View.NO_ID:
+ case HOST_ID:
return performActionForHost(action, arguments);
default:
return performActionForChild(virtualViewId, action, arguments);
@@ -542,7 +548,15 @@
}
// TODO: Check virtual view visibility.
if (!isAccessibilityFocused(virtualViewId)) {
+ // Clear focus from the previously focused view, if applicable.
+ if (mFocusedVirtualViewId != INVALID_ID) {
+ sendEventForVirtualView(mFocusedVirtualViewId,
+ AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+ }
+
+ // Set focus on the new view.
mFocusedVirtualViewId = virtualViewId;
+
// TODO: Only invalidate virtual view bounds.
mView.invalidate();
sendEventForVirtualView(virtualViewId,
@@ -577,7 +591,7 @@
* @param x The view-relative x coordinate
* @param y The view-relative y coordinate
* @return virtual view identifier for the logical item under
- * coordinates (x,y) or {@link View#NO_ID} if there is no item at
+ * coordinates (x,y) or {@link #HOST_ID} if there is no item at
* the given coordinates
*/
protected abstract int getVirtualViewAt(float x, float y);
@@ -683,6 +697,17 @@
int virtualViewId, AccessibilityNodeInfoCompat node);
/**
+ * 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:
+ */
+ public void onPopulateNodeForHost(AccessibilityNodeInfoCompat node) {
+ // Default implementation is no-op.
+ }
+
+ /**
* Performs the specified accessibility action on the item associated
* with the virtual view identifier. See
* {@link AccessibilityNodeInfoCompat#performAction(int, Bundle)} for
diff -Nur support-v4-22.2.1/android/support/v4/widget/NestedScrollView.java support-v4-23.0.0/android/support/v4/widget/NestedScrollView.java
--- support-v4-22.2.1/android/support/v4/widget/NestedScrollView.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/widget/NestedScrollView.java 2015-07-15 08:50:04.000000000 +0900
@@ -1636,6 +1636,7 @@
mIsBeingDragged = false;
recycleVelocityTracker();
+ stopNestedScroll();
if (mEdgeGlowTop != null) {
mEdgeGlowTop.onRelease();
diff -Nur support-v4-22.2.1/android/support/v4/widget/PopupWindowCompat.java support-v4-23.0.0/android/support/v4/widget/PopupWindowCompat.java
--- support-v4-22.2.1/android/support/v4/widget/PopupWindowCompat.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/widget/PopupWindowCompat.java 2015-06-23 08:43:44.000000000 +0900
@@ -17,7 +17,7 @@
package android.support.v4.widget;
import android.view.View;
-import android.view.View.OnTouchListener;
+import android.view.WindowManager;
import android.widget.PopupWindow;
/**
@@ -29,8 +29,11 @@
* Interface for the full API.
*/
interface PopupWindowImpl {
- public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff,
- int gravity);
+ void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, int gravity);
+ void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor);
+ boolean getOverlapAnchor(PopupWindow popupWindow);
+ void setWindowLayoutType(PopupWindow popupWindow, int layoutType);
+ int getWindowLayoutType(PopupWindow popupWindow);
}
/**
@@ -42,12 +45,47 @@
int gravity) {
popup.showAsDropDown(anchor, xoff, yoff);
}
+
+ @Override
+ public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
+ // noop
+ }
+
+ @Override
+ public boolean getOverlapAnchor(PopupWindow popupWindow) {
+ return false;
+ }
+
+ @Override
+ public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
+ // no-op
+ }
+
+ @Override
+ public int getWindowLayoutType(PopupWindow popupWindow) {
+ return 0;
+ }
+ }
+
+ /**
+ * Interface implementation that doesn't use anything above v4 APIs.
+ */
+ static class GingerbreadPopupWindowImpl extends BasePopupWindowImpl {
+ @Override
+ public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
+ PopupWindowCompatGingerbread.setWindowLayoutType(popupWindow, layoutType);
+ }
+
+ @Override
+ public int getWindowLayoutType(PopupWindow popupWindow) {
+ return PopupWindowCompatGingerbread.getWindowLayoutType(popupWindow);
+ }
}
/**
* Interface implementation for devices with at least KitKat APIs.
*/
- static class KitKatPopupWindowImpl extends BasePopupWindowImpl {
+ static class KitKatPopupWindowImpl extends GingerbreadPopupWindowImpl {
@Override
public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff,
int gravity) {
@@ -55,14 +93,54 @@
}
}
+ static class Api21PopupWindowImpl extends KitKatPopupWindowImpl {
+ @Override
+ public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
+ PopupWindowCompatApi21.setOverlapAnchor(popupWindow, overlapAnchor);
+ }
+
+ @Override
+ public boolean getOverlapAnchor(PopupWindow popupWindow) {
+ return PopupWindowCompatApi21.getOverlapAnchor(popupWindow);
+ }
+ }
+
+ static class Api23PopupWindowImpl extends Api21PopupWindowImpl {
+ @Override
+ public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
+ PopupWindowCompatApi23.setOverlapAnchor(popupWindow, overlapAnchor);
+ }
+
+ @Override
+ public boolean getOverlapAnchor(PopupWindow popupWindow) {
+ return PopupWindowCompatApi23.getOverlapAnchor(popupWindow);
+ }
+
+ @Override
+ public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
+ PopupWindowCompatApi23.setWindowLayoutType(popupWindow, layoutType);
+ }
+
+ @Override
+ public int getWindowLayoutType(PopupWindow popupWindow) {
+ return PopupWindowCompatApi23.getWindowLayoutType(popupWindow);
+ }
+ }
+
/**
* Select the correct implementation to use for the current platform.
*/
static final PopupWindowImpl IMPL;
static {
final int version = android.os.Build.VERSION.SDK_INT;
- if (version >= 19) {
+ if (version >= 23) {
+ IMPL = new Api23PopupWindowImpl();
+ } else if (version >= 21) {
+ IMPL = new Api21PopupWindowImpl();
+ } else if (version >= 19) {
IMPL = new KitKatPopupWindowImpl();
+ } else if (version >= 9) {
+ IMPL = new GingerbreadPopupWindowImpl();
} else {
IMPL = new BasePopupWindowImpl();
}
@@ -92,4 +170,46 @@
int gravity) {
IMPL.showAsDropDown(popup, anchor, xoff, yoff, gravity);
}
+
+ /**
+ * Sets whether the popup window should overlap its anchor view when
+ * displayed as a drop-down.
+ *
+ * @param overlapAnchor Whether the popup should overlap its anchor.
+ */
+ public static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
+ IMPL.setOverlapAnchor(popupWindow, overlapAnchor);
+ }
+
+ /**
+ * Returns whether the popup window should overlap its anchor view when
+ * displayed as a drop-down.
+ *
+ * @return Whether the popup should overlap its anchor.
+ */
+ public static boolean getOverlapAnchor(PopupWindow popupWindow) {
+ return IMPL.getOverlapAnchor(popupWindow);
+ }
+
+ /**
+ * Set the layout type for this window. This value will be passed through to
+ * {@link WindowManager.LayoutParams#type} therefore the value should match any value
+ * {@link WindowManager.LayoutParams#type} accepts.
+ *
+ * @param layoutType Layout type for this window.
+ *
+ * @see WindowManager.LayoutParams#type
+ */
+ public static void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
+ IMPL.setWindowLayoutType(popupWindow, layoutType);
+ }
+
+ /**
+ * Returns the layout type for this window.
+ *
+ * @see #setWindowLayoutType(PopupWindow popupWindow, int)
+ */
+ public static int getWindowLayoutType(PopupWindow popupWindow) {
+ return IMPL.getWindowLayoutType(popupWindow);
+ }
}
diff -Nur support-v4-22.2.1/android/support/v4/widget/PopupWindowCompatApi21.java support-v4-23.0.0/android/support/v4/widget/PopupWindowCompatApi21.java
--- support-v4-22.2.1/android/support/v4/widget/PopupWindowCompatApi21.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/widget/PopupWindowCompatApi21.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,60 @@
+/*
+ * 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.util.Log;
+import android.widget.PopupWindow;
+
+import java.lang.reflect.Field;
+
+class PopupWindowCompatApi21 {
+
+ private static final String TAG = "PopupWindowCompatApi21";
+
+ private static Field sOverlapAnchorField;
+
+ static {
+ try {
+ sOverlapAnchorField = PopupWindow.class.getDeclaredField("mOverlapAnchor");
+ sOverlapAnchorField.setAccessible(true);
+ } catch (NoSuchFieldException e) {
+ Log.i(TAG, "Could not fetch mOverlapAnchor field from PopupWindow", e);
+ }
+ }
+
+ static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
+ if (sOverlapAnchorField != null) {
+ try {
+ sOverlapAnchorField.set(popupWindow, overlapAnchor);
+ } catch (IllegalAccessException e) {
+ Log.i(TAG, "Could not set overlap anchor field in PopupWindow", e);
+ }
+ }
+ }
+
+ static boolean getOverlapAnchor(PopupWindow popupWindow) {
+ if (sOverlapAnchorField != null) {
+ try {
+ return (Boolean) sOverlapAnchorField.get(popupWindow);
+ } catch (IllegalAccessException e) {
+ Log.i(TAG, "Could not get overlap anchor field in PopupWindow", e);
+ }
+ }
+ return false;
+ }
+
+}
diff -Nur support-v4-22.2.1/android/support/v4/widget/PopupWindowCompatApi23.java support-v4-23.0.0/android/support/v4/widget/PopupWindowCompatApi23.java
--- support-v4-22.2.1/android/support/v4/widget/PopupWindowCompatApi23.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/widget/PopupWindowCompatApi23.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,39 @@
+/*
+ * 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.widget.PopupWindow;
+
+class PopupWindowCompatApi23 {
+
+ static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
+ popupWindow.setOverlapAnchor(overlapAnchor);
+ }
+
+ static boolean getOverlapAnchor(PopupWindow popupWindow) {
+ return popupWindow.getOverlapAnchor();
+ }
+
+ static void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
+ popupWindow.setWindowLayoutType(layoutType);
+ }
+
+ static int getWindowLayoutType(PopupWindow popupWindow) {
+ return popupWindow.getWindowLayoutType();
+ }
+
+}
diff -Nur support-v4-22.2.1/android/support/v4/widget/PopupWindowCompatGingerbread.java support-v4-23.0.0/android/support/v4/widget/PopupWindowCompatGingerbread.java
--- support-v4-22.2.1/android/support/v4/widget/PopupWindowCompatGingerbread.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/widget/PopupWindowCompatGingerbread.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,76 @@
+/*
+ * 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.widget.PopupWindow;
+
+import java.lang.reflect.Method;
+
+/**
+ * Implementation of PopupWindow compatibility that can call Gingerbread APIs.
+ */
+class PopupWindowCompatGingerbread {
+
+ private static Method sSetWindowLayoutTypeMethod;
+ private static boolean sSetWindowLayoutTypeMethodAttempted;
+ private static Method sGetWindowLayoutTypeMethod;
+ private static boolean sGetWindowLayoutTypeMethodAttempted;
+
+ static void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
+ if (!sSetWindowLayoutTypeMethodAttempted) {
+ try {
+ sSetWindowLayoutTypeMethod = PopupWindow.class.getDeclaredMethod(
+ "setWindowLayoutType", int.class);
+ sSetWindowLayoutTypeMethod.setAccessible(true);
+ } catch (Exception e) {
+ // Reflection method fetch failed. Oh well.
+ }
+ sSetWindowLayoutTypeMethodAttempted = true;
+ }
+
+ if (sSetWindowLayoutTypeMethod != null) {
+ try {
+ sSetWindowLayoutTypeMethod.invoke(popupWindow, layoutType);
+ } catch (Exception e) {
+ // Reflection call failed. Oh well.
+ }
+ }
+ }
+
+ static int getWindowLayoutType(PopupWindow popupWindow) {
+ if (!sGetWindowLayoutTypeMethodAttempted) {
+ try {
+ sGetWindowLayoutTypeMethod = PopupWindow.class.getDeclaredMethod(
+ "getWindowLayoutType");
+ sGetWindowLayoutTypeMethod.setAccessible(true);
+ } catch (Exception e) {
+ // Reflection method fetch failed. Oh well.
+ }
+ sGetWindowLayoutTypeMethodAttempted = true;
+ }
+
+ if (sGetWindowLayoutTypeMethod != null) {
+ try {
+ return (Integer) sGetWindowLayoutTypeMethod.invoke(popupWindow);
+ } catch (Exception e) {
+ // Reflection call failed. Oh well.
+ }
+ }
+ return 0;
+ }
+
+}
diff -Nur support-v4-22.2.1/android/support/v4/widget/SlidingPaneLayout.java support-v4-23.0.0/android/support/v4/widget/SlidingPaneLayout.java
--- support-v4-22.2.1/android/support/v4/widget/SlidingPaneLayout.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/widget/SlidingPaneLayout.java 2015-06-23 08:43:44.000000000 +0900
@@ -29,6 +29,7 @@
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.annotation.ColorInt;
import android.support.annotation.DrawableRes;
import android.support.v4.view.AccessibilityDelegateCompat;
import android.support.v4.view.MotionEventCompat;
@@ -297,13 +298,14 @@
*
* @param color An ARGB-packed color value
*/
- public void setSliderFadeColor(int color) {
+ public void setSliderFadeColor(@ColorInt int color) {
mSliderFadeColor = color;
}
/**
* @return The ARGB-packed color value used to fade the sliding pane
*/
+ @ColorInt
public int getSliderFadeColor() {
return mSliderFadeColor;
}
@@ -314,13 +316,14 @@
*
* @param color An ARGB-packed color value
*/
- public void setCoveredFadeColor(int color) {
+ public void setCoveredFadeColor(@ColorInt int color) {
mCoveredFadeColor = color;
}
/**
* @return The ARGB-packed color value used to fade the fixed pane
*/
+ @ColorInt
public int getCoveredFadeColor() {
return mCoveredFadeColor;
}
diff -Nur support-v4-22.2.1/android/support/v4/widget/SwipeRefreshLayout.java support-v4-23.0.0/android/support/v4/widget/SwipeRefreshLayout.java
--- support-v4-22.2.1/android/support/v4/widget/SwipeRefreshLayout.java 2015-07-17 03:08:30.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/widget/SwipeRefreshLayout.java 2015-06-23 08:43:44.000000000 +0900
@@ -19,8 +19,13 @@
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.support.annotation.ColorInt;
+import android.support.annotation.ColorRes;
import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.ScrollingView;
+import android.support.v4.view.NestedScrollingChild;
+import android.support.v4.view.NestedScrollingChildHelper;
+import android.support.v4.view.NestedScrollingParent;
+import android.support.v4.view.NestedScrollingParentHelper;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -56,7 +61,8 @@
* refresh of the content wherever this gesture is used.
* </p>
*/
-public class SwipeRefreshLayout extends ViewGroup {
+public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingParent,
+ NestedScrollingChild {
// Maps to ProgressBar.Large style
public static final int LARGE = MaterialProgressDrawable.LARGE;
// Maps to ProgressBar default style
@@ -96,6 +102,14 @@
private boolean mRefreshing = false;
private int mTouchSlop;
private float mTotalDragDistance = -1;
+ // If nested scrolling is enabled, the total amount that needed to be
+ // consumed by this as the nested scrolling parent is used in place of the
+ // overscroll determined by MOVE events in the onTouch handler
+ private float mTotalUnconsumed;
+ private final NestedScrollingParentHelper mNestedScrollingParentHelper;
+ private final NestedScrollingChildHelper mNestedScrollingChildHelper;
+ private final int[] mParentScrollConsumed = new int[2];
+
private int mMediumAnimationDuration;
private int mCurrentTargetOffsetTop;
// Whether or not the starting offset has been determined.
@@ -290,6 +304,10 @@
// the absolute offset has to take into account that the circle starts at an offset
mSpinnerFinalOffset = DEFAULT_CIRCLE_TARGET * metrics.density;
mTotalDragDistance = mSpinnerFinalOffset;
+ mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
+
+ mNestedScrollingChildHelper = new NestedScrollingChildHelper(this);
+ setNestedScrollingEnabled(true);
}
protected int getChildDrawingOrder(int childCount, int i) {
@@ -460,7 +478,7 @@
*
* @param colorRes Resource id of the color.
*/
- public void setProgressBackgroundColorSchemeResource(int colorRes) {
+ public void setProgressBackgroundColorSchemeResource(@ColorRes int colorRes) {
setProgressBackgroundColorSchemeColor(getResources().getColor(colorRes));
}
@@ -469,7 +487,7 @@
*
* @param color
*/
- public void setProgressBackgroundColorSchemeColor(int color) {
+ public void setProgressBackgroundColorSchemeColor(@ColorInt int color) {
mCircleView.setBackgroundColor(color);
mProgress.setBackgroundColor(color);
}
@@ -478,7 +496,7 @@
* @deprecated Use {@link #setColorSchemeResources(int...)}
*/
@Deprecated
- public void setColorScheme(int... colors) {
+ public void setColorScheme(@ColorInt int... colors) {
setColorSchemeResources(colors);
}
@@ -489,7 +507,7 @@
*
* @param colorResIds
*/
- public void setColorSchemeResources(int... colorResIds) {
+ public void setColorSchemeResources(@ColorRes int... colorResIds) {
final Resources res = getResources();
int[] colorRes = new int[colorResIds.length];
for (int i = 0; i < colorResIds.length; i++) {
@@ -505,6 +523,7 @@
*
* @param colors
*/
+ @ColorInt
public void setColorSchemeColors(int... colors) {
ensureTarget();
mProgress.setColorSchemeColors(colors);
@@ -694,13 +713,233 @@
@Override
public void requestDisallowInterceptTouchEvent(boolean b) {
- // Nope.
+ // if this is a List < L or another view that doesn't support nested
+ // scrolling, ignore this request so that the vertical scroll event
+ // isn't stolen
+ if ((android.os.Build.VERSION.SDK_INT < 21 && mTarget instanceof AbsListView)
+ || (mTarget != null && !ViewCompat.isNestedScrollingEnabled(mTarget))) {
+ // Nope.
+ } else {
+ super.requestDisallowInterceptTouchEvent(b);
+ }
+ }
+
+ // NestedScrollingParent
+
+ @Override
+ public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
+ if (isEnabled() && (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0) {
+ // Dispatch up to the nested parent
+ startNestedScroll(nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onNestedScrollAccepted(View child, View target, int axes) {
+ // Reset the counter of how much leftover scroll needs to be consumed.
+ mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes);
+ mTotalUnconsumed = 0;
+ }
+
+ @Override
+ public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
+ // If we are in the middle of consuming, a scroll, then we want to move the spinner back up
+ // before allowing the list to scroll
+ if (dy > 0 && mTotalUnconsumed > 0) {
+ if (dy > mTotalUnconsumed) {
+ consumed[1] = dy - (int) mTotalUnconsumed;
+ mTotalUnconsumed = 0;
+ } else {
+ mTotalUnconsumed -= dy;
+ consumed[1] = dy;
+
+ }
+ moveSpinner(mTotalUnconsumed);
+ }
+
+ // Now let our nested parent consume the leftovers
+ final int[] parentConsumed = mParentScrollConsumed;
+ if (dispatchNestedPreScroll(dx - consumed[0], dy - consumed[1], parentConsumed, null)) {
+ consumed[0] += parentConsumed[0];
+ consumed[1] += parentConsumed[1];
+ }
+ }
+
+ @Override
+ public int getNestedScrollAxes() {
+ return mNestedScrollingParentHelper.getNestedScrollAxes();
+ }
+
+ @Override
+ public void onStopNestedScroll(View target) {
+ mNestedScrollingParentHelper.onStopNestedScroll(target);
+ // Finish the spinner for nested scrolling if we ever consumed any
+ // unconsumed nested scroll
+ if (mTotalUnconsumed > 0) {
+ finishSpinner(mTotalUnconsumed);
+ mTotalUnconsumed = 0;
+ }
+ // Dispatch up our nested parent
+ stopNestedScroll();
+ }
+
+ @Override
+ public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
+ int dyUnconsumed) {
+ if (dyUnconsumed < 0) {
+ dyUnconsumed = Math.abs(dyUnconsumed);
+ mTotalUnconsumed += dyUnconsumed;
+ moveSpinner(mTotalUnconsumed);
+ }
+ // Dispatch up to the nested parent
+ dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dxConsumed, null);
+ }
+
+ // NestedScrollingChild
+
+ @Override
+ public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+ return false;
+ }
+
+ @Override
+ public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
+ return false;
+ }
+
+ @Override
+ public void setNestedScrollingEnabled(boolean enabled) {
+ mNestedScrollingChildHelper.setNestedScrollingEnabled(enabled);
+ }
+
+ @Override
+ public boolean isNestedScrollingEnabled() {
+ return mNestedScrollingChildHelper.isNestedScrollingEnabled();
+ }
+
+ @Override
+ public boolean startNestedScroll(int axes) {
+ return mNestedScrollingChildHelper.startNestedScroll(axes);
+ }
+
+ @Override
+ public void stopNestedScroll() {
+ mNestedScrollingChildHelper.stopNestedScroll();
+ }
+
+ @Override
+ public boolean hasNestedScrollingParent() {
+ return mNestedScrollingChildHelper.hasNestedScrollingParent();
+ }
+
+ @Override
+ public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
+ int dyUnconsumed, int[] offsetInWindow) {
+ return mNestedScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed,
+ dxUnconsumed, dyUnconsumed, offsetInWindow);
+ }
+
+ @Override
+ public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
+ return mNestedScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
+ }
+
+ @Override
+ public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
+ return mNestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
+ }
+
+ @Override
+ public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
+ return mNestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
}
private boolean isAnimationRunning(Animation animation) {
return animation != null && animation.hasStarted() && !animation.hasEnded();
}
+ private void moveSpinner(float overscrollTop) {
+ mProgress.showArrow(true);
+ float originalDragPercent = overscrollTop / mTotalDragDistance;
+
+ float dragPercent = Math.min(1f, Math.abs(originalDragPercent));
+ float adjustedPercent = (float) Math.max(dragPercent - .4, 0) * 5 / 3;
+ float extraOS = Math.abs(overscrollTop) - mTotalDragDistance;
+ float slingshotDist = mUsingCustomStart ? mSpinnerFinalOffset - mOriginalOffsetTop
+ : mSpinnerFinalOffset;
+ float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, slingshotDist * 2)
+ / slingshotDist);
+ float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow(
+ (tensionSlingshotPercent / 4), 2)) * 2f;
+ float extraMove = (slingshotDist) * tensionPercent * 2;
+
+ int targetY = mOriginalOffsetTop + (int) ((slingshotDist * dragPercent) + extraMove);
+ // where 1.0f is a full circle
+ if (mCircleView.getVisibility() != View.VISIBLE) {
+ mCircleView.setVisibility(View.VISIBLE);
+ }
+ if (!mScale) {
+ ViewCompat.setScaleX(mCircleView, 1f);
+ ViewCompat.setScaleY(mCircleView, 1f);
+ }
+ if (overscrollTop < mTotalDragDistance) {
+ if (mScale) {
+ setAnimationProgress(overscrollTop / mTotalDragDistance);
+ }
+ if (mProgress.getAlpha() > STARTING_PROGRESS_ALPHA
+ && !isAnimationRunning(mAlphaStartAnimation)) {
+ // Animate the alpha
+ startProgressAlphaStartAnimation();
+ }
+ float strokeStart = adjustedPercent * .8f;
+ mProgress.setStartEndTrim(0f, Math.min(MAX_PROGRESS_ANGLE, strokeStart));
+ mProgress.setArrowScale(Math.min(1f, adjustedPercent));
+ } else {
+ if (mProgress.getAlpha() < MAX_ALPHA && !isAnimationRunning(mAlphaMaxAnimation)) {
+ // Animate the alpha
+ startProgressAlphaMaxAnimation();
+ }
+ }
+ float rotation = (-0.25f + .4f * adjustedPercent + tensionPercent * 2) * .5f;
+ mProgress.setProgressRotation(rotation);
+ setTargetOffsetTopAndBottom(targetY - mCurrentTargetOffsetTop, true /* requires update */);
+ }
+
+ private void finishSpinner(float overscrollTop) {
+ if (overscrollTop > mTotalDragDistance) {
+ setRefreshing(true, true /* notify */);
+ } else {
+ // cancel refresh
+ mRefreshing = false;
+ mProgress.setStartEndTrim(0f, 0f);
+ Animation.AnimationListener listener = null;
+ if (!mScale) {
+ listener = new Animation.AnimationListener() {
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ if (!mScale) {
+ startScaleDownAnimation(null);
+ }
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ };
+ }
+ animateOffsetToStartPosition(mCurrentTargetOffsetTop, listener);
+ mProgress.showArrow(false);
+ }
+ }
+
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
@@ -730,55 +969,11 @@
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE;
if (mIsBeingDragged) {
- mProgress.showArrow(true);
- float originalDragPercent = overscrollTop / mTotalDragDistance;
- if (originalDragPercent < 0) {
- return false;
- }
- float dragPercent = Math.min(1f, Math.abs(originalDragPercent));
- float adjustedPercent = (float) Math.max(dragPercent - .4, 0) * 5 / 3;
- float extraOS = Math.abs(overscrollTop) - mTotalDragDistance;
- float slingshotDist = mUsingCustomStart ? mSpinnerFinalOffset
- - mOriginalOffsetTop : mSpinnerFinalOffset;
- float tensionSlingshotPercent = Math.max(0,
- Math.min(extraOS, slingshotDist * 2) / slingshotDist);
- float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow(
- (tensionSlingshotPercent / 4), 2)) * 2f;
- float extraMove = (slingshotDist) * tensionPercent * 2;
-
- int targetY = mOriginalOffsetTop
- + (int) ((slingshotDist * dragPercent) + extraMove);
- // where 1.0f is a full circle
- if (mCircleView.getVisibility() != View.VISIBLE) {
- mCircleView.setVisibility(View.VISIBLE);
- }
- if (!mScale) {
- ViewCompat.setScaleX(mCircleView, 1f);
- ViewCompat.setScaleY(mCircleView, 1f);
- }
- if (overscrollTop < mTotalDragDistance) {
- if (mScale) {
- setAnimationProgress(overscrollTop / mTotalDragDistance);
- }
- if (mProgress.getAlpha() > STARTING_PROGRESS_ALPHA
- && !isAnimationRunning(mAlphaStartAnimation)) {
- // Animate the alpha
- startProgressAlphaStartAnimation();
- }
- float strokeStart = adjustedPercent * .8f;
- mProgress.setStartEndTrim(0f, Math.min(MAX_PROGRESS_ANGLE, strokeStart));
- mProgress.setArrowScale(Math.min(1f, adjustedPercent));
+ if (overscrollTop > 0) {
+ moveSpinner(overscrollTop);
} else {
- if (mProgress.getAlpha() < MAX_ALPHA
- && !isAnimationRunning(mAlphaMaxAnimation)) {
- // Animate the alpha
- startProgressAlphaMaxAnimation();
- }
+ return false;
}
- float rotation = (-0.25f + .4f * adjustedPercent + tensionPercent * 2) * .5f;
- mProgress.setProgressRotation(rotation);
- setTargetOffsetTopAndBottom(targetY - mCurrentTargetOffsetTop,
- true /* requires update */);
}
break;
}
@@ -804,36 +999,7 @@
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE;
mIsBeingDragged = false;
- if (overscrollTop > mTotalDragDistance) {
- setRefreshing(true, true /* notify */);
- } else {
- // cancel refresh
- mRefreshing = false;
- mProgress.setStartEndTrim(0f, 0f);
- Animation.AnimationListener listener = null;
- if (!mScale) {
- listener = new Animation.AnimationListener() {
-
- @Override
- public void onAnimationStart(Animation animation) {
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- if (!mScale) {
- startScaleDownAnimation(null);
- }
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- }
-
- };
- }
- animateOffsetToStartPosition(mCurrentTargetOffsetTop, listener);
- mProgress.showArrow(false);
- }
+ finishSpinner(overscrollTop);
mActivePointerId = INVALID_POINTER;
return false;
}
@@ -854,6 +1020,18 @@
mCircleView.startAnimation(mAnimateToCorrectPosition);
}
+ private void peek(int from, AnimationListener listener) {
+ mFrom = from;
+ mPeek.reset();
+ mPeek.setDuration(500);
+ mPeek.setInterpolator(mDecelerateInterpolator);
+ if (listener != null) {
+ mCircleView.setAnimationListener(listener);
+ }
+ mCircleView.clearAnimation();
+ mCircleView.startAnimation(mPeek);
+ }
+
private void animateOffsetToStartPosition(int from, AnimationListener listener) {
if (mScale) {
// Scale the item back down
@@ -888,6 +1066,23 @@
}
};
+ private final Animation mPeek = new Animation() {
+ @Override
+ public void applyTransformation(float interpolatedTime, Transformation t) {
+ int targetTop = 0;
+ int endTarget = 0;
+ if (!mUsingCustomStart) {
+ endTarget = (int) (mSpinnerFinalOffset - Math.abs(mOriginalOffsetTop));
+ } else {
+ endTarget = (int) mSpinnerFinalOffset; //mSpinnerFinalOffset;
+ }
+ targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime));
+ int offset = targetTop - mCircleView.getTop();
+ setTargetOffsetTopAndBottom(offset, false /* requires update */);
+ mProgress.setArrowScale(1 - interpolatedTime);
+ }
+ };
+
private void moveToStart(float interpolatedTime) {
int targetTop = 0;
targetTop = (mFrom + (int) ((mOriginalOffsetTop - mFrom) * interpolatedTime));
diff -Nur support-v4-22.2.1/android/support/v4/widget/TintableCompoundButton.java support-v4-23.0.0/android/support/v4/widget/TintableCompoundButton.java
--- support-v4-22.2.1/android/support/v4/widget/TintableCompoundButton.java 1970-01-01 09:00:00.000000000 +0900
+++ support-v4-23.0.0/android/support/v4/widget/TintableCompoundButton.java 2015-06-23 08:43:44.000000000 +0900
@@ -0,0 +1,70 @@
+/*
+ * 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.content.res.ColorStateList;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+
+/**
+ * Interface which allows a {@link android.widget.CompoundButton} to receive tinting
+ * calls from {@code CompoundButtonCompat} when running on API v20 devices or lower.
+ */
+public interface TintableCompoundButton {
+
+ /**
+ * Applies a tint to the button drawable. Does not modify the current tint
+ * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+ * <p>
+ * Subsequent calls to
+ * {@link android.widget.CompoundButton#setButtonDrawable(Drawable) setButtonDrawable(Drawable)}
+ * should automatically mutate the drawable and apply the specified tint and tint mode.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ */
+ public void setSupportButtonTintList(@Nullable ColorStateList tint);
+
+ /**
+ * Returns the tint applied to the button drawable
+ *
+ * @see #setSupportButtonTintList(ColorStateList)
+ */
+ @Nullable
+ public ColorStateList getSupportButtonTintList();
+
+ /**
+ * Specifies the blending mode which should be used to apply the tint specified by
+ * {@link #setSupportButtonTintList(ColorStateList)} to the button drawable. The
+ * default mode is {@link PorterDuff.Mode#SRC_IN}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ *
+ * @see #getSupportButtonTintMode()
+ * @see DrawableCompat#setTintMode(Drawable, PorterDuff.Mode)
+ */
+ public void setSupportButtonTintMode(@Nullable PorterDuff.Mode tintMode);
+
+ /**
+ * Returns the blending mode used to apply the tint to the button drawable
+ *
+ * @see #setSupportButtonTintMode(PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getSupportButtonTintMode();
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment