Created
August 18, 2015 07:44
-
-
Save zaki50/6cbe208bee2f8b2db3d2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 <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("<"); //$NON-NLS-1$ | |
+ break; | |
+ case '>': | |
+ sb.append(">"); //$NON-NLS-1$ | |
+ break; | |
+ case '&': | |
+ sb.append("&"); //$NON-NLS-1$ | |
+ break; | |
+ case '\'': | |
+ //http://www.w3.org/TR/xhtml1 | |
+ // The named character reference ' (the apostrophe, U+0027) was | |
+ // introduced in XML 1.0 but does not appear in HTML. Authors should | |
+ // therefore use ' instead of ' to work as expected in HTML 4 | |
+ // user agents. | |
+ sb.append("'"); //$NON-NLS-1$ | |
+ break; | |
+ case '"': | |
+ sb.append("""); //$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("<"); //$NON-NLS-1$ | |
- break; | |
- case '>': | |
- sb.append(">"); //$NON-NLS-1$ | |
- break; | |
- case '&': | |
- sb.append("&"); //$NON-NLS-1$ | |
- break; | |
- case '\'': | |
- //http://www.w3.org/TR/xhtml1 | |
- // The named character reference ' (the apostrophe, U+0027) was introduced in | |
- // XML 1.0 but does not appear in HTML. Authors should therefore use ' instead | |
- // of ' to work as expected in HTML 4 user agents. | |
- sb.append("'"); //$NON-NLS-1$ | |
- break; | |
- case '"': | |
- sb.append("""); //$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