diff options
Diffstat (limited to 'core/java/android')
72 files changed, 2561 insertions, 316 deletions
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java index 87ad49b..3f71d51 100644 --- a/core/java/android/animation/ObjectAnimator.java +++ b/core/java/android/animation/ObjectAnimator.java @@ -16,6 +16,7 @@ package android.animation; +import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Path; @@ -861,6 +862,7 @@ public final class ObjectAnimator extends ValueAnimator { * <p>Overriders of this method should call the superclass method to cause * internal mechanisms to be set up correctly.</p> */ + @CallSuper @Override void initAnimation() { if (!mInitialized) { @@ -961,6 +963,7 @@ public final class ObjectAnimator extends ValueAnimator { * * @param fraction The elapsed fraction of the animation. */ + @CallSuper @Override void animateValue(float fraction) { final Object target = getTarget(); diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index 118af64..85dc832 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -16,6 +16,7 @@ package android.animation; +import android.annotation.CallSuper; import android.os.Looper; import android.os.Trace; import android.util.AndroidRuntimeException; @@ -506,6 +507,7 @@ public class ValueAnimator extends Animator { * <p>Overrides of this method should call the superclass method to ensure * that internal mechanisms for the animation are set up correctly.</p> */ + @CallSuper void initAnimation() { if (!mInitialized) { int numValues = mValues.length; @@ -1375,6 +1377,7 @@ public class ValueAnimator extends Animator { * * @param fraction The elapsed fraction of the animation. */ + @CallSuper void animateValue(float fraction) { fraction = mInterpolator.getInterpolation(fraction); mCurrentFraction = fraction; diff --git a/core/java/android/annotation/CallSuper.java b/core/java/android/annotation/CallSuper.java new file mode 100644 index 0000000..82e2723 --- /dev/null +++ b/core/java/android/annotation/CallSuper.java @@ -0,0 +1,38 @@ +/* + * 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.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Denotes that any overriding methods should invoke this method as well. + * <p> + * Example: + * <pre>{@code + * @CallSuper + * public abstract void onFocusLost(); + * }</pre> + * + * @hide + */ +@Retention(SOURCE) +@Target({METHOD}) +public @interface CallSuper { +}
\ No newline at end of file diff --git a/core/java/android/annotation/CheckResult.java b/core/java/android/annotation/CheckResult.java new file mode 100644 index 0000000..787514e --- /dev/null +++ b/core/java/android/annotation/CheckResult.java @@ -0,0 +1,58 @@ +/* + * 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.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Denotes that the annotated method returns a result that it typically is + * an error to ignore. This is usually used for methods that have no side effect, + * so calling it without actually looking at the result usually means the developer + * has misunderstood what the method does. + * <p> + * Example: + * <pre>{@code + * public @CheckResult String trim(String s) { return s.trim(); } + * ... + * s.trim(); // this is probably an error + * s = s.trim(); // ok + * }</pre> + * + * @hide + */ +@Retention(SOURCE) +@Target({METHOD}) +public @interface CheckResult { + /** Defines the name of the suggested method to use instead, if applicable (using + * the same signature format as javadoc.) If there is more than one possibility, + * list them all separated by commas. + * <p> + * For example, ProcessBuilder has a method named {@code redirectErrorStream()} + * which sounds like it might redirect the error stream. It does not. It's just + * a getter which returns whether the process builder will redirect the error stream, + * and to actually set it, you must call {@code redirectErrorStream(boolean)}. + * In that case, the method should be defined like this: + * <pre> + * @CheckResult(suggest="#redirectErrorStream(boolean)") + * public boolean redirectErrorStream() { ... } + * </pre> + */ + String suggest() default ""; +}
\ No newline at end of file diff --git a/core/java/android/annotation/ColorInt.java b/core/java/android/annotation/ColorInt.java new file mode 100644 index 0000000..c4c93ee --- /dev/null +++ b/core/java/android/annotation/ColorInt.java @@ -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.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Denotes that the annotated element represents a packed color + * int, {@code AARRGGBB}. If applied to an int array, every element + * in the array represents a color integer. + * <p> + * public abstract void setTextColor(@ColorInt int color); + * }</pre> + * + * @hide + */ +@Retention(SOURCE) +@Target({PARAMETER,METHOD,LOCAL_VARIABLE,FIELD}) +public @interface ColorInt { +}
\ No newline at end of file diff --git a/core/java/android/annotation/FloatRange.java b/core/java/android/annotation/FloatRange.java new file mode 100644 index 0000000..3a7c150 --- /dev/null +++ b/core/java/android/annotation/FloatRange.java @@ -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.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Denotes that the annotated element should be a float or double in the given range + * <p> + * Example: + * <pre>{@code + * @FloatRange(from=0.0,to=1.0) + * public float getAlpha() { + * ... + * } + * }</pre> + * + * @hide + */ +@Retention(SOURCE) +@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE}) +public @interface FloatRange { + /** Smallest value. Whether it is inclusive or not is determined + * by {@link #fromInclusive} */ + double from() default Double.NEGATIVE_INFINITY; + /** Largest value. Whether it is inclusive or not is determined + * by {@link #toInclusive} */ + double to() default Double.POSITIVE_INFINITY; + + /** Whether the from value is included in the range */ + boolean fromInclusive() default true; + + /** Whether the to value is included in the range */ + boolean toInclusive() default true; +}
\ No newline at end of file diff --git a/core/java/android/annotation/IntRange.java b/core/java/android/annotation/IntRange.java new file mode 100644 index 0000000..1e3c072 --- /dev/null +++ b/core/java/android/annotation/IntRange.java @@ -0,0 +1,47 @@ +/* + * 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.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Denotes that the annotated element should be an int or long in the given range + * <p> + * Example: + * <pre>{@code + * @IntRange(from=0,to=255) + * public int getAlpha() { + * ... + * } + * }</pre> + * + * @hide + */ +@Retention(SOURCE) +@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE}) +public @interface IntRange { + /** Smallest value, inclusive */ + long from() default Long.MIN_VALUE; + /** Largest value, inclusive */ + long to() default Long.MAX_VALUE; +}
\ No newline at end of file diff --git a/core/java/android/annotation/Size.java b/core/java/android/annotation/Size.java new file mode 100644 index 0000000..389b819 --- /dev/null +++ b/core/java/android/annotation/Size.java @@ -0,0 +1,52 @@ +/* + * 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.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Denotes that the annotated element should have a given size or length. + * Note that "-1" means "unset". Typically used with a parameter or + * return value of type array or collection. + * <p> + * Example: + * <pre>{@code + * public void getLocationInWindow(@Size(2) int[] location) { + * ... + * } + * }</pre> + * + * @hide + */ +@Retention(SOURCE) +@Target({PARAMETER,LOCAL_VARIABLE,METHOD,FIELD}) +public @interface Size { + /** An exact size (or -1 if not specified) */ + long value() default -1; + /** A minimum size, inclusive */ + long min() default Long.MIN_VALUE; + /** A maximum size, inclusive */ + long max() default Long.MAX_VALUE; + /** The size must be a multiple of this factor */ + long multiple() default 1; +}
\ No newline at end of file diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index a36d34a..9f8befe 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.CallSuper; import android.annotation.DrawableRes; import android.annotation.IdRes; import android.annotation.IntDef; @@ -921,6 +922,7 @@ public class Activity extends ContextThemeWrapper * @see #onRestoreInstanceState * @see #onPostCreate */ + @CallSuper protected void onCreate(@Nullable Bundle savedInstanceState) { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState); if (mLastNonConfigurationInstances != null) { @@ -1122,6 +1124,7 @@ public class Activity extends ContextThemeWrapper * recently supplied in {@link #onSaveInstanceState}. <b><i>Note: Otherwise it is null.</i></b> * @see #onCreate */ + @CallSuper protected void onPostCreate(@Nullable Bundle savedInstanceState) { if (!isChild()) { mTitleReady = true; @@ -1159,6 +1162,7 @@ public class Activity extends ContextThemeWrapper * @see #onStop * @see #onResume */ + @CallSuper protected void onStart() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this); mCalled = true; @@ -1196,6 +1200,7 @@ public class Activity extends ContextThemeWrapper * @see #onStart * @see #onResume */ + @CallSuper protected void onRestart() { mCalled = true; } @@ -1220,6 +1225,7 @@ public class Activity extends ContextThemeWrapper * @see #onPostResume * @see #onPause */ + @CallSuper protected void onResume() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this); getApplication().dispatchActivityResumed(this); @@ -1239,6 +1245,7 @@ public class Activity extends ContextThemeWrapper * * @see #onResume */ + @CallSuper protected void onPostResume() { final Window win = getWindow(); if (win != null) win.makeActive(); @@ -1464,6 +1471,7 @@ public class Activity extends ContextThemeWrapper * @see #onSaveInstanceState * @see #onStop */ + @CallSuper protected void onPause() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this); getApplication().dispatchActivityPaused(this); @@ -1570,6 +1578,7 @@ public class Activity extends ContextThemeWrapper * @see #onSaveInstanceState * @see #onDestroy */ + @CallSuper protected void onStop() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this); if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); @@ -1607,6 +1616,7 @@ public class Activity extends ContextThemeWrapper * @see #finish * @see #isFinishing */ + @CallSuper protected void onDestroy() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this); mCalled = true; @@ -5587,6 +5597,7 @@ public class Activity extends ContextThemeWrapper * @see #requestVisibleBehind(boolean) * @see #onBackgroundVisibleBehindChanged(boolean) */ + @CallSuper public void onVisibleBehindCanceled() { mCalled = true; } @@ -5709,6 +5720,7 @@ public class Activity extends ContextThemeWrapper * * @param mode The new action mode. */ + @CallSuper @Override public void onActionModeStarted(ActionMode mode) { } @@ -5719,6 +5731,7 @@ public class Activity extends ContextThemeWrapper * * @param mode The action mode that just finished. */ + @CallSuper @Override public void onActionModeFinished(ActionMode mode) { } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index c6ffef6..c525ef2 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -309,6 +309,21 @@ public class ActivityManager { /** @hide Process is being cached for later use and is empty. */ public static final int PROCESS_STATE_CACHED_EMPTY = 13; + /** + * Lock task mode is not active. + */ + public static final int LOCK_TASK_MODE_NONE = 0; + + /** + * Full lock task mode is active. + */ + public static final int LOCK_TASK_MODE_LOCKED = 1; + + /** + * App pinning mode is active. + */ + public static final int LOCK_TASK_MODE_PINNED = 2; + Point mAppTaskThumbnailSize; /*package*/ ActivityManager(Context context, Handler handler) { @@ -2684,12 +2699,25 @@ public class ActivityManager { * no new tasks can be created or switched to. * * @see Activity#startLockTask() + * + * @deprecated Use {@link #getLockTaskModeState} instead. */ public boolean isInLockTaskMode() { + return getLockTaskModeState() != LOCK_TASK_MODE_NONE; + } + + /** + * Return the current state of task locking. The three possible outcomes + * are {@link #LOCK_TASK_MODE_NONE}, {@link #LOCK_TASK_MODE_LOCKED} + * and {@link #LOCK_TASK_MODE_PINNED}. + * + * @see Activity#startLockTask() + */ + public int getLockTaskModeState() { try { - return ActivityManagerNative.getDefault().isInLockTaskMode(); + return ActivityManagerNative.getDefault().getLockTaskModeState(); } catch (RemoteException e) { - return false; + return ActivityManager.LOCK_TASK_MODE_NONE; } } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index bb307bb..997f69d 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2294,6 +2294,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case GET_LOCK_TASK_MODE_STATE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + final int lockTaskModeState = getLockTaskModeState(); + reply.writeNoException(); + reply.writeInt(lockTaskModeState); + return true; + } + case SET_TASK_DESCRIPTION_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); @@ -2415,6 +2423,23 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeNoException(); return true; } + + case SET_DUMP_HEAP_DEBUG_LIMIT_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + String procName = data.readString(); + long maxMemSize = data.readLong(); + setDumpHeapDebugLimit(procName, maxMemSize); + reply.writeNoException(); + return true; + } + + case DUMP_HEAP_FINISHED_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + String path = data.readString(); + dumpHeapFinished(path); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -5420,6 +5445,19 @@ class ActivityManagerProxy implements IActivityManager } @Override + public int getLockTaskModeState() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(GET_LOCK_TASK_MODE_STATE_TRANSACTION, data, reply, 0); + reply.readException(); + int lockTaskModeState = reply.readInt(); + data.recycle(); + reply.recycle(); + return lockTaskModeState; + } + + @Override public void setTaskDescription(IBinder token, ActivityManager.TaskDescription values) throws RemoteException { Parcel data = Parcel.obtain(); @@ -5595,5 +5633,30 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + @Override + public void setDumpHeapDebugLimit(String processName, long maxMemSize) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeString(processName); + data.writeLong(maxMemSize); + mRemote.transact(SET_DUMP_HEAP_DEBUG_LIMIT_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + @Override + public void dumpHeapFinished(String path) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeString(path); + mRemote.transact(DUMP_HEAP_FINISHED_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 653b951..97793c5 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -121,7 +121,6 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.TimeZone; -import java.util.regex.Pattern; import libcore.io.DropBox; import libcore.io.EventLogger; @@ -160,7 +159,6 @@ public final class ActivityThread { private static final boolean DEBUG_MEMORY_TRIM = false; private static final boolean DEBUG_PROVIDER = false; private static final long MIN_TIME_BETWEEN_GCS = 5*1000; - private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";"); private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003; private static final int LOG_AM_ON_PAUSE_CALLED = 30021; private static final int LOG_AM_ON_RESUME_CALLED = 30022; @@ -4247,6 +4245,10 @@ public final class ActivityThread { } else { Debug.dumpNativeHeap(dhd.fd.getFileDescriptor()); } + try { + ActivityManagerNative.getDefault().dumpHeapFinished(dhd.path); + } catch (RemoteException e) { + } } final void handleDispatchPackageBroadcast(int cmd, String[] packages) { @@ -4986,7 +4988,7 @@ public final class ActivityThread { private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider, ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) { - final String auths[] = PATTERN_SEMICOLON.split(holder.info.authority); + final String auths[] = holder.info.authority.split(";"); final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid); final ProviderClientRecord pcr = new ProviderClientRecord( diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 95870cf..4bd2332 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -206,8 +206,10 @@ public class AppOpsManager { public static final int OP_PROJECT_MEDIA = 46; /** @hide Activate a VPN connection without user intervention. */ public static final int OP_ACTIVATE_VPN = 47; + /** @hide Access the WallpaperManagerAPI to write wallpapers. */ + public static final int OP_WRITE_WALLPAPER = 48; /** @hide */ - public static final int _NUM_OP = 48; + public static final int _NUM_OP = 49; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = @@ -285,6 +287,7 @@ public class AppOpsManager { OP_TOAST_WINDOW, OP_PROJECT_MEDIA, OP_ACTIVATE_VPN, + OP_WRITE_WALLPAPER, }; /** @@ -340,6 +343,7 @@ public class AppOpsManager { null, null, OPSTR_ACTIVATE_VPN, + null, }; /** @@ -395,6 +399,7 @@ public class AppOpsManager { "TOAST_WINDOW", "PROJECT_MEDIA", "ACTIVATE_VPN", + "WRITE_WALLPAPER", }; /** @@ -450,6 +455,7 @@ public class AppOpsManager { null, // no permission for displaying toasts null, // no permission for projecting media null, // no permission for activating vpn + null, // no permission for supporting wallpaper }; /** @@ -506,6 +512,7 @@ public class AppOpsManager { UserManager.DISALLOW_CREATE_WINDOWS, // TOAST_WINDOW null, //PROJECT_MEDIA UserManager.DISALLOW_CONFIG_VPN, // ACTIVATE_VPN + UserManager.DISALLOW_WALLPAPER, // WRITE_WALLPAPER }; /** @@ -561,6 +568,7 @@ public class AppOpsManager { true, //TOAST_WINDOW false, //PROJECT_MEDIA false, //ACTIVATE_VPN + false, //WALLPAPER }; /** @@ -615,6 +623,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA AppOpsManager.MODE_IGNORED, // OP_ACTIVATE_VPN + AppOpsManager.MODE_ALLOWED, }; /** @@ -673,6 +682,7 @@ public class AppOpsManager { false, false, false, + false, }; private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>(); diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index e465d57..8e64b34 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.CallSuper; import android.annotation.DrawableRes; import android.annotation.IdRes; import android.annotation.LayoutRes; @@ -1010,6 +1011,7 @@ public class Dialog implements DialogInterface, Window.Callback, * Note that if you override this method you should always call through * to the superclass implementation by calling super.onActionModeStarted(mode). */ + @CallSuper public void onActionModeStarted(ActionMode mode) { mActionMode = mode; } @@ -1020,6 +1022,7 @@ public class Dialog implements DialogInterface, Window.Callback, * Note that if you override this method you should always call through * to the superclass implementation by calling super.onActionModeFinished(mode). */ + @CallSuper public void onActionModeFinished(ActionMode mode) { if (mode == mActionMode) { mActionMode = null; diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index a7e9413..3dcbdd2 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -461,6 +461,8 @@ public interface IActivityManager extends IInterface { public boolean isInLockTaskMode() throws RemoteException; + public int getLockTaskModeState() throws RemoteException; + public void setTaskDescription(IBinder token, ActivityManager.TaskDescription values) throws RemoteException; public void setTaskResizeable(int taskId, boolean resizeable) throws RemoteException; @@ -480,6 +482,9 @@ public interface IActivityManager extends IInterface { public void systemBackupRestored() throws RemoteException; public void notifyCleartextNetwork(int uid, byte[] firstPacket) throws RemoteException; + public void setDumpHeapDebugLimit(String processName, long maxMemSize) throws RemoteException; + public void dumpHeapFinished(String path) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -510,7 +515,7 @@ public interface IActivityManager extends IInterface { dest.writeStrongBinder(null); } dest.writeStrongBinder(connection); - dest.writeInt(noReleaseNeeded ? 1:0); + dest.writeInt(noReleaseNeeded ? 1 : 0); } public static final Parcelable.Creator<ContentProviderHolder> CREATOR @@ -529,7 +534,7 @@ public interface IActivityManager extends IInterface { private ContentProviderHolder(Parcel source) { info = ProviderInfo.CREATOR.createFromParcel(source); provider = ContentProviderNative.asInterface( - source.readStrongBinder()); + source.readStrongBinder()); connection = source.readStrongBinder(); noReleaseNeeded = source.readInt() != 0; } @@ -810,4 +815,7 @@ public interface IActivityManager extends IInterface { int SET_TASK_RESIZEABLE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+283; int REQUEST_ASSIST_CONTEXT_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+284; int RESIZE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+285; + int GET_LOCK_TASK_MODE_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+286; + int SET_DUMP_HEAP_DEBUG_LIMIT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+287; + int DUMP_HEAP_FINISHED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+288; } diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 3b5900b..ccba250 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -29,13 +29,18 @@ interface IWallpaperManager { /** * Set the wallpaper. */ - ParcelFileDescriptor setWallpaper(String name); + ParcelFileDescriptor setWallpaper(String name, in String callingPackage); /** * Set the live wallpaper. */ + void setWallpaperComponentChecked(in ComponentName name, in String callingPackage); + + /** + * Set the live wallpaper. + */ void setWallpaperComponent(in ComponentName name); - + /** * Get the wallpaper. */ @@ -50,7 +55,7 @@ interface IWallpaperManager { /** * Clear the wallpaper. */ - void clearWallpaper(); + void clearWallpaper(in String callingPackage); /** * Return whether there is a wallpaper set with the given name. @@ -61,7 +66,7 @@ interface IWallpaperManager { * Sets the dimension hint for the wallpaper. These hints indicate the desired * minimum width and height for the wallpaper. */ - void setDimensionHints(in int width, in int height); + void setDimensionHints(in int width, in int height, in String callingPackage); /** * Returns the desired minimum width for the wallpaper. @@ -76,7 +81,7 @@ interface IWallpaperManager { /** * Sets extra padding that we would like the wallpaper to have outside of the display. */ - void setDisplayPadding(in Rect padding); + void setDisplayPadding(in Rect padding, in String callingPackage); /** * Returns the name of the wallpaper. Private API. @@ -87,4 +92,9 @@ interface IWallpaperManager { * Informs the service that wallpaper settings have been restored. Private API. */ void settingsRestored(); + + /** + * Check whether wallpapers are supported for the calling user. + */ + boolean isWallpaperSupported(in String callingPackage); } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index ad2b61f..5572d30 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1322,7 +1322,10 @@ public class Instrumentation { /* * Starts allocation counting. This triggers a gc and resets the counts. + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public void startAllocCounting() { // Before we start trigger a GC and reset the debug counts. Run the // finalizers and another GC before starting and stopping the alloc @@ -1340,7 +1343,10 @@ public class Instrumentation { /* * Stops allocation counting. + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public void stopAllocCounting() { Runtime.getRuntime().gc(); Runtime.getRuntime().runFinalization(); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 9c00e1c..85a6aff 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.IntDef; import android.annotation.SdkConstant; @@ -338,6 +339,7 @@ public class Notification implements Parcelable * @see #FLAG_SHOW_LIGHTS * @see #flags */ + @ColorInt public int ledARGB; /** @@ -415,7 +417,6 @@ public class Notification implements Parcelable * Bit to be bitwise-ored into the {@link #flags} field that should be * set if the notification should be canceled when it is clicked by the * user. - */ public static final int FLAG_AUTO_CANCEL = 0x00000010; @@ -520,12 +521,14 @@ public class Notification implements Parcelable * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are * ignored. */ + @ColorInt public int color = COLOR_DEFAULT; /** * Special value of {@link #color} 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 = 0; // AKA Color.TRANSPARENT /** @@ -2388,7 +2391,7 @@ public class Notification implements Parcelable * @see Notification#ledOnMS * @see Notification#ledOffMS */ - public Builder setLights(int argb, int onMs, int offMs) { + public Builder setLights(@ColorInt int argb, int onMs, int offMs) { mLedArgb = argb; mLedOnMs = onMs; mLedOffMs = offMs; @@ -2712,7 +2715,7 @@ public class Notification implements Parcelable * * @return The same Builder. */ - public Builder setColor(int argb) { + public Builder setColor(@ColorInt int argb) { mColor = argb; return this; } @@ -5160,7 +5163,7 @@ public class Notification implements Parcelable * 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; } @@ -5170,6 +5173,7 @@ public class Notification implements Parcelable * * @see setColor */ + @ColorInt public int getColor() { return mColor; } diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index badb606..22e79b6 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -64,7 +64,10 @@ import java.util.List; * Provides access to the system wallpaper. With WallpaperManager, you can * get the current wallpaper, get the desired dimensions for the wallpaper, set * the wallpaper, and more. Get an instance of WallpaperManager with - * {@link #getInstance(android.content.Context) getInstance()}. + * {@link #getInstance(android.content.Context) getInstance()}. + * + * <p> An app can check whether wallpapers are supported for the current user, by calling + * {@link #isWallpaperSupported()}. */ public class WallpaperManager { private static String TAG = "WallpaperManager"; @@ -249,6 +252,15 @@ public class WallpaperManager { public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) { synchronized (this) { + if (mService != null) { + try { + if (!mService.isWallpaperSupported(context.getOpPackageName())) { + return null; + } + } catch (RemoteException e) { + // Ignore + } + } if (mWallpaper != null) { return mWallpaper; } @@ -618,7 +630,9 @@ public class WallpaperManager { * wallpaper will require reloading it again from disk. */ public void forgetLoadedWallpaper() { - sGlobals.forgetLoadedWallpaper(); + if (isWallpaperSupported()) { + sGlobals.forgetLoadedWallpaper(); + } } /** @@ -717,7 +731,7 @@ public class WallpaperManager { Resources resources = mContext.getResources(); /* Set the wallpaper to the default values */ ParcelFileDescriptor fd = sGlobals.mService.setWallpaper( - "res:" + resources.getResourceName(resid)); + "res:" + resources.getResourceName(resid), mContext.getOpPackageName()); if (fd != null) { FileOutputStream fos = null; try { @@ -753,7 +767,8 @@ public class WallpaperManager { return; } try { - ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null); + ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, + mContext.getOpPackageName()); if (fd == null) { return; } @@ -792,7 +807,8 @@ public class WallpaperManager { return; } try { - ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null); + ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, + mContext.getOpPackageName()); if (fd == null) { return; } @@ -945,7 +961,8 @@ public class WallpaperManager { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); } else { - sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight); + sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight, + mContext.getOpPackageName()); } } catch (RemoteException e) { // Ignore @@ -966,7 +983,7 @@ public class WallpaperManager { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); } else { - sGlobals.mService.setDisplayPadding(padding); + sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName()); } } catch (RemoteException e) { // Ignore @@ -1006,7 +1023,7 @@ public class WallpaperManager { return; } try { - sGlobals.mService.clearWallpaper(); + sGlobals.mService.clearWallpaper(mContext.getOpPackageName()); } catch (RemoteException e) { // Ignore } @@ -1027,7 +1044,7 @@ public class WallpaperManager { return false; } try { - sGlobals.mService.setWallpaperComponent(name); + sGlobals.mService.setWallpaperComponentChecked(name, mContext.getOpPackageName()); return true; } catch (RemoteException e) { // Ignore @@ -1096,7 +1113,24 @@ public class WallpaperManager { // Ignore. } } - + + /** + * Returns whether wallpapers are supported for the calling user. If this function returns + * false, any attempts to changing the wallpaper will have no effect. + */ + public boolean isWallpaperSupported() { + if (sGlobals.mService == null) { + Log.w(TAG, "WallpaperService not running"); + } else { + try { + return sGlobals.mService.isWallpaperSupported(mContext.getOpPackageName()); + } catch (RemoteException e) { + // Ignore + } + } + return false; + } + /** * Clear the offsets previously associated with this window through * {@link #setWallpaperOffsets(IBinder, float, float)}. This reverts diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index 4fc990e..fe284ce 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -168,8 +168,8 @@ public class DeviceAdminReceiver extends BroadcastReceiver { /** * Action sent to a device administrator to notify that the device is entering - * lock task mode from an authorized package. The extra {@link #EXTRA_LOCK_TASK_PACKAGE} - * will describe the authorized package using lock task mode. + * lock task mode. The extra {@link #EXTRA_LOCK_TASK_PACKAGE} + * will describe the package using lock task mode. * * <p>The calling device admin must be the device owner or profile * owner to receive this broadcast. @@ -182,7 +182,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver { /** * Action sent to a device administrator to notify that the device is exiting - * lock task mode from an authorized package. + * lock task mode. * * <p>The calling device admin must be the device owner or profile * owner to receive this broadcast. @@ -215,7 +215,8 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * <p>A device admin application which listens to this intent can find out if the device was * provisioned for the device owner or profile owner case by calling respectively * {@link android.app.admin.DevicePolicyManager#isDeviceOwnerApp} and - * {@link android.app.admin.DevicePolicyManager#isProfileOwnerApp}. + * {@link android.app.admin.DevicePolicyManager#isProfileOwnerApp}. You will generally handle + * this in {@link DeviceAdminReceiver#onProfileProvisioningComplete}. * * <p>Input: Nothing.</p> * <p>Output: Nothing</p> @@ -224,6 +225,23 @@ public class DeviceAdminReceiver extends BroadcastReceiver { public static final String ACTION_PROFILE_PROVISIONING_COMPLETE = "android.app.action.PROFILE_PROVISIONING_COMPLETE"; + /** + * Broadcast Action: This broadcast is sent to indicate that the system is ready for the device + * initializer to perform user setup tasks. This is only applicable to devices managed by a + * device owner app. + * + * <p>The broadcast will be limited to the {@link DeviceAdminReceiver} component specified in + * the (@link DevicePolicyManager#EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME) field + * of the original intent or NFC bump that started the provisioning process. You will generally + * handle this in {@link DeviceAdminReceiver#onReadyForUserInitialization}. + * + * <p>Input: Nothing.</p> + * <p>Output: Nothing</p> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_READY_FOR_USER_INITIALIZATION = + "android.app.action.READY_FOR_USER_INITIALIZATION"; + /** @hide */ public static final String ACTION_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS"; @@ -245,7 +263,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver { /** @hide */ public static final String EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE = "android.app.extra.CHOOSE_PRIVATE_KEY_RESPONSE"; - /** + /** * Name under which a DevicePolicy component publishes information * about itself. This meta-data must reference an XML resource containing * a device-admin tag. @@ -382,20 +400,20 @@ public class DeviceAdminReceiver extends BroadcastReceiver { /** * Called when provisioning of a managed profile or managed device has completed successfully. * - * <p> As a prerequisit for the execution of this callback the (@link DeviceAdminReceiver} has + * <p> As a prerequisite for the execution of this callback the {@link DeviceAdminReceiver} has * to declare an intent filter for {@link #ACTION_PROFILE_PROVISIONING_COMPLETE}. * Its component must also be specified in the {@link DevicePolicyManager#EXTRA_DEVICE_ADMIN} * of the {@link DevicePolicyManager#ACTION_PROVISION_MANAGED_PROFILE} intent that started the * managed provisioning. * - * <p>When provisioning is complete, the managed profile is hidden until the profile owner - * calls {DevicePolicyManager#setProfileEnabled(ComponentName admin)}. Typically a profile - * owner will enable the profile when it has finished any additional setup such as adding an - * account by using the {@link AccountManager} and calling apis to bring the profile into the - * desired state. + * <p>When provisioning of a managed profile is complete, the managed profile is hidden until + * the profile owner calls {DevicePolicyManager#setProfileEnabled(ComponentName admin)}. + * Typically a profile owner will enable the profile when it has finished any additional setup + * such as adding an account by using the {@link AccountManager} and calling apis to bring the + * profile into the desired state. * * <p> Note that provisioning completes without waiting for any server interactions, so the - * profile owner needs to wait for data to be available if required (e.g android device ids or + * profile owner needs to wait for data to be available if required (e.g. android device ids or * other data that is set as a result of server interactions). * * @param context The running context as per {@link #onReceive}. @@ -405,8 +423,31 @@ public class DeviceAdminReceiver extends BroadcastReceiver { } /** - * Called when a device is entering lock task mode by a package authorized - * by {@link DevicePolicyManager#isLockTaskPermitted(String)} + * Called during provisioning of a managed device to allow the device initializer to perform + * user setup steps. Only device initializers should override this method. + * + * <p> Called when the DeviceAdminReceiver receives a + * {@link #ACTION_READY_FOR_USER_INITIALIZATION} broadcast. As a prerequisite for the execution + * of this callback the {@link DeviceAdminReceiver} has + * to declare an intent filter for {@link #ACTION_READY_FOR_USER_INITIALIZATION}. Only the + * component specified in the + * {@link DevicePolicyManager#EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME} field of the + * original intent or NFC bump that started the provisioning process will receive this callback. + * + * <p>It is not assumed that the device initializer is finished when it returns from + * this call, as it may do additional setup asynchronously. The device initializer must call + * {DevicePolicyManager#setUserEnabled(ComponentName admin)} when it has finished any additional + * setup (such as adding an account by using the {@link AccountManager}) in order for the user + * to be functional. + * + * @param context The running context as per {@link #onReceive}. + * @param intent The received intent as per {@link #onReceive}. + */ + public void onReadyForUserInitialization(Context context, Intent intent) { + } + + /** + * Called when a device is entering lock task mode. * * @param context The running context as per {@link #onReceive}. * @param intent The received intent as per {@link #onReceive}. @@ -416,8 +457,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver { } /** - * Called when a device is exiting lock task mode by a package authorized - * by {@link DevicePolicyManager#isLockTaskPermitted(String)} + * Called when a device is exiting lock task mode. * * @param context The running context as per {@link #onReceive}. * @param intent The received intent as per {@link #onReceive}. @@ -488,6 +528,8 @@ public class DeviceAdminReceiver extends BroadcastReceiver { onLockTaskModeEntering(context, intent, pkg); } else if (ACTION_LOCK_TASK_EXITING.equals(action)) { onLockTaskModeExiting(context, intent); + } else if (ACTION_READY_FOR_USER_INITIALIZATION.equals(action)) { + onReadyForUserInitialization(context, intent); } } } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 170224d..2c2328a 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -28,6 +28,7 @@ import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.graphics.Bitmap; import android.net.ProxyInfo; import android.os.Bundle; import android.os.Handler; @@ -108,7 +109,11 @@ public class DevicePolicyManager { * Provisioning adds a managed profile and sets the MDM as the profile owner who has full * control over the profile. * - * <p>This intent must contain the extra {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}. + * In version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this intent must contain the + * extra {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}. + * As of {@link android.os.Build.VERSION_CODES#MNC}, it should contain the extra + * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} instead, although specifying only + * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} is still supported. * * <p> When managed provisioning has completed, broadcasts are sent to the application specified * in the provisioning intent. The @@ -149,11 +154,36 @@ public class DevicePolicyManager { * * <p>This package is set as device owner when device owner provisioning is started by an NFC * message containing an NFC record with MIME type {@link #MIME_TYPE_PROVISIONING_NFC}. + * + * <p> When this extra is set, the application must have exactly one device admin receiver. + * This receiver will be set as the profile or device owner and active admin.</p> + + * @see DeviceAdminReceiver + * @deprecated Use {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}. This extra is still + * supported. */ + @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME"; /** + * A ComponentName extra indicating the device admin receiver of the mobile device management + * application that will be set as the profile owner or device owner and active admin. + * + * <p>If an application starts provisioning directly via an intent with action + * {@link #ACTION_PROVISION_MANAGED_PROFILE} the package name of this component has to match the + * package name of the application that started provisioning. + * + * <p>This component is set as device owner and active admin when device owner provisioning is + * started by an NFC message containing an NFC record with MIME type + * {@link #MIME_TYPE_PROVISIONING_NFC}. + * + * @see DeviceAdminReceiver + */ + public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME + = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME"; + + /** * An {@link android.accounts.Account} extra holding the account to migrate during managed * profile provisioning. If the account supplied is present in the primary user, it will be * copied, along with its credentials to the managed profile and removed from the primary user. @@ -357,6 +387,52 @@ public class DevicePolicyManager { "android.app.extra.PROVISIONING_SKIP_ENCRYPTION"; /** + * On devices managed by a device owner app, a String representation of a Component name extra + * indicating the component of the application that is temporarily granted device owner + * privileges during device initialization and profile owner privileges during secondary user + * initialization. + * + * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner + * provisioning via an NFC bump. + * @see ComponentName#unflattenFromString() + */ + public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME + = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME"; + + /** + * A String extra holding an http url that specifies the download location of the device + * initializer package. When not provided it is assumed that the device initializer package is + * already installed. + * + * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner + * provisioning via an NFC bump. + */ + public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION + = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION"; + + /** + * A String extra holding a http cookie header which should be used in the http request to the + * url specified in {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}. + * + * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner + * provisioning via an NFC bump. + */ + public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER + = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER"; + + /** + * A String extra holding the SHA-1 checksum of the file at download location specified in + * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}. If this doesn't + * match the file at the download location an error will be shown to the user and the user will + * be asked to factory reset the device. + * + * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner + * provisioning via an NFC bump. + */ + public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM + = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM"; + + /** * This MIME type is used for starting the Device Owner provisioning. * * <p>During device owner provisioning a device admin app is set as the owner of the device. @@ -372,7 +448,6 @@ public class DevicePolicyManager { * <p>The NFC record must contain a serialized {@link java.util.Properties} object which * contains the following properties: * <ul> - * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}</li> * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}</li> * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER}, optional</li> * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM}</li> @@ -389,6 +464,15 @@ public class DevicePolicyManager { * <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}, optional</li> * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li></ul> * + * <p> + * In version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, it should also contain + * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}. + * As of {@link android.os.Build.VERSION_CODES#MNC}, it should contain + * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} instead, (although + * specifying only {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} is still supported). + * This componentName must have been converted to a String via + * {@link android.content.ComponentName#flattenToString()} + * * <p> When device owner provisioning has completed, an intent of the type * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcasted to the * device owner. @@ -2382,6 +2466,113 @@ public class DevicePolicyManager { } /** + * Sets the given component as the device initializer. The package must already be installed and + * set as an active device administrator, and there must not be an existing device initializer, + * for this call to succeed. This method can only be called by an app holding the + * MANAGE_DEVICE_ADMINS permission before the device is provisioned or by a device owner app. A + * device initializer app is granted device owner privileges during device initialization and + * profile owner privileges during secondary user initialization. + * @param who Which {@link DeviceAdminReceiver} this request is associated with, or null if not + * called by the device owner. + * @param initializer Which {@link DeviceAdminReceiver} to make device initializer. + * @param initializerName The user-visible name of the device initializer. + * @return whether the package was successfully registered as the device initializer. + * @throws IllegalArgumentException if the package name is null or invalid + * @throws IllegalStateException if the caller is not device owner or the device has + * already been provisioned or a device initializer already exists. + */ + public boolean setDeviceInitializer(ComponentName who, ComponentName initializer, + String initializerName) throws IllegalArgumentException, IllegalStateException { + if (mService != null) { + try { + return mService.setDeviceInitializer(who, initializer, initializerName); + } catch (RemoteException re) { + Log.w(TAG, "Failed to set device initializer"); + } + } + return false; + } + + /** + * Used to determine if a particular package has been registered as the device initializer. + * + * @param packageName the package name of the app, to compare with the registered device + * initializer app, if any. + * @return whether or not the caller is registered as the device initializer app. + */ + public boolean isDeviceInitializerApp(String packageName) { + if (mService != null) { + try { + return mService.isDeviceInitializer(packageName); + } catch (RemoteException re) { + Log.w(TAG, "Failed to check device initializer"); + } + } + return false; + } + + /** + * Removes the device initializer, so that it will not be invoked on user initialization for any + * subsequently created users. This method can be called by either the device owner or device + * initializer itself. The caller must be an active administrator. + * + * @param who Which {@link DeviceAdminReceiver} this request is associated with. + */ + public void clearDeviceInitializerApp(ComponentName who) { + if (mService != null) { + try { + mService.clearDeviceInitializer(who); + } catch (RemoteException re) { + Log.w(TAG, "Failed to clear device initializer"); + } + } + } + + /** + * @hide + * Gets the device initializer of the system. + * + * @return the package name of the device initializer. + */ + @SystemApi + public String getDeviceInitializerApp() { + if (mService != null) { + try { + return mService.getDeviceInitializer(); + } catch (RemoteException re) { + Log.w(TAG, "Failed to get device initializer"); + } + } + return null; + } + + /** + * Sets the enabled state of the user. A user should be enabled only once it is ready to + * be used. + * + * <p>Device initializer must call this method to mark the user as functional. + * Only the device initializer agent can call this. + * + * <p>When the user is enabled, if the device initializer is not also the device owner, the + * device initializer will no longer have elevated permissions to call methods in this class. + * Additionally, it will be removed as an active administrator and its + * {@link DeviceAdminReceiver} will be disabled. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @return whether the user is now enabled. + */ + public boolean setUserEnabled(ComponentName admin) { + if (mService != null) { + try { + return mService.setUserEnabled(admin); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return false; + } + + /** * @hide * @deprecated Use #ACTION_SET_PROFILE_OWNER * Sets the given component as an active admin and registers the package as the profile @@ -3119,8 +3310,7 @@ public class DevicePolicyManager { } /** - * Called by a profile or device owner to set a user restriction specified - * by the key. + * Called by a profile or device owner to set a user restriction specified by the key. * <p> * The calling device admin must be a profile or device owner; if it is not, * a security exception will be thrown. @@ -3141,8 +3331,7 @@ public class DevicePolicyManager { } /** - * Called by a profile or device owner to clear a user restriction specified - * by the key. + * Called by a profile or device owner to clear a user restriction specified by the key. * <p> * The calling device admin must be a profile or device owner; if it is not, * a security exception will be thrown. @@ -3163,7 +3352,7 @@ public class DevicePolicyManager { } /** - * Called by device or profile owner to hide or unhide packages. When a package is hidden it + * Called by profile or device owners to hide or unhide packages. When a package is hidden it * is unavailable for use, but the data and actual package file remain. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. @@ -3185,7 +3374,7 @@ public class DevicePolicyManager { } /** - * Called by device or profile owner to determine if a package is hidden. + * Called by profile or device owners to determine if a package is hidden. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param packageName The name of the package to retrieve the hidden status of. @@ -3203,7 +3392,7 @@ public class DevicePolicyManager { } /** - * Called by profile or device owner to re-enable a system app that was disabled by default + * Called by profile or device owners to re-enable a system app that was disabled by default * when the user was initialized. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. @@ -3220,7 +3409,7 @@ public class DevicePolicyManager { } /** - * Called by profile or device owner to re-enable system apps by intent that were disabled + * Called by profile or device owners to re-enable system apps by intent that were disabled * by default when the user was initialized. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. @@ -3591,4 +3780,18 @@ public class DevicePolicyManager { } return Collections.emptyList(); } + + /** + * Called by profile or device owners to set the current user's photo. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param icon the bitmap to set as the photo. + */ + public void setUserIcon(ComponentName admin, Bitmap icon) { + try { + mService.setUserIcon(admin, icon); + } catch (RemoteException re) { + Log.w(TAG, "Could not set the user icon ", re); + } + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 714e740..f69cf36 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -20,6 +20,7 @@ package android.app.admin; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.Bitmap; import android.net.ProxyInfo; import android.os.Bundle; import android.os.PersistableBundle; @@ -199,4 +200,12 @@ interface IDevicePolicyManager { boolean getAutoTimeRequired(); boolean isRemovingAdmin(in ComponentName adminReceiver, int userHandle); + + boolean setUserEnabled(in ComponentName who); + boolean isDeviceInitializer(String packageName); + void clearDeviceInitializer(in ComponentName who); + boolean setDeviceInitializer(in ComponentName who, in ComponentName initializer, String initializerName); + String getDeviceInitializer(); + + void setUserIcon(in ComponentName admin, in Bitmap icon); } diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 1b1e600..7f89100 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -258,6 +258,7 @@ public abstract class BackupAgent extends ContextWrapper { * * <ul> * <li>The contents of the {@link #getCacheDir()} directory</li> + * <li>The contents of the {@link #getCodeCacheDir()} directory</li> * <li>The contents of the {@link #getNoBackupFilesDir()} directory</li> * <li>The contents of the app's shared library directory</li> * </ul> @@ -285,6 +286,7 @@ public abstract class BackupAgent extends ContextWrapper { String databaseDir = getDatabasePath("foo").getParentFile().getCanonicalPath(); String sharedPrefsDir = getSharedPrefsFile("foo").getParentFile().getCanonicalPath(); String cacheDir = getCacheDir().getCanonicalPath(); + String codeCacheDir = getCodeCacheDir().getCanonicalPath(); String libDir = (appInfo.nativeLibraryDir != null) ? new File(appInfo.nativeLibraryDir).getCanonicalPath() : null; @@ -298,6 +300,7 @@ public abstract class BackupAgent extends ContextWrapper { filterSet.add(libDir); } filterSet.add(cacheDir); + filterSet.add(codeCacheDir); filterSet.add(databaseDir); filterSet.add(sharedPrefsDir); filterSet.add(filesDir); @@ -354,6 +357,7 @@ public abstract class BackupAgent extends ContextWrapper { String dbDir; String spDir; String cacheDir; + String codeCacheDir; String libDir; String efDir = null; String filePath; @@ -367,6 +371,7 @@ public abstract class BackupAgent extends ContextWrapper { dbDir = getDatabasePath("foo").getParentFile().getCanonicalPath(); spDir = getSharedPrefsFile("foo").getParentFile().getCanonicalPath(); cacheDir = getCacheDir().getCanonicalPath(); + codeCacheDir = getCodeCacheDir().getCanonicalPath(); libDir = (appInfo.nativeLibraryDir == null) ? null : new File(appInfo.nativeLibraryDir).getCanonicalPath(); @@ -380,7 +385,8 @@ public abstract class BackupAgent extends ContextWrapper { } // Now figure out which well-defined tree the file is placed in, working from - // most to least specific. We also specifically exclude the lib and cache dirs. + // most to least specific. We also specifically exclude the lib, cache, + // and code_cache dirs. filePath = file.getCanonicalPath(); } catch (IOException e) { Log.w(TAG, "Unable to obtain canonical paths"); @@ -388,9 +394,10 @@ public abstract class BackupAgent extends ContextWrapper { } if (filePath.startsWith(cacheDir) + || filePath.startsWith(codeCacheDir) || filePath.startsWith(libDir) || filePath.startsWith(nbFilesDir)) { - Log.w(TAG, "lib, cache, and no_backup files are not backed up"); + Log.w(TAG, "lib, cache, code_cache, and no_backup files are not backed up"); return; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 010c860..61cdec3 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -16,6 +16,7 @@ package android.content; +import android.annotation.CheckResult; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -3041,6 +3042,7 @@ public abstract class Context { * @see PackageManager#checkPermission(String, String) * @see #checkCallingPermission */ + @CheckResult(suggest="#enforcePermission(String,int,int,String)") @PackageManager.PermissionResult public abstract int checkPermission(@NonNull String permission, int pid, int uid); @@ -3070,6 +3072,7 @@ public abstract class Context { * @see #checkPermission * @see #checkCallingOrSelfPermission */ + @CheckResult(suggest="#enforceCallingPermission(String,String)") @PackageManager.PermissionResult public abstract int checkCallingPermission(@NonNull String permission); @@ -3089,6 +3092,7 @@ public abstract class Context { * @see #checkPermission * @see #checkCallingPermission */ + @CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)") @PackageManager.PermissionResult public abstract int checkCallingOrSelfPermission(@NonNull String permission); @@ -3233,6 +3237,7 @@ public abstract class Context { * * @see #checkCallingUriPermission */ + @CheckResult(suggest="#enforceUriPermission(Uri,int,int,String)") public abstract int checkUriPermission(Uri uri, int pid, int uid, @Intent.AccessUriMode int modeFlags); @@ -3261,6 +3266,7 @@ public abstract class Context { * * @see #checkUriPermission(Uri, int, int, int) */ + @CheckResult(suggest="#enforceCallingUriPermission(Uri,int,String)") public abstract int checkCallingUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags); /** @@ -3280,6 +3286,7 @@ public abstract class Context { * * @see #checkCallingUriPermission */ + @CheckResult(suggest="#enforceCallingOrSelfUriPermission(Uri,int,String)") public abstract int checkCallingOrSelfUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags); @@ -3305,6 +3312,7 @@ public abstract class Context { * is allowed to access that uri or holds one of the given permissions, or * {@link PackageManager#PERMISSION_DENIED} if it is not. */ + @CheckResult(suggest="#enforceUriPermission(Uri,String,String,int,int,int,String)") public abstract int checkUriPermission(@Nullable Uri uri, @Nullable String readPermission, @Nullable String writePermission, int pid, int uid, @Intent.AccessUriMode int modeFlags); diff --git a/core/java/android/content/UriMatcher.java b/core/java/android/content/UriMatcher.java index 8487dae..71a035e 100644 --- a/core/java/android/content/UriMatcher.java +++ b/core/java/android/content/UriMatcher.java @@ -20,7 +20,6 @@ import android.net.Uri; import java.util.ArrayList; import java.util.List; -import java.util.regex.Pattern; /** Utility class to aid in matching URIs in content providers. @@ -171,7 +170,7 @@ public class UriMatcher if (path.length() > 0 && path.charAt(0) == '/') { newPath = path.substring(1); } - tokens = PATH_SPLIT_PATTERN.split(newPath); + tokens = newPath.split("/"); } int numTokens = tokens != null ? tokens.length : 0; @@ -207,8 +206,6 @@ public class UriMatcher node.mCode = code; } - static final Pattern PATH_SPLIT_PATTERN = Pattern.compile("/"); - /** * Try to match against the path in a url. * diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index ebc8e1e..0365689 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -16,6 +16,7 @@ package android.content.pm; +import android.annotation.CheckResult; import android.annotation.DrawableRes; import android.annotation.IntDef; import android.annotation.NonNull; @@ -2121,6 +2122,7 @@ public abstract class PackageManager { * @see #PERMISSION_GRANTED * @see #PERMISSION_DENIED */ + @CheckResult public abstract int checkPermission(String permName, String pkgName); /** @@ -2248,6 +2250,7 @@ public abstract class PackageManager { * @see #SIGNATURE_NO_MATCH * @see #SIGNATURE_UNKNOWN_PACKAGE */ + @CheckResult public abstract int checkSignatures(String pkg1, String pkg2); /** @@ -2270,6 +2273,7 @@ public abstract class PackageManager { * @see #SIGNATURE_NO_MATCH * @see #SIGNATURE_UNKNOWN_PACKAGE */ + @CheckResult public abstract int checkSignatures(int uid1, int uid2); /** diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java index ace402a..841b09d 100644 --- a/core/java/android/content/res/ColorStateList.java +++ b/core/java/android/content/res/ColorStateList.java @@ -16,6 +16,7 @@ package android.content.res; +import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.Resources.Theme; @@ -91,7 +92,7 @@ public class ColorStateList implements Parcelable { * Creates a ColorStateList that returns the specified mapping from * states to colors. */ - public ColorStateList(int[][] states, int[] colors) { + public ColorStateList(int[][] states, @ColorInt int[] colors) { mStateSpecs = states; mColors = colors; @@ -102,7 +103,7 @@ public class ColorStateList implements Parcelable { * @return A ColorStateList containing a single color. */ @NonNull - public static ColorStateList valueOf(int color) { + public static ColorStateList valueOf(@ColorInt int color) { synchronized (sCache) { final int index = sCache.indexOfKey(color); if (index >= 0) { @@ -436,6 +437,7 @@ public class ColorStateList implements Parcelable { * * @return the default color in this {@link ColorStateList}. */ + @ColorInt public int getDefaultColor() { return mDefaultColor; } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 584f3f6..5dc9ef9 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -16,6 +16,7 @@ package android.content.res; +import android.annotation.ColorInt; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; @@ -925,6 +926,7 @@ public class Resources { * @return A single color value in the form 0xAARRGGBB. * @deprecated Use {@link #getColor(int, Theme)} instead. */ + @ColorInt public int getColor(@ColorRes int id) throws NotFoundException { return getColor(id, null); } @@ -945,6 +947,7 @@ public class Resources { * * @return A single color value in the form 0xAARRGGBB. */ + @ColorInt public int getColor(@ColorRes int id, @Nullable Theme theme) throws NotFoundException { TypedValue value; synchronized (mAccessLock) { diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index 3e07f0c..410849a 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -17,6 +17,7 @@ package android.content.res; import android.annotation.AnyRes; +import android.annotation.ColorInt; import android.annotation.Nullable; import android.graphics.drawable.Drawable; import android.os.StrictMode; @@ -420,7 +421,8 @@ public class TypedArray { * @throws UnsupportedOperationException if the attribute is defined but is * not an integer color or color state list. */ - public int getColor(int index, int defValue) { + @ColorInt + public int getColor(int index, @ColorInt int defValue) { if (mRecycled) { throw new RuntimeException("Cannot make calls to a recycled instance!"); } diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java index e1a2a25..e0d454c 100644 --- a/core/java/android/gesture/GestureOverlayView.java +++ b/core/java/android/gesture/GestureOverlayView.java @@ -16,6 +16,7 @@ package android.gesture; +import android.annotation.ColorInt; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; @@ -204,18 +205,20 @@ public class GestureOverlayView extends FrameLayout { mOrientation = orientation; } - public void setGestureColor(int color) { + public void setGestureColor(@ColorInt int color) { mCertainGestureColor = color; } - public void setUncertainGestureColor(int color) { + public void setUncertainGestureColor(@ColorInt int color) { mUncertainGestureColor = color; } + @ColorInt public int getUncertainGestureColor() { return mUncertainGestureColor; } + @ColorInt public int getGestureColor() { return mCertainGestureColor; } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 43d3a71..0fee4b3 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -635,24 +635,34 @@ public abstract class BatteryStats implements Parcelable { while (i < N && (c=value.charAt(i)) != '-') { i++; switch (c) { - case 'f': out |= ((Display.STATE_OFF-1)<<STEP_LEVEL_INITIAL_MODE_SHIFT); break; - case 'o': out |= ((Display.STATE_ON-1)<<STEP_LEVEL_INITIAL_MODE_SHIFT); break; - case 'd': out |= ((Display.STATE_DOZE-1)<<STEP_LEVEL_INITIAL_MODE_SHIFT); break; - case 'z': out |= ((Display.STATE_DOZE_SUSPEND-1)<<STEP_LEVEL_INITIAL_MODE_SHIFT); + case 'f': out |= (((long)Display.STATE_OFF-1)<<STEP_LEVEL_INITIAL_MODE_SHIFT); break; - case 'p': out |= (STEP_LEVEL_MODE_POWER_SAVE<<STEP_LEVEL_INITIAL_MODE_SHIFT); + case 'o': out |= (((long)Display.STATE_ON-1)<<STEP_LEVEL_INITIAL_MODE_SHIFT); break; - case 'F': out |= ((Display.STATE_OFF-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT); break; - case 'O': out |= ((Display.STATE_ON-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT); break; - case 'D': out |= ((Display.STATE_DOZE-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT); break; - case 'Z': out |= ((Display.STATE_DOZE_SUSPEND-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT); + case 'd': out |= (((long)Display.STATE_DOZE-1)<<STEP_LEVEL_INITIAL_MODE_SHIFT); break; - case 'P': out |= (STEP_LEVEL_MODE_POWER_SAVE<<STEP_LEVEL_MODIFIED_MODE_SHIFT); + case 'z': out |= (((long)Display.STATE_DOZE_SUSPEND-1) + << STEP_LEVEL_INITIAL_MODE_SHIFT); + break; + case 'p': out |= (((long)STEP_LEVEL_MODE_POWER_SAVE) + << STEP_LEVEL_INITIAL_MODE_SHIFT); + break; + case 'F': out |= (((long)Display.STATE_OFF-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT); + break; + case 'O': out |= (((long)Display.STATE_ON-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT); + break; + case 'D': out |= (((long)Display.STATE_DOZE-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT); + break; + case 'Z': out |= (((long)Display.STATE_DOZE_SUSPEND-1) + << STEP_LEVEL_MODIFIED_MODE_SHIFT); + break; + case 'P': out |= (((long)STEP_LEVEL_MODE_POWER_SAVE) + << STEP_LEVEL_MODIFIED_MODE_SHIFT); break; } } i++; - int level = 0; + long level = 0; while (i < N && (c=value.charAt(i)) != '-') { i++; level <<= 4; @@ -664,6 +674,7 @@ public abstract class BatteryStats implements Parcelable { level += c - 'A' + 10; } } + i++; out |= (level << STEP_LEVEL_LEVEL_SHIFT) & STEP_LEVEL_LEVEL_MASK; long duration = 0; while (i < N && (c=value.charAt(i)) != '-') { diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index d03365b..512e212 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -63,7 +63,10 @@ public final class Debug * * TRACE_COUNT_ALLOCS adds the results from startAllocCounting to the * trace key file. + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static final int TRACE_COUNT_ALLOCS = VMDebug.TRACE_COUNT_ALLOCS; /** @@ -760,7 +763,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Stop counting the number and aggregate size of memory allocations. * - * @see #startAllocCounting() + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ @Deprecated public static void stopAllocCounting() { @@ -770,7 +773,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Returns the global count of objects allocated by the runtime between a * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}. + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static int getGlobalAllocCount() { return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_OBJECTS); } @@ -778,7 +784,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Clears the global count of objects allocated. * @see #getGlobalAllocCount() + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static void resetGlobalAllocCount() { VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_OBJECTS); } @@ -786,7 +795,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Returns the global size, in bytes, of objects allocated by the runtime between a * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}. + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static int getGlobalAllocSize() { return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_BYTES); } @@ -794,7 +806,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Clears the global size of objects allocated. * @see #getGlobalAllocSize() + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static void resetGlobalAllocSize() { VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_BYTES); } @@ -802,7 +817,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Returns the global count of objects freed by the runtime between a * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}. + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static int getGlobalFreedCount() { return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_FREED_OBJECTS); } @@ -810,7 +828,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Clears the global count of objects freed. * @see #getGlobalFreedCount() + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static void resetGlobalFreedCount() { VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_FREED_OBJECTS); } @@ -818,7 +839,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Returns the global size, in bytes, of objects freed by the runtime between a * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}. + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static int getGlobalFreedSize() { return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_FREED_BYTES); } @@ -826,7 +850,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Clears the global size of objects freed. * @see #getGlobalFreedSize() + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static void resetGlobalFreedSize() { VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_FREED_BYTES); } @@ -834,7 +861,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Returns the number of non-concurrent GC invocations between a * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}. + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static int getGlobalGcInvocationCount() { return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_GC_INVOCATIONS); } @@ -842,7 +872,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Clears the count of non-concurrent GC invocations. * @see #getGlobalGcInvocationCount() + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static void resetGlobalGcInvocationCount() { VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_GC_INVOCATIONS); } @@ -851,7 +884,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * Returns the number of classes successfully initialized (ie those that executed without * throwing an exception) between a {@link #startAllocCounting() start} and * {@link #stopAllocCounting() stop}. + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static int getGlobalClassInitCount() { return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_COUNT); } @@ -859,7 +895,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Clears the count of classes initialized. * @see #getGlobalClassInitCount() + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static void resetGlobalClassInitCount() { VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_COUNT); } @@ -867,7 +906,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Returns the time spent successfully initializing classes between a * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}. + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static int getGlobalClassInitTime() { /* cumulative elapsed time for class initialization, in usec */ return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_TIME); @@ -876,7 +918,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Clears the count of time spent initializing classes. * @see #getGlobalClassInitTime() + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static void resetGlobalClassInitTime() { VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_TIME); } @@ -948,7 +993,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Returns the thread-local count of objects allocated by the runtime between a * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}. + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static int getThreadAllocCount() { return VMDebug.getAllocCount(VMDebug.KIND_THREAD_ALLOCATED_OBJECTS); } @@ -956,7 +1004,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Clears the thread-local count of objects allocated. * @see #getThreadAllocCount() + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static void resetThreadAllocCount() { VMDebug.resetAllocCount(VMDebug.KIND_THREAD_ALLOCATED_OBJECTS); } @@ -965,7 +1016,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * Returns the thread-local size of objects allocated by the runtime between a * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}. * @return The allocated size in bytes. + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static int getThreadAllocSize() { return VMDebug.getAllocCount(VMDebug.KIND_THREAD_ALLOCATED_BYTES); } @@ -973,7 +1027,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Clears the thread-local count of objects allocated. * @see #getThreadAllocSize() + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static void resetThreadAllocSize() { VMDebug.resetAllocCount(VMDebug.KIND_THREAD_ALLOCATED_BYTES); } @@ -1013,7 +1070,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Returns the number of thread-local non-concurrent GC invocations between a * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}. + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static int getThreadGcInvocationCount() { return VMDebug.getAllocCount(VMDebug.KIND_THREAD_GC_INVOCATIONS); } @@ -1021,7 +1081,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Clears the thread-local count of non-concurrent GC invocations. * @see #getThreadGcInvocationCount() + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static void resetThreadGcInvocationCount() { VMDebug.resetAllocCount(VMDebug.KIND_THREAD_GC_INVOCATIONS); } @@ -1029,7 +1092,10 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Clears all the global and thread-local memory allocation counters. * @see #startAllocCounting() + * + * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + @Deprecated public static void resetAllCounts() { VMDebug.resetAllocCount(VMDebug.KIND_ALL_COUNTS); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 650f3b3..706e0d0 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -391,6 +391,15 @@ public class UserManager { public static final String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam"; /** + * Hidden user restriction to disallow access to wallpaper manager APIs. This user restriction + * is always set for managed profiles. + * @hide + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_WALLPAPER = "no_wallpaper"; + + /** * Application restriction key that is used to indicate the pending arrival * of real restrictions for the app. * diff --git a/core/java/android/preference/DialogPreference.java b/core/java/android/preference/DialogPreference.java index 1b226c1..3d57b4d 100644 --- a/core/java/android/preference/DialogPreference.java +++ b/core/java/android/preference/DialogPreference.java @@ -17,6 +17,7 @@ package android.preference; +import android.annotation.CallSuper; import android.annotation.DrawableRes; import android.annotation.StringRes; import android.app.AlertDialog; @@ -360,6 +361,7 @@ public abstract class DialogPreference extends Preference implements * * @param view The content View of the dialog, if it is custom. */ + @CallSuper protected void onBindDialogView(View view) { View dialogMessageView = view.findViewById(com.android.internal.R.id.message); diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java index 78928b2..ccf2cfa 100644 --- a/core/java/android/preference/Preference.java +++ b/core/java/android/preference/Preference.java @@ -16,6 +16,7 @@ package android.preference; +import android.annotation.CallSuper; import com.android.internal.util.CharSequences; import android.annotation.DrawableRes; @@ -508,6 +509,7 @@ public class Preference implements Comparable<Preference> { * @return The View that displays this Preference. * @see #onBindView(View) */ + @CallSuper protected View onCreateView(ViewGroup parent) { final LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -537,6 +539,7 @@ public class Preference implements Comparable<Preference> { * @param view The View that shows this Preference. * @see #onCreateView(ViewGroup) */ + @CallSuper protected void onBindView(View view) { final TextView titleView = (TextView) view.findViewById(com.android.internal.R.id.title); if (titleView != null) { @@ -1356,6 +1359,7 @@ public class Preference implements Comparable<Preference> { * should remove any references to this Preference that you know about. Make * sure to call through to the superclass implementation. */ + @CallSuper protected void onPrepareForRemoval() { unregisterDependency(); } diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 9cc12b5..cc7783f 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -1010,7 +1010,8 @@ public final class ContactsContract { /** * Types of data used to produce the display name for a contact. In the order * of increasing priority: {@link #EMAIL}, {@link #PHONE}, - * {@link #ORGANIZATION}, {@link #NICKNAME}, {@link #STRUCTURED_NAME}. + * {@link #ORGANIZATION}, {@link #NICKNAME}, {@link #STRUCTURED_PHONETIC_NAME}, + * {@link #STRUCTURED_NAME}. */ public interface DisplayNameSources { public static final int UNDEFINED = 0; @@ -1018,6 +1019,8 @@ public final class ContactsContract { public static final int PHONE = 20; public static final int ORGANIZATION = 30; public static final int NICKNAME = 35; + /** Display name comes from a structured name that only has phonetic components. */ + public static final int STRUCTURED_PHONETIC_NAME = 37; public static final int STRUCTURED_NAME = 40; } diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index 1316471..6979bee 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -28,6 +28,7 @@ import static android.provider.DocumentsContract.getSearchDocumentsQuery; import static android.provider.DocumentsContract.getTreeDocumentId; import static android.provider.DocumentsContract.isTreeUri; +import android.annotation.CallSuper; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentValues; @@ -541,6 +542,7 @@ public abstract class DocumentsProvider extends ContentProvider { * * @see DocumentsContract#buildDocumentUriUsingTree(Uri, String) */ + @CallSuper @Override public Uri canonicalize(Uri uri) { final Context context = getContext(); @@ -616,6 +618,7 @@ public abstract class DocumentsProvider extends ContentProvider { * call the superclass. If the superclass returns {@code null}, the subclass * may implement custom behavior. */ + @CallSuper @Override public Bundle call(String method, String arg, Bundle extras) { if (!method.startsWith("android:")) { diff --git a/core/java/android/service/chooser/ChooserTarget.aidl b/core/java/android/service/chooser/ChooserTarget.aidl new file mode 100644 index 0000000..ca91bc8 --- /dev/null +++ b/core/java/android/service/chooser/ChooserTarget.aidl @@ -0,0 +1,19 @@ +/* + * 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.service.chooser; + +parcelable ChooserTarget; diff --git a/core/java/android/service/chooser/ChooserTarget.java b/core/java/android/service/chooser/ChooserTarget.java new file mode 100644 index 0000000..7fd1d10 --- /dev/null +++ b/core/java/android/service/chooser/ChooserTarget.java @@ -0,0 +1,248 @@ +/* + * 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.service.chooser; + +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.graphics.Bitmap; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +/** + * A ChooserTarget represents a deep-link into an application as returned by a + * {@link android.service.chooser.ChooserTargetService}. + */ +public final class ChooserTarget implements Parcelable { + private static final String TAG = "ChooserTarget"; + + /** + * The title of this target that will be shown to the user. The title may be truncated + * if it is too long to display in the space provided. + */ + private CharSequence mTitle; + + /** + * The icon that will be shown to the user to represent this target. + * The system may resize this icon as appropriate. + */ + private Bitmap mIcon; + + /** + * The IntentSender that will be used to deliver the intent to the target. + * It will be {@link android.content.Intent#fillIn(android.content.Intent, int)} filled in} + * by the real intent sent by the application. + */ + private IntentSender mIntentSender; + + /** + * The score given to this item. It can be normalized. + */ + private float mScore; + + /** + * Construct a deep link target for presentation by a chooser UI. + * + * <p>A target is composed of a title and an icon for presentation to the user. + * The UI presenting this target may truncate the title if it is too long to be presented + * in the available space, as well as crop, resize or overlay the supplied icon.</p> + * + * <p>The creator of a target may supply a ranking score. This score is assumed to be relative + * to the other targets supplied by the same + * {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}. + * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).</p> + * + * <p>Before being sent, the PendingIntent supplied will be + * {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied + * to the chooser. When constructing a PendingIntent for use in a ChooserTarget, make sure + * that you permit the relevant fields to be filled in using the appropriate flags such as + * {@link Intent#FILL_IN_ACTION}, {@link Intent#FILL_IN_CATEGORIES}, + * {@link Intent#FILL_IN_DATA} and {@link Intent#FILL_IN_CLIP_DATA}. Note that + * {@link Intent#FILL_IN_CLIP_DATA} is required to appropriately receive URI permission grants + * for {@link Intent#ACTION_SEND} intents.</p> + * + * <p>Take care not to place custom {@link android.os.Parcelable} types into + * the PendingIntent as extras, as the system will not be able to unparcel it to merge + * additional extras.</p> + * + * @param title title of this target that will be shown to a user + * @param icon icon to represent this target + * @param score ranking score for this target between 0.0f and 1.0f, inclusive + * @param pendingIntent PendingIntent to fill in and send if the user chooses this target + */ + public ChooserTarget(CharSequence title, Bitmap icon, float score, + PendingIntent pendingIntent) { + this(title, icon, score, pendingIntent.getIntentSender()); + } + + /** + * Construct a deep link target for presentation by a chooser UI. + * + * <p>A target is composed of a title and an icon for presentation to the user. + * The UI presenting this target may truncate the title if it is too long to be presented + * in the available space, as well as crop, resize or overlay the supplied icon.</p> + * + * <p>The creator of a target may supply a ranking score. This score is assumed to be relative + * to the other targets supplied by the same + * {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}. + * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).</p> + * + * <p>Before being sent, the IntentSender supplied will be + * {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied + * to the chooser. When constructing an IntentSender for use in a ChooserTarget, make sure + * that you permit the relevant fields to be filled in using the appropriate flags such as + * {@link Intent#FILL_IN_ACTION}, {@link Intent#FILL_IN_CATEGORIES}, + * {@link Intent#FILL_IN_DATA} and {@link Intent#FILL_IN_CLIP_DATA}. Note that + * {@link Intent#FILL_IN_CLIP_DATA} is required to appropriately receive URI permission grants + * for {@link Intent#ACTION_SEND} intents.</p> + * + * <p>Take care not to place custom {@link android.os.Parcelable} types into + * the IntentSender as extras, as the system will not be able to unparcel it to merge + * additional extras.</p> + * + * @param title title of this target that will be shown to a user + * @param icon icon to represent this target + * @param score ranking score for this target between 0.0f and 1.0f, inclusive + * @param intentSender IntentSender to fill in and send if the user chooses this target + */ + public ChooserTarget(CharSequence title, Bitmap icon, float score, IntentSender intentSender) { + mTitle = title; + mIcon = icon; + if (score > 1.f || score < 0.f) { + throw new IllegalArgumentException("Score " + score + " out of range; " + + "must be between 0.0f and 1.0f"); + } + mScore = score; + mIntentSender = intentSender; + } + + ChooserTarget(Parcel in) { + mTitle = in.readCharSequence(); + if (in.readInt() != 0) { + mIcon = Bitmap.CREATOR.createFromParcel(in); + } else { + mIcon = null; + } + mScore = in.readFloat(); + mIntentSender = IntentSender.readIntentSenderOrNullFromParcel(in); + } + + /** + * Returns the title of this target for display to a user. The UI displaying the title + * may truncate this title if it is too long to be displayed in full. + * + * @return the title of this target, intended to be shown to a user + */ + public CharSequence getTitle() { + return mTitle; + } + + /** + * Returns the icon representing this target for display to a user. The UI displaying the icon + * may crop, resize or overlay this icon. + * + * @return the icon representing this target, intended to be shown to a user + */ + public Bitmap getIcon() { + return mIcon; + } + + /** + * Returns the ranking score supplied by the creator of this ChooserTarget. + * Values are between 0.0f and 1.0f. The UI displaying the target may + * take this score into account when sorting and merging targets from multiple sources. + * + * @return the ranking score for this target between 0.0f and 1.0f, inclusive + */ + public float getScore() { + return mScore; + } + + /** + * Returns the raw IntentSender supplied by the ChooserTarget's creator. + * + * <p>To fill in and send the intent, see {@link #sendIntent(Context, Intent)}.</p> + * + * @return the IntentSender supplied by the ChooserTarget's creator + */ + public IntentSender getIntentSender() { + return mIntentSender; + } + + /** + * Fill in the IntentSender supplied by the ChooserTarget's creator and send it. + * + * @param context the sending Context; generally the Activity presenting the chooser UI + * @param fillInIntent the Intent provided to the Chooser to be sent to a selected target + * @return true if sending the Intent was successful + */ + public boolean sendIntent(Context context, Intent fillInIntent) { + if (fillInIntent != null) { + fillInIntent.migrateExtraStreamToClipData(); + fillInIntent.prepareToLeaveProcess(); + } + try { + mIntentSender.sendIntent(context, 0, fillInIntent, null, null); + return true; + } catch (IntentSender.SendIntentException e) { + Log.e(TAG, "sendIntent " + this + " failed", e); + return false; + } + } + + @Override + public String toString() { + return "ChooserTarget{" + mIntentSender.getCreatorPackage() + "'" + mTitle + + "', " + mScore + "}"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeCharSequence(mTitle); + if (mIcon != null) { + dest.writeInt(1); + mIcon.writeToParcel(dest, 0); + } else { + dest.writeInt(0); + } + dest.writeFloat(mScore); + IntentSender.writeIntentSenderOrNullToParcel(mIntentSender, dest); + } + + public static final Creator<ChooserTarget> CREATOR + = new Creator<ChooserTarget>() { + @Override + public ChooserTarget createFromParcel(Parcel source) { + return new ChooserTarget(source); + } + + @Override + public ChooserTarget[] newArray(int size) { + return new ChooserTarget[size]; + } + }; +} diff --git a/core/java/android/service/chooser/ChooserTargetService.java b/core/java/android/service/chooser/ChooserTargetService.java new file mode 100644 index 0000000..9188806 --- /dev/null +++ b/core/java/android/service/chooser/ChooserTargetService.java @@ -0,0 +1,130 @@ +/* + * 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.service.chooser; + +import android.annotation.SdkConstant; +import android.app.Service; +import android.content.ComponentName; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.IBinder; +import android.os.RemoteException; + +import java.util.List; + +/** + * A service that receives calls from the system when the user is asked to choose + * a target for an intent explicitly by another app. The calling app must have invoked + * {@link android.content.Intent#ACTION_CHOOSER ACTION_CHOOSER} as handled by the system; + * applications do not have the ability to query a ChooserTargetService directly. + * + * <p>Which ChooserTargetServices are queried depends on a system-level policy decision + * made at the moment the chooser is invoked, including but not limited to user time + * spent with the app package or associated components in the foreground, recency of usage + * or frequency of usage. These will generally correlate with the order that app targets + * are shown in the list of intent handlers shown in the system chooser or resolver.</p> + * + * <p>To extend this class, you must declare the service in your manifest file with + * the {@link android.Manifest.permission#BIND_CHOOSER_TARGET_SERVICE} permission + * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p> + * <pre> + * <service android:name=".ChooserTargetService" + * android:label="@string/service_name" + * android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE"> + * <intent-filter> + * <action android:name="android.service.chooser.ChooserTargetService" /> + * </intent-filter> + * </service> + * </pre> + * + * <p>For the system to query your service, you must add a <meta-data> element to the + * Activity in your manifest that can handle Intents that you would also like to provide + * optional deep links for. For example, a chat app might offer deep links to recent active + * conversations instead of invoking a generic picker after the app itself is chosen as a target. + * </p> + * + * <p>The meta-data element should have the name + * <code>android.service.chooser.chooser_target_service</code> and a value corresponding to + * the component name of your service. Example:</p> + * <pre> + * <activity android:name=".MyShareActivity" + * android:label="@string/share_activity_label"> + * <intent-filter> + * <action android:name="android.intent.action.SEND" /> + * </intent-filter> + * <meta-data android:name="android.service.chooser.chooser_target_service" + * android:value=".ChooserTargetService" /> + * </activity> + * </pre> + */ +public abstract class ChooserTargetService extends Service { + // TAG = "ChooserTargetService[MySubclass]"; + private final String TAG = ChooserTargetService.class.getSimpleName() + + '[' + getClass().getSimpleName() + ']'; + + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE = "android.service.chooser.ChooserTargetService"; + + private IChooserTargetServiceWrapper mWrapper = null; + + /** + * Called by the system to retrieve a set of deep-link {@link ChooserTarget targets} that + * can handle an intent. + * + * <p>The returned list should be sorted such that the most relevant targets appear first. + * Any PendingIntents used to construct the resulting ChooserTargets should always be prepared + * to have the relevant data fields filled in by the sender. See + * {@link ChooserTarget#ChooserTarget(CharSequence, android.graphics.Bitmap, float, android.app.PendingIntent) ChooserTarget}.</p> + * + * <p><em>Important:</em> Calls to this method from other applications will occur on + * a binder thread, not on your app's main thread. Make sure that access to relevant data + * within your app is thread-safe.</p> + * + * @param targetActivityName the ComponentName of the matched activity that referred the system + * to this ChooserTargetService + * @param matchedFilter the specific IntentFilter on the component that was matched + * @return a list of deep-link targets to fulfill the intent match, sorted by relevance + */ + public abstract List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName, + IntentFilter matchedFilter); + + @Override + public IBinder onBind(Intent intent) { + if (!SERVICE_INTERFACE.equals(intent.getAction())) { + return null; + } + + if (mWrapper == null) { + mWrapper = new IChooserTargetServiceWrapper(); + } + return mWrapper; + } + + private class IChooserTargetServiceWrapper extends IChooserTargetService.Stub { + @Override + public void getChooserTargets(ComponentName targetComponentName, + IntentFilter matchedFilter, IChooserTargetResult result) throws RemoteException { + List<ChooserTarget> targets = null; + try { + targets = onGetChooserTargets(targetComponentName, matchedFilter); + } finally { + result.sendResult(targets); + } + } + } +} diff --git a/core/java/android/service/chooser/IChooserTargetResult.aidl b/core/java/android/service/chooser/IChooserTargetResult.aidl new file mode 100644 index 0000000..dbd7cbd --- /dev/null +++ b/core/java/android/service/chooser/IChooserTargetResult.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.chooser; + +import android.service.chooser.ChooserTarget; + +/** + * @hide + */ +interface IChooserTargetResult +{ + void sendResult(in List<ChooserTarget> targets); +} diff --git a/core/java/android/service/chooser/IChooserTargetService.aidl b/core/java/android/service/chooser/IChooserTargetService.aidl new file mode 100644 index 0000000..6cfa9a2 --- /dev/null +++ b/core/java/android/service/chooser/IChooserTargetService.aidl @@ -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.service.chooser; + +import android.content.ComponentName; +import android.content.IntentFilter; +import android.service.chooser.IChooserTargetResult; + +/** + * @hide + */ +oneway interface IChooserTargetService +{ + void getChooserTargets(in ComponentName targetComponentName, + in IntentFilter matchedFilter, IChooserTargetResult result); +}
\ No newline at end of file diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 77ef1da..1bdaef0 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -270,22 +270,30 @@ public class DynamicLayout extends Layout // generate new layout for affected text StaticLayout reflowed; + StaticLayout.Builder b; synchronized (sLock) { reflowed = sStaticLayout; + b = sBuilder; sStaticLayout = null; + sBuilder = null; } + // TODO: make sure reflowed is properly initialized if (reflowed == null) { reflowed = new StaticLayout(null); - } else { - reflowed.prepare(); - } - - reflowed.generate(text, where, where + after, - getPaint(), getWidth(), getTextDirectionHeuristic(), getSpacingMultiplier(), - getSpacingAdd(), false, - true, mEllipsizedWidth, mEllipsizeAt); + b = StaticLayout.Builder.obtain(); + } + + b.setText(text, where, where + after) + .setPaint(getPaint()) + .setWidth(getWidth()) + .setTextDir(getTextDirectionHeuristic()) + .setSpacingMult(getSpacingMultiplier()) + .setSpacingAdd(getSpacingAdd()) + .setEllipsizedWidth(mEllipsizedWidth) + .setEllipsize(mEllipsizeAt); + reflowed.generate(b, false, true); int n = reflowed.getLineCount(); // If the new layout has a blank line at the end, but it is not @@ -359,9 +367,10 @@ public class DynamicLayout extends Layout updateBlocks(startline, endline - 1, n); + b.finish(); synchronized (sLock) { sStaticLayout = reflowed; - reflowed.finish(); + sBuilder = b; } } @@ -720,7 +729,8 @@ public class DynamicLayout extends Layout private int mTopPadding, mBottomPadding; - private static StaticLayout sStaticLayout = new StaticLayout(null); + private static StaticLayout sStaticLayout = null; + private static StaticLayout.Builder sBuilder = null; private static final Object[] sLock = new Object[0]; diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java index e72e18f..832002c 100644 --- a/core/java/android/text/MeasuredText.java +++ b/core/java/android/text/MeasuredText.java @@ -66,21 +66,28 @@ class MeasuredText { } static MeasuredText recycle(MeasuredText mt) { - mt.mText = null; - if (mt.mLen < 1000) { - synchronized(sLock) { - for (int i = 0; i < sCached.length; ++i) { - if (sCached[i] == null) { - sCached[i] = mt; - mt.mText = null; - break; - } + mt.finish(); + synchronized(sLock) { + for (int i = 0; i < sCached.length; ++i) { + if (sCached[i] == null) { + sCached[i] = mt; + mt.mText = null; + break; } } } return null; } + void finish() { + mText = null; + if (mLen > 1000) { + mWidths = null; + mChars = null; + mLevels = null; + } + } + void setPos(int pos) { mPos = pos - mTextStart; } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index ffb7d36..967e80c 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -28,6 +28,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; import java.util.Arrays; +import java.util.Locale; /** * StaticLayout is a Layout for text that will not be edited after it @@ -43,6 +44,179 @@ public class StaticLayout extends Layout { static final String TAG = "StaticLayout"; + /** + * Builder for static layouts. It would be better if this were a public + * API (as it would offer much greater flexibility for adding new options) + * but for the time being it's just internal. + * + * @hide + */ + public final static class Builder { + private Builder() { + mNativePtr = nNewBuilder(); + } + + static Builder obtain() { + Builder b = null; + synchronized (sLock) { + for (int i = 0; i < sCached.length; i++) { + if (sCached[i] != null) { + b = sCached[i]; + sCached[i] = null; + break; + } + } + } + if (b == null) { + b = new Builder(); + } + + // set default initial values + b.mWidth = 0; + b.mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR; + b.mSpacingMult = 1.0f; + b.mSpacingAdd = 0.0f; + b.mIncludePad = true; + b.mEllipsizedWidth = 0; + b.mEllipsize = null; + b.mMaxLines = Integer.MAX_VALUE; + + b.mMeasuredText = MeasuredText.obtain(); + return b; + } + + static void recycle(Builder b) { + b.mPaint = null; + b.mText = null; + MeasuredText.recycle(b.mMeasuredText); + synchronized (sLock) { + for (int i = 0; i < sCached.length; i++) { + if (sCached[i] == null) { + sCached[i] = b; + break; + } + } + } + } + + // release any expensive state + /* package */ void finish() { + nFinishBuilder(mNativePtr); + mMeasuredText.finish(); + } + + public Builder setText(CharSequence source) { + return setText(source, 0, source.length()); + } + + public Builder setText(CharSequence source, int start, int end) { + mText = source; + mStart = start; + mEnd = end; + return this; + } + + public Builder setPaint(TextPaint paint) { + mPaint = paint; + return this; + } + + public Builder setWidth(int width) { + mWidth = width; + if (mEllipsize == null) { + mEllipsizedWidth = width; + } + return this; + } + + public Builder setTextDir(TextDirectionHeuristic textDir) { + mTextDir = textDir; + return this; + } + + // TODO: combine the following, as they're almost always set together? + public Builder setSpacingMult(float spacingMult) { + mSpacingMult = spacingMult; + return this; + } + + public Builder setSpacingAdd(float spacingAdd) { + mSpacingAdd = spacingAdd; + return this; + } + + public Builder setIncludePad(boolean includePad) { + mIncludePad = includePad; + return this; + } + + // TODO: combine the following? + public Builder setEllipsizedWidth(int ellipsizedWidth) { + mEllipsizedWidth = ellipsizedWidth; + return this; + } + + public Builder setEllipsize(TextUtils.TruncateAt ellipsize) { + mEllipsize = ellipsize; + return this; + } + + public Builder setMaxLines(int maxLines) { + mMaxLines = maxLines; + return this; + } + + /* @hide */ + public void setLocale(Locale locale) { + if (!locale.equals(mLocale)) { + nBuilderSetLocale(mNativePtr, locale.toLanguageTag()); + mLocale = locale; + } + } + + public StaticLayout build() { + // TODO: can optimize based on whether ellipsis is needed + StaticLayout result = new StaticLayout(mText); + result.initFromBuilder(this); + recycle(this); + return result; + } + + @Override + protected void finalize() throws Throwable { + try { + nFreeBuilder(mNativePtr); + } finally { + super.finalize(); + } + } + + /* package */ long mNativePtr; + + CharSequence mText; + int mStart; + int mEnd; + TextPaint mPaint; + int mWidth; + TextDirectionHeuristic mTextDir; + float mSpacingMult; + float mSpacingAdd; + boolean mIncludePad; + int mEllipsizedWidth; + TextUtils.TruncateAt mEllipsize; + int mMaxLines; + + Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); + + // This will go away and be subsumed by native builder code + MeasuredText mMeasuredText; + + Locale mLocale; + + private static final Object sLock = new Object(); + private static final Builder[] sCached = new Builder[3]; + } + public StaticLayout(CharSequence source, TextPaint paint, int width, Alignment align, float spacingmult, float spacingadd, @@ -110,6 +284,17 @@ public class StaticLayout extends Layout { : new Ellipsizer(source), paint, outerwidth, align, textDir, spacingmult, spacingadd); + Builder b = Builder.obtain(); + b.setText(source, bufstart, bufend) + .setPaint(paint) + .setWidth(outerwidth) + .setTextDir(textDir) + .setSpacingMult(spacingmult) + .setSpacingAdd(spacingadd) + .setIncludePad(includepad) + .setEllipsizedWidth(ellipsizedWidth) + .setEllipsize(ellipsize) + .setMaxLines(maxLines); /* * This is annoying, but we can't refer to the layout until * superclass construction is finished, and the superclass @@ -136,14 +321,9 @@ public class StaticLayout extends Layout { mLines = new int[mLineDirections.length]; mMaximumVisibleLineCount = maxLines; - mMeasured = MeasuredText.obtain(); - - generate(source, bufstart, bufend, paint, outerwidth, textDir, spacingmult, - spacingadd, includepad, includepad, ellipsizedWidth, - ellipsize); + initFromBuilder(b); - mMeasured = MeasuredText.recycle(mMeasured); - mFontMetricsInt = null; + Builder.recycle(b); } /* package */ StaticLayout(CharSequence text) { @@ -152,33 +332,40 @@ public class StaticLayout extends Layout { mColumns = COLUMNS_ELLIPSIZE; mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns); mLines = new int[mLineDirections.length]; - // FIXME This is never recycled - mMeasured = MeasuredText.obtain(); } - /* package */ void generate(CharSequence source, int bufStart, int bufEnd, - TextPaint paint, int outerWidth, - TextDirectionHeuristic textDir, float spacingmult, - float spacingadd, boolean includepad, - boolean trackpad, float ellipsizedWidth, - TextUtils.TruncateAt ellipsize) { - LineBreaks lineBreaks = new LineBreaks(); + private void initFromBuilder(Builder b) { + generate(b, b.mIncludePad, b.mIncludePad); + } + + /* package */ void generate(Builder b, boolean includepad, boolean trackpad) { + CharSequence source = b.mText; + int bufStart = b.mStart; + int bufEnd = b.mEnd; + TextPaint paint = b.mPaint; + int outerWidth = b.mWidth; + TextDirectionHeuristic textDir = b.mTextDir; + float spacingmult = b.mSpacingMult; + float spacingadd = b.mSpacingAdd; + float ellipsizedWidth = b.mEllipsizedWidth; + TextUtils.TruncateAt ellipsize = b.mEllipsize; + LineBreaks lineBreaks = new LineBreaks(); // TODO: move to builder to avoid allocation costs // store span end locations int[] spanEndCache = new int[4]; // store fontMetrics per span range // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range) int[] fmCache = new int[4 * 4]; - final String localeLanguageTag = paint.getTextLocale().toLanguageTag(); + b.setLocale(paint.getTextLocale()); // TODO: also respect LocaleSpan within the text mLineCount = 0; int v = 0; boolean needMultiply = (spacingmult != 1 || spacingadd != 0); - Paint.FontMetricsInt fm = mFontMetricsInt; + Paint.FontMetricsInt fm = b.mFontMetricsInt; int[] chooseHtv = null; - MeasuredText measured = mMeasured; + MeasuredText measured = b.mMeasuredText; Spanned spanned = null; if (source instanceof Spanned) @@ -306,7 +493,7 @@ public class StaticLayout extends Layout { } } - int breakCount = nComputeLineBreaks(localeLanguageTag, chs, widths, paraEnd - paraStart, firstWidth, + int breakCount = nComputeLineBreaks(b.mNativePtr, chs, widths, paraEnd - paraStart, firstWidth, firstWidthLineCount, restWidth, variableTabStops, TAB_INCREMENT, false, lineBreaks, lineBreaks.breaks, lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length); @@ -746,24 +933,21 @@ public class StaticLayout extends Layout { return mEllipsizedWidth; } - void prepare() { - mMeasured = MeasuredText.obtain(); - } - - void finish() { - mMeasured = MeasuredText.recycle(mMeasured); - } - // populates LineBreaks and returns the number of breaks found // // the arrays inside the LineBreaks objects are passed in as well // to reduce the number of JNI calls in the common case where the // arrays do not have to be resized - private static native int nComputeLineBreaks(String locale, char[] text, float[] widths, + private static native int nComputeLineBreaks(long nativePtr, char[] text, float[] widths, int length, float firstWidth, int firstWidthLineCount, float restWidth, int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle, int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength); + private static native long nNewBuilder(); + private static native void nFreeBuilder(long nativePtr); + private static native void nFinishBuilder(long nativePtr); + private static native void nBuilderSetLocale(long nativePtr, String locale); + private int mLineCount; private int mTopPadding, mBottomPadding; private int mColumns; @@ -793,12 +977,6 @@ public class StaticLayout extends Layout { private static final double EXTRA_ROUNDING = 0.5; - /* - * This is reused across calls to generate() - */ - private MeasuredText mMeasured; - private Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); - // This is used to return three arrays from a single JNI call when // performing line breaking /*package*/ static class LineBreaks { diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java index 0447117..4f8cff0 100644 --- a/core/java/android/text/TextPaint.java +++ b/core/java/android/text/TextPaint.java @@ -16,6 +16,7 @@ package android.text; +import android.annotation.ColorInt; import android.graphics.Paint; /** @@ -25,8 +26,10 @@ import android.graphics.Paint; public class TextPaint extends Paint { // Special value 0 means no background paint + @ColorInt public int bgColor; public int baselineShift; + @ColorInt public int linkColor; public int[] drawableState; public float density = 1.0f; @@ -34,6 +37,7 @@ public class TextPaint extends Paint { * Special value 0 means no custom underline * @hide */ + @ColorInt public int underlineColor = 0; /** * Defined as a multiplier of the default underline thickness. Use 1.0f for default thickness. diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java index c9e09bd..f167aab 100644 --- a/core/java/android/text/style/ForegroundColorSpan.java +++ b/core/java/android/text/style/ForegroundColorSpan.java @@ -16,6 +16,7 @@ package android.text.style; +import android.annotation.ColorInt; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextPaint; @@ -26,7 +27,7 @@ public class ForegroundColorSpan extends CharacterStyle private final int mColor; - public ForegroundColorSpan(int color) { + public ForegroundColorSpan(@ColorInt int color) { mColor = color; } @@ -46,6 +47,7 @@ public class ForegroundColorSpan extends CharacterStyle dest.writeInt(mColor); } + @ColorInt public int getForegroundColor() { return mColor; } diff --git a/core/java/android/text/style/QuoteSpan.java b/core/java/android/text/style/QuoteSpan.java index 29dd273..17748ca 100644 --- a/core/java/android/text/style/QuoteSpan.java +++ b/core/java/android/text/style/QuoteSpan.java @@ -16,6 +16,7 @@ package android.text.style; +import android.annotation.ColorInt; import android.graphics.Paint; import android.graphics.Canvas; import android.os.Parcel; @@ -34,7 +35,7 @@ public class QuoteSpan implements LeadingMarginSpan, ParcelableSpan { mColor = 0xff0000ff; } - public QuoteSpan(int color) { + public QuoteSpan(@ColorInt int color) { super(); mColor = color; } @@ -55,6 +56,7 @@ public class QuoteSpan implements LeadingMarginSpan, ParcelableSpan { dest.writeInt(mColor); } + @ColorInt public int getColor() { return mColor; } diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java index 68f725e..7da3941 100644 --- a/core/java/android/util/ArraySet.java +++ b/core/java/android/util/ArraySet.java @@ -475,6 +475,26 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { } /** + * Perform a {@link #remove(Object)} of all values in <var>array</var> + * @param array The array whose contents are to be removed. + */ + public boolean removeAll(ArraySet<? extends E> array) { + // TODO: If array is sufficiently large, a marking approach might be beneficial. In a first + // pass, use the property that the sets are sorted by hash to make this linear passes + // (except for hash collisions, which means worst case still n*m), then do one + // collection pass into a new array. This avoids binary searches and excessive memcpy. + final int N = array.mSize; + + // Note: ArraySet does not make thread-safety guarantees. So instead of OR-ing together all + // the single results, compare size before and after. + final int originalSize = mSize; + for (int i = 0; i < N; i++) { + remove(array.valueAt(i)); + } + return originalSize != mSize; + } + + /** * Return the number of items in this array map. */ @Override diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java index f607207..84d9ce8 100644 --- a/core/java/android/util/DebugUtils.java +++ b/core/java/android/util/DebugUtils.java @@ -16,6 +16,7 @@ package android.util; +import java.io.PrintWriter; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.util.Locale; @@ -123,4 +124,83 @@ public class DebugUtils { } } + /** @hide */ + public static void printSizeValue(PrintWriter pw, long number) { + float result = number; + String suffix = ""; + if (result > 900) { + suffix = "KB"; + result = result / 1024; + } + if (result > 900) { + suffix = "MB"; + result = result / 1024; + } + if (result > 900) { + suffix = "GB"; + result = result / 1024; + } + if (result > 900) { + suffix = "TB"; + result = result / 1024; + } + if (result > 900) { + suffix = "PB"; + result = result / 1024; + } + String value; + if (result < 1) { + value = String.format("%.2f", result); + } else if (result < 10) { + value = String.format("%.1f", result); + } else if (result < 100) { + value = String.format("%.0f", result); + } else { + value = String.format("%.0f", result); + } + pw.print(value); + pw.print(suffix); + } + + /** @hide */ + public static String sizeValueToString(long number, StringBuilder outBuilder) { + if (outBuilder == null) { + outBuilder = new StringBuilder(32); + } + float result = number; + String suffix = ""; + if (result > 900) { + suffix = "KB"; + result = result / 1024; + } + if (result > 900) { + suffix = "MB"; + result = result / 1024; + } + if (result > 900) { + suffix = "GB"; + result = result / 1024; + } + if (result > 900) { + suffix = "TB"; + result = result / 1024; + } + if (result > 900) { + suffix = "PB"; + result = result / 1024; + } + String value; + if (result < 1) { + value = String.format("%.2f", result); + } else if (result < 10) { + value = String.format("%.1f", result); + } else if (result < 100) { + value = String.format("%.0f", result); + } else { + value = String.format("%.0f", result); + } + outBuilder.append(value); + outBuilder.append(suffix); + return outBuilder.toString(); + } } diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 0d36949..06e196d 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -204,7 +204,7 @@ class GLES20Canvas extends HardwareCanvas { Bitmap bitmap = patch.getBitmap(); throwIfCannotDraw(bitmap); final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); - nDrawPatch(mNativeCanvasWrapper, bitmap.mNativeBitmap, patch.mNativeChunk, + nDrawPatch(mNativeCanvasWrapper, bitmap.getSkBitmap(), patch.mNativeChunk, dst.left, dst.top, dst.right, dst.bottom, nativePaint); } @@ -214,7 +214,7 @@ class GLES20Canvas extends HardwareCanvas { Bitmap bitmap = patch.getBitmap(); throwIfCannotDraw(bitmap); final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); - nDrawPatch(mNativeCanvasWrapper, bitmap.mNativeBitmap, patch.mNativeChunk, + nDrawPatch(mNativeCanvasWrapper, bitmap.getSkBitmap(), patch.mNativeChunk, dst.left, dst.top, dst.right, dst.bottom, nativePaint); } diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index df0838f..69b4c47 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -364,7 +364,7 @@ public class ThreadedRenderer extends HardwareRenderer { @Override boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) { return nCopyLayerInto(mNativeProxy, - layer.getDeferredLayerUpdater(), bitmap.mNativeBitmap); + layer.getDeferredLayerUpdater(), bitmap.getSkBitmap()); } @Override @@ -465,7 +465,7 @@ public class ThreadedRenderer extends HardwareRenderer { for (int i = 0; i < count; i++) { drawables.valueAt(i).addAtlasableBitmaps(tmpList); for (int j = 0; j < tmpList.size(); j++) { - preloadedPointers.add(tmpList.get(j).mNativeBitmap); + preloadedPointers.add(tmpList.get(j).getSkBitmap()); } tmpList.clear(); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f25b640..7d49969 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -18,11 +18,15 @@ package android.view; import android.animation.AnimatorInflater; import android.animation.StateListAnimator; +import android.annotation.CallSuper; +import android.annotation.ColorInt; import android.annotation.DrawableRes; +import android.annotation.FloatRange; import android.annotation.IdRes; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.Size; import android.content.ClipData; import android.content.Context; import android.content.res.ColorStateList; @@ -5148,6 +5152,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * passed in as finer grained information about where the focus is coming * from (in addition to direction). Will be <code>null</code> otherwise. */ + @CallSuper protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction, @Nullable Rect previouslyFocusedRect) { if (gainFocus) { @@ -5370,6 +5375,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #sendAccessibilityEvent(int) * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent) */ + @CallSuper public void onPopulateAccessibilityEvent(AccessibilityEvent event) { if (mAccessibilityDelegate != null) { mAccessibilityDelegate.onPopulateAccessibilityEvent(this, event); @@ -5414,6 +5420,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #sendAccessibilityEvent(int) * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent) */ + @CallSuper public void onInitializeAccessibilityEvent(AccessibilityEvent event) { if (mAccessibilityDelegate != null) { mAccessibilityDelegate.onInitializeAccessibilityEvent(this, event); @@ -5528,6 +5535,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @param info The instance to initialize. */ + @CallSuper public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { if (mAccessibilityDelegate != null) { mAccessibilityDelegate.onInitializeAccessibilityNodeInfo(this, info); @@ -5849,7 +5857,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see AccessibilityDelegate */ - public void setAccessibilityDelegate(AccessibilityDelegate delegate) { + public void setAccessibilityDelegate(@Nullable AccessibilityDelegate delegate) { mAccessibilityDelegate = delegate; } @@ -6089,6 +6097,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @hide pending API council approval */ + @CallSuper protected void onFocusLost() { resetPressedState(); } @@ -10573,7 +10582,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_alpha */ - public void setAlpha(float alpha) { + public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) { ensureTransformationInfo(); if (mTransformationInfo.mAlpha != alpha) { mTransformationInfo.mAlpha = alpha; @@ -13425,6 +13434,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @hide */ + @CallSuper protected void onDetachedFromWindowInternal() { mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT; mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT; @@ -14039,6 +14049,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @hide */ + @CallSuper protected void destroyHardwareResources() { // Although the Layer will be destroyed by RenderNode, we want to release // the staging display list, which is also a signal to RenderNode that it's @@ -14311,7 +14322,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #buildDrawingCache() * @see #getDrawingCache() */ - public void setDrawingCacheBackgroundColor(int color) { + public void setDrawingCacheBackgroundColor(@ColorInt int color) { if (color != mDrawingCacheBackgroundColor) { mDrawingCacheBackgroundColor = color; mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; @@ -14323,6 +14334,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return The background color to used for the drawing cache's bitmap */ + @ColorInt public int getDrawingCacheBackgroundColor() { return mDrawingCacheBackgroundColor; } @@ -15227,6 +15239,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @param canvas The Canvas to which the View is rendered. */ + @CallSuper public void draw(Canvas canvas) { final int privateFlags = mPrivateFlags; final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE && @@ -15536,6 +15549,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The known solid color background for this view, or 0 if the color may vary */ @ViewDebug.ExportedProperty(category = "drawing") + @ColorInt public int getSolidColor() { return 0; } @@ -15828,6 +15842,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <p>Even if the subclass overrides onFinishInflate, they should always be * sure to call the super method, so that we get called. */ + @CallSuper protected void onFinishInflate() { } @@ -15993,6 +16008,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #unscheduleDrawable(android.graphics.drawable.Drawable) * @see #drawableStateChanged() */ + @CallSuper protected boolean verifyDrawable(Drawable who) { return who == mBackground || (mScrollCache != null && mScrollCache.scrollBar == who); } @@ -16008,6 +16024,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see Drawable#setState(int[]) */ + @CallSuper protected void drawableStateChanged() { final int[] state = getDrawableState(); @@ -16040,6 +16057,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param x hotspot x coordinate * @param y hotspot y coordinate */ + @CallSuper public void drawableHotspotChanged(float x, float y) { if (mBackground != null) { mBackground.setHotspot(x, y); @@ -16222,7 +16240,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param color the color of the background */ @RemotableViewMethod - public void setBackgroundColor(int color) { + public void setBackgroundColor(@ColorInt int color) { if (mBackground instanceof ColorDrawable) { ((ColorDrawable) mBackground.mutate()).setColor(color); computeOpaqueFlags(); @@ -16238,6 +16256,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return The color of the ColorDrawable background, if set, otherwise 0. */ + @ColorInt public int getBackgroundColor() { if (mBackground instanceof ColorDrawable) { return ((ColorDrawable) mBackground).getColor(); @@ -17011,7 +17030,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @param location an array of two integers in which to hold the coordinates */ - public void getLocationOnScreen(int[] location) { + public void getLocationOnScreen(@Size(2) int[] location) { getLocationInWindow(location); final AttachInfo info = mAttachInfo; @@ -17028,7 +17047,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @param location an array of two integers in which to hold the coordinates */ - public void getLocationInWindow(int[] location) { + public void getLocationInWindow(@Size(2) int[] location) { if (location == null || location.length < 2) { throw new IllegalArgumentException("location must be an array of two integers"); } @@ -17512,6 +17531,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <p>Subclasses which override this method should call the superclass method to * handle possible request-during-layout errors correctly.</p> */ + @CallSuper public void requestLayout() { if (mMeasureCache != null) mMeasureCache.clear(); @@ -18871,7 +18891,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #dispatchNestedPreScroll(int, int, int[], int[]) */ public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, - int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) { + int dxUnconsumed, int dyUnconsumed, @Nullable @Size(2) int[] offsetInWindow) { if (isNestedScrollingEnabled() && mNestedScrollingParent != null) { if (dxConsumed != 0 || dyConsumed != 0 || dxUnconsumed != 0 || dyUnconsumed != 0) { int startX = 0; @@ -18919,7 +18939,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return true if the parent consumed some or all of the scroll delta * @see #dispatchNestedScroll(int, int, int, int, int[]) */ - public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { + public boolean dispatchNestedPreScroll(int dx, int dy, + @Nullable @Size(2) int[] consumed, @Nullable @Size(2) int[] offsetInWindow) { if (isNestedScrollingEnabled() && mNestedScrollingParent != null) { if (dx != 0 || dy != 0) { int startX = 0; diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index e332135..744f665 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.IdRes; import android.annotation.LayoutRes; @@ -1069,7 +1070,7 @@ public abstract class Window { public abstract void setTitle(CharSequence title); @Deprecated - public abstract void setTitleColor(int textColor); + public abstract void setTitleColor(@ColorInt int textColor); public abstract void openPanel(int featureId, KeyEvent event); @@ -1835,6 +1836,7 @@ public abstract class Window { /** * @return the color of the status bar. */ + @ColorInt public abstract int getStatusBarColor(); /** @@ -1852,11 +1854,12 @@ public abstract class Window { * The transitionName for the view background will be "android:status:background". * </p> */ - public abstract void setStatusBarColor(int color); + public abstract void setStatusBarColor(@ColorInt int color); /** * @return the color of the navigation bar. */ + @ColorInt public abstract int getNavigationBarColor(); /** @@ -1874,7 +1877,7 @@ public abstract class Window { * The transitionName for the view background will be "android:navigation:background". * </p> */ - public abstract void setNavigationBarColor(int color); + public abstract void setNavigationBarColor(@ColorInt int color); } diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index 85d77cb..a5524d8 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -16,6 +16,7 @@ package android.view.animation; +import android.annotation.ColorInt; import android.content.Context; import android.content.res.TypedArray; import android.graphics.RectF; @@ -622,7 +623,7 @@ public abstract class Animation implements Cloneable { * @param bg The background color. If 0, no background. Currently must * be black, with any desired alpha level. */ - public void setBackgroundColor(int bg) { + public void setBackgroundColor(@ColorInt int bg) { mBackgroundColor = bg; } @@ -753,6 +754,7 @@ public abstract class Animation implements Cloneable { /** * Returns the background color behind the animation. */ + @ColorInt public int getBackgroundColor() { return mBackgroundColor; } diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java index 30c12ed..8eb5b5c 100644 --- a/core/java/android/view/animation/Transformation.java +++ b/core/java/android/view/animation/Transformation.java @@ -16,6 +16,7 @@ package android.view.animation; +import android.annotation.FloatRange; import android.graphics.Matrix; import android.graphics.Rect; @@ -163,7 +164,7 @@ public class Transformation { * Sets the degree of transparency * @param alpha 1.0 means fully opaqe and 0.0 means fully transparent */ - public void setAlpha(float alpha) { + public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) { mAlpha = alpha; } diff --git a/core/java/android/webkit/WebResourceError.java b/core/java/android/webkit/WebResourceError.java new file mode 100644 index 0000000..080d174 --- /dev/null +++ b/core/java/android/webkit/WebResourceError.java @@ -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.webkit; + +/** + * Encapsulates information about errors occured during loading of web resources. See + * {@link WebViewClient#onReceivedError(WebView, WebResourceRequest, WebResourceError) WebViewClient.onReceivedError(WebView, WebResourceRequest, WebResourceError)} + */ +public abstract class WebResourceError { + /** + * Gets the error code of the error. The code corresponds to one + * of the ERROR_* constants in {@link WebViewClient}. + * + * @return The error code of the error + */ + public abstract int getErrorCode(); + + /** + * Gets the string describing the error. Descriptions are localized, + * and thus can be used for communicating the problem to the user. + * + * @return The description of the error + */ + public abstract String getDescription(); +} diff --git a/core/java/android/webkit/WebResourceResponse.java b/core/java/android/webkit/WebResourceResponse.java index f487a4e..a42aaa7 100644 --- a/core/java/android/webkit/WebResourceResponse.java +++ b/core/java/android/webkit/WebResourceResponse.java @@ -25,7 +25,7 @@ import java.util.Map; * class from {@link WebViewClient#shouldInterceptRequest} to provide a custom * response when the WebView requests a particular resource. */ -public class WebResourceResponse { +public class WebResourceResponse extends WebResourceResponseBase { private String mMimeType; private String mEncoding; private int mStatusCode; @@ -75,38 +75,36 @@ public class WebResourceResponse { } /** - * Sets the resource response's MIME type, for example text/html. + * Sets the resource response's MIME type, for example "text/html". * - * @param mimeType the resource response's MIME type + * @param mimeType The resource response's MIME type */ public void setMimeType(String mimeType) { mMimeType = mimeType; } /** - * Gets the resource response's MIME type. - * - * @return the resource response's MIME type + * {@inheritDoc} */ + @Override public String getMimeType() { return mMimeType; } /** - * Sets the resource response's encoding, for example UTF-8. This is used + * Sets the resource response's encoding, for example "UTF-8". This is used * to decode the data from the input stream. * - * @param encoding the resource response's encoding + * @param encoding The resource response's encoding */ public void setEncoding(String encoding) { mEncoding = encoding; } /** - * Gets the resource response's encoding. - * - * @return the resource response's encoding + * {@inheritDoc} */ + @Override public String getEncoding() { return mEncoding; } @@ -142,19 +140,17 @@ public class WebResourceResponse { } /** - * Gets the resource response's status code. - * - * @return the resource response's status code. + * {@inheritDoc} */ + @Override public int getStatusCode() { return mStatusCode; } /** - * Gets the description of the resource response's status code. - * - * @return the description of the resource response's status code. + * {@inheritDoc} */ + @Override public String getReasonPhrase() { return mReasonPhrase; } @@ -162,17 +158,16 @@ public class WebResourceResponse { /** * Sets the headers for the resource response. * - * @param headers mapping of header name -> header value. + * @param headers Mapping of header name -> header value. */ public void setResponseHeaders(Map<String, String> headers) { mResponseHeaders = headers; } /** - * Gets the headers for the resource response. - * - * @return the headers for the resource response. + * {@inheritDoc} */ + @Override public Map<String, String> getResponseHeaders() { return mResponseHeaders; } @@ -190,15 +185,13 @@ public class WebResourceResponse { throw new IllegalArgumentException("StringBufferInputStream is deprecated and must " + "not be passed to a WebResourceResponse"); } - mInputStream = data; } /** - * Gets the input stream that provides the resource response's data. - * - * @return the input stream that provides the resource response's data + * {@inheritDoc} */ + @Override public InputStream getData() { return mInputStream; } diff --git a/core/java/android/webkit/WebResourceResponseBase.java b/core/java/android/webkit/WebResourceResponseBase.java new file mode 100644 index 0000000..cffde82 --- /dev/null +++ b/core/java/android/webkit/WebResourceResponseBase.java @@ -0,0 +1,68 @@ +/* + * 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.webkit; + +import java.io.InputStream; +import java.util.Map; + +/** + * Encapsulates a resource response received from the server. + * This is an abstract class used by WebView callbacks. + */ +public abstract class WebResourceResponseBase { + /** + * Gets the resource response's MIME type. + * + * @return The resource response's MIME type + */ + public abstract String getMimeType(); + + /** + * Gets the resource response's encoding. + * + * @return The resource response's encoding + */ + public abstract String getEncoding(); + + /** + * Gets the resource response's status code. + * + * @return The resource response's status code. + */ + public abstract int getStatusCode(); + + /** + * Gets the description of the resource response's status code. + * + * @return The description of the resource response's status code. + */ + public abstract String getReasonPhrase(); + + /** + * Gets the headers for the resource response. + * + * @return The headers for the resource response. + */ + public abstract Map<String, String> getResponseHeaders(); + + /** + * Gets the input stream that provides the resource response's data. + * + * @return The input stream that provides the resource response's data + */ + public abstract InputStream getData(); +} diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 1d2c311..3df1293 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -1285,7 +1285,7 @@ public abstract class WebSettings { * strongly discouraged. * * @param mode The mixed content mode to use. One of {@link #MIXED_CONTENT_NEVER_ALLOW}, - * {@link #MIXED_CONTENT_NEVER_ALLOW} or {@link #MIXED_CONTENT_COMPATIBILITY_MODE}. + * {@link #MIXED_CONTENT_ALWAYS_ALLOW} or {@link #MIXED_CONTENT_COMPATIBILITY_MODE}. */ public abstract void setMixedContentMode(int mode); @@ -1293,7 +1293,7 @@ public abstract class WebSettings { * Gets the current behavior of the WebView with regard to loading insecure content from a * secure origin. * @return The current setting, one of {@link #MIXED_CONTENT_NEVER_ALLOW}, - * {@link #MIXED_CONTENT_NEVER_ALLOW} or {@link #MIXED_CONTENT_COMPATIBILITY_MODE}. + * {@link #MIXED_CONTENT_ALWAYS_ALLOW} or {@link #MIXED_CONTENT_COMPATIBILITY_MODE}. */ public abstract int getMixedContentMode(); diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 01f9b37..34b8cf6 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -174,6 +174,8 @@ public class WebViewClient { public static final int ERROR_FILE_NOT_FOUND = -14; /** Too many requests during this load */ public static final int ERROR_TOO_MANY_REQUESTS = -15; + /** Request blocked by the browser */ + public static final int ERROR_BLOCKED = -16; /** * Report an error to the host application. These errors are unrecoverable @@ -183,12 +185,45 @@ public class WebViewClient { * @param errorCode The error code corresponding to an ERROR_* value. * @param description A String describing the error. * @param failingUrl The url that failed to load. + * @deprecated Use {@link #onReceivedError(WebView, WebResourceRequest, WebResourceError) + * onReceivedError(WebView, WebResourceRequest, WebResourceError)} instead. */ + @Deprecated public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { } /** + * Report web resource loading error to the host application. These errors usually indicate + * inability to connect to the server. Note that unlike the deprecated version of the callback, + * the new version will be called for any resource (iframe, image, etc), not just for the main + * page. Thus, it is recommended to perform minimum required work in this callback. + * @param view The WebView that is initiating the callback. + * @param request The originating request. + * @param error Information about the error occured. + */ + public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { + if (request.isForMainFrame()) { + onReceivedError(view, + error.getErrorCode(), error.getDescription(), request.getUrl().toString()); + } + } + + /** + * Notify the host application that an HTTP error has been received from the server while + * loading a resource. HTTP errors have status codes >= 400. This callback will be called + * for any resource (iframe, image, etc), not just for the main page. Thus, it is recommended to + * perform minimum required work in this callback. Note that the content of the server + * response may not be provided within the <b>errorResponse</b> parameter. + * @param view The WebView that is initiating the callback. + * @param request The originating request. + * @param errorResponse Information about the error occured. + */ + public void onReceivedHttpError( + WebView view, WebResourceRequest request, WebResourceResponseBase errorResponse) { + } + + /** * As the host application if the browser should resend data as the * requested page was a result of a POST. The default is to not resend the * data. diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index a6e2952..168066a 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.content.Context; import android.content.Intent; @@ -5982,7 +5983,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * * @param color The background color */ - public void setCacheColorHint(int color) { + public void setCacheColorHint(@ColorInt int color) { if (color != mCacheColorHint) { mCacheColorHint = color; int count = getChildCount(); @@ -6000,6 +6001,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * @return The cache color hint */ @ViewDebug.ExportedProperty(category = "drawing") + @ColorInt public int getCacheColorHint() { return mCacheColorHint; } diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 9b977fa..72cb0b5 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.Nullable; import android.content.Context; import android.database.DataSetObserver; import android.os.Parcelable; @@ -276,7 +277,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { * * @param listener The callback that will be invoked. */ - public void setOnItemClickListener(OnItemClickListener listener) { + public void setOnItemClickListener(@Nullable OnItemClickListener listener) { mOnItemClickListener = listener; } @@ -284,6 +285,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { * @return The callback to be invoked with an item in this AdapterView has * been clicked, or null id no callback has been set. */ + @Nullable public final OnItemClickListener getOnItemClickListener() { return mOnItemClickListener; } @@ -394,10 +396,11 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { * * @param listener The callback that will run */ - public void setOnItemSelectedListener(OnItemSelectedListener listener) { + public void setOnItemSelectedListener(@Nullable OnItemSelectedListener listener) { mOnItemSelectedListener = listener; } + @Nullable public final OnItemSelectedListener getOnItemSelectedListener() { return mOnItemSelectedListener; } diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java index fd1c4b8..5bc16cb 100644 --- a/core/java/android/widget/CalendarView.java +++ b/core/java/android/widget/CalendarView.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.Widget; import android.content.Context; @@ -140,7 +141,7 @@ public class CalendarView extends FrameLayout { * * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor */ - public void setSelectedWeekBackgroundColor(int color) { + public void setSelectedWeekBackgroundColor(@ColorInt int color) { mDelegate.setSelectedWeekBackgroundColor(color); } @@ -151,6 +152,7 @@ public class CalendarView extends FrameLayout { * * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor */ + @ColorInt public int getSelectedWeekBackgroundColor() { return mDelegate.getSelectedWeekBackgroundColor(); } @@ -162,7 +164,7 @@ public class CalendarView extends FrameLayout { * * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor */ - public void setFocusedMonthDateColor(int color) { + public void setFocusedMonthDateColor(@ColorInt int color) { mDelegate.setFocusedMonthDateColor(color); } @@ -173,6 +175,7 @@ public class CalendarView extends FrameLayout { * * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor */ + @ColorInt public int getFocusedMonthDateColor() { return mDelegate.getFocusedMonthDateColor(); } @@ -184,7 +187,7 @@ public class CalendarView extends FrameLayout { * * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor */ - public void setUnfocusedMonthDateColor(int color) { + public void setUnfocusedMonthDateColor(@ColorInt int color) { mDelegate.setUnfocusedMonthDateColor(color); } @@ -195,6 +198,7 @@ public class CalendarView extends FrameLayout { * * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor */ + @ColorInt public int getUnfocusedMonthDateColor() { return mDelegate.getUnfocusedMonthDateColor(); } @@ -206,7 +210,7 @@ public class CalendarView extends FrameLayout { * * @attr ref android.R.styleable#CalendarView_weekNumberColor */ - public void setWeekNumberColor(int color) { + public void setWeekNumberColor(@ColorInt int color) { mDelegate.setWeekNumberColor(color); } @@ -217,6 +221,7 @@ public class CalendarView extends FrameLayout { * * @attr ref android.R.styleable#CalendarView_weekNumberColor */ + @ColorInt public int getWeekNumberColor() { return mDelegate.getWeekNumberColor(); } @@ -228,7 +233,7 @@ public class CalendarView extends FrameLayout { * * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor */ - public void setWeekSeparatorLineColor(int color) { + public void setWeekSeparatorLineColor(@ColorInt int color) { mDelegate.setWeekSeparatorLineColor(color); } @@ -239,6 +244,7 @@ public class CalendarView extends FrameLayout { * * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor */ + @ColorInt public int getWeekSeparatorLineColor() { return mDelegate.getWeekSeparatorLineColor(); } diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index 391347e..9019f31 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.ColorInt; import android.content.res.TypedArray; import android.graphics.Paint; import android.graphics.PorterDuff; @@ -292,7 +293,7 @@ public class EdgeEffect { * * @param color Color in argb */ - public void setColor(int color) { + public void setColor(@ColorInt int color) { mPaint.setColor(color); } @@ -300,6 +301,7 @@ public class EdgeEffect { * Return the color of this edge effect in argb. * @return The color of this edge effect in argb */ + @ColorInt public int getColor() { return mPaint.getColor(); } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index f8e207f..8197b3d 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -128,7 +128,7 @@ public class Editor { // Each Editor manages its own undo stack. private final UndoManager mUndoManager = new UndoManager(); private UndoOwner mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this); - final InputFilter mUndoInputFilter = new UndoInputFilter(this); + final UndoInputFilter mUndoInputFilter = new UndoInputFilter(this); boolean mAllowUndo = true; // Cursor Controllers. @@ -217,6 +217,12 @@ public class Editor { WordIterator mWordIterator; SpellChecker mSpellChecker; + // This word iterator is set with text and used to determine word boundaries + // when a user is selecting text. + private WordIterator mWordIteratorWithText; + // Indicate that the text in the word iterator needs to be updated. + private boolean mUpdateWordIteratorText; + private Rect mTempRect; private TextView mTextView; @@ -240,6 +246,15 @@ public class Editor { mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this); } + /** + * Forgets all undo and redo operations for this Editor. + */ + void forgetUndoRedo() { + UndoOwner[] owners = { mUndoOwner }; + mUndoManager.forgetUndos(owners, -1 /* all */); + mUndoManager.forgetRedos(owners, -1 /* all */); + } + boolean canUndo() { UndoOwner[] owners = { mUndoOwner }; return mAllowUndo && mUndoManager.countUndos(owners) > 0; @@ -689,9 +704,52 @@ public class Editor { return mTextView.getTransformationMethod() instanceof PasswordTransformationMethod; } + private int getWordStart(int offset) { + // FIXME - For this and similar methods we're not doing anything to check if there's + // a LocaleSpan in the text, this may be something we should try handling or checking for. + int retOffset = getWordIteratorWithText().getBeginning(offset); + if (retOffset == BreakIterator.DONE) retOffset = offset; + return retOffset; + } + + private int getWordEnd(int offset, boolean includePunctuation) { + int retOffset = getWordIteratorWithText().getEnd(offset); + if (retOffset == BreakIterator.DONE) { + retOffset = offset; + } else if (includePunctuation) { + retOffset = handlePunctuation(retOffset); + } + return retOffset; + } + + private boolean isEndBoundary(int offset) { + int thisEnd = getWordEnd(offset, false); + return offset == thisEnd; + } + + private boolean isStartBoundary(int offset) { + int thisStart = getWordStart(offset); + return thisStart == offset; + } + + private int handlePunctuation(int offset) { + // FIXME - Check with UX how repeated ending punctuation should be handled. + // FIXME - Check with UX if / how we would handle non sentence ending characters. + // FIXME - Consider punctuation in different languages. + CharSequence text = mTextView.getText(); + if (offset < text.length()) { + int c = Character.codePointAt(text, offset); + if (c == 0x002e /* period */|| c == 0x003f /* question mark */ + || c == 0x0021 /* exclamation mark */) { + offset = Character.offsetByCodePoints(text, offset, 1); + } + } + return offset; + } + /** - * Adjusts selection to the word under last touch offset. - * Return true if the operation was successfully performed. + * Adjusts selection to the word under last touch offset. Return true if the operation was + * successfully performed. */ private boolean selectCurrentWord() { if (!canSelectText()) { @@ -738,6 +796,8 @@ public class Editor { selectionStart = ((Spanned) mTextView.getText()).getSpanStart(urlSpan); selectionEnd = ((Spanned) mTextView.getText()).getSpanEnd(urlSpan); } else { + // FIXME - We should check if there's a LocaleSpan in the text, this may be + // something we should try handling or checking for. final WordIterator wordIterator = getWordIterator(); wordIterator.setCharSequence(mTextView.getText(), minOffset, maxOffset); @@ -760,6 +820,7 @@ public class Editor { void onLocaleChanged() { // Will be re-created on demand in getWordIterator with the proper new locale mWordIterator = null; + mWordIteratorWithText = null; } /** @@ -772,6 +833,23 @@ public class Editor { return mWordIterator; } + private WordIterator getWordIteratorWithText() { + if (mWordIteratorWithText == null) { + mWordIteratorWithText = new WordIterator(mTextView.getTextServicesLocale()); + mUpdateWordIteratorText = true; + } + if (mUpdateWordIteratorText) { + // FIXME - Shouldn't copy all of the text as only the area of the text relevant + // to the user's selection is needed. A possible solution would be to + // copy some number N of characters near the selection and then when the + // user approaches N then we'd do another copy of the next N characters. + CharSequence text = mTextView.getText(); + mWordIteratorWithText.setCharSequence(text, 0, text.length()); + mUpdateWordIteratorText = false; + } + return mWordIteratorWithText; + } + private long getCharRange(int offset) { final int textLength = mTextView.getText().length(); if (offset + 1 < textLength) { @@ -920,9 +998,8 @@ public class Editor { mTextView.startDrag(data, getTextThumbnailBuilder(selectedText), localState, 0); stopSelectionActionMode(); } else { - getSelectionController().hide(); - selectCurrentWord(); - getSelectionController().show(); + stopSelectionActionMode(); + startSelectionActionMode(); } handled = true; } @@ -1058,6 +1135,9 @@ public class Editor { void sendOnTextChanged(int start, int after) { updateSpellCheckSpans(start, start + after, false); + // Flip flag to indicate the word iterator needs to have the text reset. + mUpdateWordIteratorText = true; + // Hide the controllers as soon as text is modified (typing, procedural...) // We do not hide the span controllers, since they can be added when a new text is // inserted into the text view (voice IME). @@ -1143,6 +1223,7 @@ public class Editor { ims.mChangedEnd = EXTRACT_UNKNOWN; ims.mContentChanged = false; } + mUndoInputFilter.beginBatchEdit(); mTextView.onBeginBatchEdit(); } } @@ -1169,6 +1250,7 @@ public class Editor { void finishBatchEdit(final InputMethodState ims) { mTextView.onEndBatchEdit(); + mUndoInputFilter.endBatchEdit(); if (ims.mContentChanged || ims.mSelectionModeChanged) { mTextView.updateAfterEdit(); @@ -1613,6 +1695,9 @@ public class Editor { } } + if (selectionStarted) { + getSelectionController().enterDrag(); + } return selectionStarted; } @@ -2894,7 +2979,6 @@ public class Editor { } if (menu.hasVisibleItems() || mode.getCustomView() != null) { - getSelectionController().show(); mTextView.setHasTransientState(true); return true; } else { @@ -3232,6 +3316,8 @@ public class Editor { private Runnable mActionPopupShower; // Minimum touch target size for handles private int mMinSize; + // Indicates the line of text that the handle is on. + protected int mLine = -1; public HandleView(Drawable drawableLtr, Drawable drawableRtl) { super(mTextView.getContext()); @@ -3407,6 +3493,7 @@ public class Editor { addPositionToTouchUpFilter(offset); } final int line = layout.getLineForOffset(offset); + mLine = line; mPositionX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX - getHorizontalOffset() + getCursorOffset()); @@ -3456,6 +3543,30 @@ public class Editor { } } + public void showAtLocation(int offset) { + // TODO - investigate if there's a better way to show the handles + // after the drag accelerator has occured. + int[] tmpCords = new int[2]; + mTextView.getLocationInWindow(tmpCords); + + Layout layout = mTextView.getLayout(); + int posX = tmpCords[0]; + int posY = tmpCords[1]; + + final int line = layout.getLineForOffset(offset); + + int startX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f + - mHotspotX - getHorizontalOffset() + getCursorOffset()); + int startY = layout.getLineBottom(line); + + // Take TextView's padding and scroll into account. + startX += mTextView.viewportToContentHorizontalOffset(); + startY += mTextView.viewportToContentVerticalOffset(); + + mContainer.showAtLocation(mTextView, Gravity.NO_GRAVITY, + startX + posX, startY + posY); + } + @Override protected void onDraw(Canvas c) { final int drawWidth = mDrawable.getIntrinsicWidth(); @@ -3694,6 +3805,12 @@ public class Editor { } private class SelectionStartHandleView extends HandleView { + // The previous offset this handle was at. + private int mPrevOffset; + // Indicates whether the cursor is making adjustments within a word. + private boolean mInWord = false; + // Offset to track difference between touch and word boundary. + protected int mTouchWordOffset; public SelectionStartHandleView(Drawable drawableLtr, Drawable drawableRtl) { super(drawableLtr, drawableRtl); @@ -3701,11 +3818,7 @@ public class Editor { @Override protected int getHotspotX(Drawable drawable, boolean isRtlRun) { - if (isRtlRun) { - return drawable.getIntrinsicWidth() / 4; - } else { - return (drawable.getIntrinsicWidth() * 3) / 4; - } + return isRtlRun ? 0 : drawable.getIntrinsicWidth(); } @Override @@ -3727,21 +3840,81 @@ public class Editor { @Override public void updatePosition(float x, float y) { - int offset = mTextView.getOffsetForPosition(x, y); - - // Handles can not cross and selection is at least one character - final int selectionEnd = mTextView.getSelectionEnd(); - if (offset >= selectionEnd) offset = Math.max(0, selectionEnd - 1); + final int trueOffset = mTextView.getOffsetForPosition(x, y); + final int currLine = mTextView.getLineAtCoordinate(y); + int offset = trueOffset; + boolean positionCursor = false; + + int end = getWordEnd(offset, true); + int start = getWordStart(offset); + + if (offset < mPrevOffset) { + // User is increasing the selection. + if (!mInWord || currLine < mLine) { + // We're not in a word, or we're on a different line so we'll expand by + // word. First ensure the user has at least entered the next word. + int offsetToWord = Math.min((end - start) / 2, 2); + if (offset <= end - offsetToWord || currLine < mLine) { + offset = start; + } else { + offset = mPrevOffset; + } + } + mPrevOffset = offset; + mTouchWordOffset = trueOffset - offset; + mInWord = !isStartBoundary(offset); + positionCursor = true; + } else if (offset - mTouchWordOffset > mPrevOffset) { + // User is shrinking the selection. + if (currLine > mLine) { + // We're on a different line, so we'll snap to word boundaries. + offset = end; + } + offset -= mTouchWordOffset; + mPrevOffset = offset; + mInWord = !isEndBoundary(offset); + positionCursor = true; + } - positionAtCursorOffset(offset, false); + // Handles can not cross and selection is at least one character. + if (positionCursor) { + final int selectionEnd = mTextView.getSelectionEnd(); + if (offset >= selectionEnd) { + // We can't cross the handles so let's just constrain the Y value. + int alteredOffset = mTextView.getOffsetAtCoordinate(mLine, x); + if (alteredOffset >= selectionEnd) { + // Can't pass the other drag handle. + offset = Math.max(0, selectionEnd - 1); + } else { + offset = alteredOffset; + } + } + positionAtCursorOffset(offset, false); + } } public ActionPopupWindow getActionPopupWindow() { return mActionPopupWindow; } + + @Override + public boolean onTouchEvent(MotionEvent event) { + boolean superResult = super.onTouchEvent(event); + if (event.getActionMasked() == MotionEvent.ACTION_UP) { + // Reset the touch word offset when the user has lifted their finger. + mTouchWordOffset = 0; + } + return superResult; + } } private class SelectionEndHandleView extends HandleView { + // The previous offset this handle was at. + private int mPrevOffset; + // Indicates whether the cursor is making adjustments within a word. + private boolean mInWord = false; + // Offset to track difference between touch and word boundary. + protected int mTouchWordOffset; public SelectionEndHandleView(Drawable drawableLtr, Drawable drawableRtl) { super(drawableLtr, drawableRtl); @@ -3749,11 +3922,7 @@ public class Editor { @Override protected int getHotspotX(Drawable drawable, boolean isRtlRun) { - if (isRtlRun) { - return (drawable.getIntrinsicWidth() * 3) / 4; - } else { - return drawable.getIntrinsicWidth() / 4; - } + return isRtlRun ? drawable.getIntrinsicWidth() : 0; } @Override @@ -3775,20 +3944,72 @@ public class Editor { @Override public void updatePosition(float x, float y) { - int offset = mTextView.getOffsetForPosition(x, y); - - // Handles can not cross and selection is at least one character - final int selectionStart = mTextView.getSelectionStart(); - if (offset <= selectionStart) { - offset = Math.min(selectionStart + 1, mTextView.getText().length()); + final int trueOffset = mTextView.getOffsetForPosition(x, y); + final int currLine = mTextView.getLineAtCoordinate(y); + int offset = trueOffset; + boolean positionCursor = false; + + int end = getWordEnd(offset, true); + int start = getWordStart(offset); + + if (offset > mPrevOffset) { + // User is increasing the selection. + if (!mInWord || currLine > mLine) { + // We're not in a word, or we're on a different line so we'll expand by + // word. First ensure the user has at least entered the next word. + int midPoint = Math.min((end - start) / 2, 2); + if (offset >= start + midPoint || currLine > mLine) { + offset = end; + } else { + offset = mPrevOffset; + } + } + mPrevOffset = offset; + mTouchWordOffset = offset - trueOffset; + mInWord = !isEndBoundary(offset); + positionCursor = true; + } else if (offset + mTouchWordOffset < mPrevOffset) { + // User is shrinking the selection. + if (currLine > mLine) { + // We're on a different line, so we'll snap to word boundaries. + offset = getWordStart(offset); + } + offset += mTouchWordOffset; + mPrevOffset = offset; + positionCursor = true; + mInWord = !isStartBoundary(offset); } - positionAtCursorOffset(offset, false); + if (positionCursor) { + final int selectionStart = mTextView.getSelectionStart(); + if (offset <= selectionStart) { + // We can't cross the handles so let's just constrain the Y value. + int alteredOffset = mTextView.getOffsetAtCoordinate(mLine, x); + int length = mTextView.getText().length(); + if (alteredOffset <= selectionStart) { + // Can't pass the other drag handle. + offset = Math.min(selectionStart + 1, length); + } else { + offset = Math.min(alteredOffset, length); + } + } + positionAtCursorOffset(offset, false); + } } public void setActionPopupWindow(ActionPopupWindow actionPopupWindow) { mActionPopupWindow = actionPopupWindow; } + + @Override + public boolean onTouchEvent(MotionEvent event) { + boolean superResult = super.onTouchEvent(event); + if (event.getActionMasked() == MotionEvent.ACTION_UP) { + // Reset the touch word offset when the user has lifted their finger. + mTouchWordOffset = 0; + } + return superResult; + } } /** @@ -3871,6 +4092,11 @@ public class Editor { private float mDownPositionX, mDownPositionY; private boolean mGestureStayedInTapRegion; + // Where the user first starts the drag motion. + private int mStartOffset = -1; + // Indicates whether the user is selecting text and using the drag accelerator. + private boolean mDragAcceleratorActive; + SelectionModifierCursorController() { resetTouchOffsets(); } @@ -3920,6 +4146,22 @@ public class Editor { if (mEndHandle != null) mEndHandle.hide(); } + public void enterDrag() { + // Just need to init the handles / hide insertion cursor. + show(); + mDragAcceleratorActive = true; + // Start location of selection. + mStartOffset = mTextView.getOffsetForPosition(mLastDownPositionX, + mLastDownPositionY); + // Don't show the handles until user has lifted finger. + hide(); + + // This stops scrolling parents from intercepting the touch event, allowing + // the user to continue dragging across the screen to select text; TextView will + // scroll as necessary. + mTextView.getParent().requestDisallowInterceptTouchEvent(true); + } + public void onTouchEvent(MotionEvent event) { // This is done even when the View does not have focus, so that long presses can start // selection and tap can move cursor from this tap position. @@ -3928,7 +4170,7 @@ public class Editor { final float x = event.getX(); final float y = event.getY(); - // Remember finger down position, to be able to start selection from there + // Remember finger down position, to be able to start selection from there. mMinTouchOffset = mMaxTouchOffset = mTextView.getOffsetForPosition(x, y); // Double tap detection @@ -3967,23 +4209,112 @@ public class Editor { break; case MotionEvent.ACTION_MOVE: + final ViewConfiguration viewConfiguration = ViewConfiguration.get( + mTextView.getContext()); + if (mGestureStayedInTapRegion) { final float deltaX = event.getX() - mDownPositionX; final float deltaY = event.getY() - mDownPositionY; final float distanceSquared = deltaX * deltaX + deltaY * deltaY; - final ViewConfiguration viewConfiguration = ViewConfiguration.get( - mTextView.getContext()); int doubleTapTouchSlop = viewConfiguration.getScaledDoubleTapTouchSlop(); if (distanceSquared > doubleTapTouchSlop * doubleTapTouchSlop) { mGestureStayedInTapRegion = false; } } + + if (mStartHandle != null && mStartHandle.isShowing()) { + // Don't do the drag if the handles are showing already. + break; + } + + if (mStartOffset != -1) { + final int rawOffset = mTextView.getOffsetForPosition(event.getX(), + event.getY()); + int offset = rawOffset; + + // We don't start "dragging" until the user is past the initial word that + // gets selected on long press. + int firstWordStart = getWordStart(mStartOffset); + int firstWordEnd = getWordEnd(mStartOffset, false); + if (offset > firstWordEnd || offset < firstWordStart) { + + // Basically the goal in the below code is to have the highlight be + // offset so that your finger isn't covering the end point. + int fingerOffset = viewConfiguration.getScaledTouchSlop(); + float mx = event.getX(); + float my = event.getY(); + if (mx > fingerOffset) mx -= fingerOffset; + if (my > fingerOffset) my -= fingerOffset; + offset = mTextView.getOffsetForPosition(mx, my); + + // Perform the check for closeness at edge of view, if we're very close + // don't adjust the offset to be in front of the finger - otherwise the + // user can't select words at the edge. + if (mTextView.getWidth() - fingerOffset > mx) { + // We're going by word, so we need to make sure that the offset + // that we get is within this, so we'll get the previous boundary. + final WordIterator wordIterator = getWordIteratorWithText(); + + final int precedingOffset = wordIterator.preceding(offset); + if (mStartOffset < offset) { + // Expanding with bottom handle, in this case the selection end + // is before the finger. + offset = Math.max(precedingOffset - 1, 0); + } else { + // Expand with the start handle, in this case the selection + // start is before the finger. + if (precedingOffset == WordIterator.DONE) { + offset = 0; + } else { + offset = wordIterator.preceding(precedingOffset); + } + } + } + if (offset == WordIterator.DONE) + offset = rawOffset; + + // Need to adjust start offset based on direction of movement. + int newStart = mStartOffset < offset ? getWordStart(mStartOffset) + : getWordEnd(mStartOffset, true); + Selection.setSelection((Spannable) mTextView.getText(), newStart, + offset); + } + } break; case MotionEvent.ACTION_UP: mPreviousTapUpTime = SystemClock.uptimeMillis(); + if (mDragAcceleratorActive) { + // No longer dragging to select text, let the parent intercept events. + mTextView.getParent().requestDisallowInterceptTouchEvent(false); + + show(); + int startOffset = mTextView.getSelectionStart(); + int endOffset = mTextView.getSelectionEnd(); + + // Since we don't let drag handles pass once they're visible, we need to + // make sure the start / end locations are correct because the user *can* + // switch directions during the initial drag. + if (endOffset < startOffset) { + int tmp = endOffset; + endOffset = startOffset; + startOffset = tmp; + + // Also update the selection with the right offsets in this case. + Selection.setSelection((Spannable) mTextView.getText(), + startOffset, endOffset); + } + + // Need to do this to display the handles. + mStartHandle.showAtLocation(startOffset); + mEndHandle.showAtLocation(endOffset); + + // No longer the first dragging motion, reset. + mDragAcceleratorActive = false; + mStartOffset = -1; + } break; } } @@ -4010,6 +4341,8 @@ public class Editor { public void resetTouchOffsets() { mMinTouchOffset = mMaxTouchOffset = -1; + mStartOffset = -1; + mDragAcceleratorActive = false; } /** @@ -4019,6 +4352,13 @@ public class Editor { return mStartHandle != null && mStartHandle.isDragging(); } + /** + * @return true if the user is selecting text using the drag accelerator. + */ + public boolean isDragAcceleratorActive() { + return mDragAcceleratorActive; + } + public void onTouchModeChanged(boolean isInTouchMode) { if (!isInTouchMode) { hide(); @@ -4217,10 +4557,30 @@ public class Editor { public static class UndoInputFilter implements InputFilter { private final Editor mEditor; + // Whether the current filter pass is directly caused by an end-user text edit. + private boolean mIsUserEdit; + + // Whether this is the first pass through the filter for a given end-user text edit. + private boolean mFirstFilterPass; + public UndoInputFilter(Editor editor) { mEditor = editor; } + /** + * Signals that a user-triggered edit is starting. + */ + public void beginBatchEdit() { + if (DEBUG_UNDO) Log.d(TAG, "beginBatchEdit"); + mIsUserEdit = true; + mFirstFilterPass = true; + } + + public void endBatchEdit() { + if (DEBUG_UNDO) Log.d(TAG, "endBatchEdit"); + mIsUserEdit = false; + } + @Override public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { @@ -4229,36 +4589,24 @@ public class Editor { "dest=" + dest + " (" + dstart + "-" + dend + ")"); } - if (!mEditor.mAllowUndo) { - if (DEBUG_UNDO) Log.d(TAG, "filter: undo is disabled"); - return null; - } - - final UndoManager um = mEditor.mUndoManager; - if (um.isInUndo()) { - if (DEBUG_UNDO) Log.d(TAG, "filter: skipping, currently performing undo/redo"); + // Check to see if this edit should be tracked for undo. + if (!canUndoEdit(source, start, end, dest, dstart, dend)) { return null; } - // Text filters run before input operations are applied. However, some input operations - // are invalid and will throw exceptions when applied. This is common in tests. Don't - // attempt to undo invalid operations. - if (!isValidRange(source, start, end) || !isValidRange(dest, dstart, dend)) { - if (DEBUG_UNDO) Log.d(TAG, "filter: invalid op"); - return null; - } - - // Earlier filters can rewrite input to be a no-op, for example due to a length limit - // on an input field. Skip no-op changes. - if (start == end && dstart == dend) { - if (DEBUG_UNDO) Log.d(TAG, "filter: skipping no-op"); - return null; - } + // An application may install a TextWatcher to provide additional modifications after + // the initial input filters run (e.g. a credit card formatter that adds spaces to a + // string). This results in multiple filter() calls for what the user considers to be + // a single operation. Always undo the whole set of changes in one step. + final boolean forceMerge = !mFirstFilterPass; + mFirstFilterPass = false; // Build a new operation with all the information from this edit. - EditOperation edit = new EditOperation(mEditor, source, start, end, dest, dstart, dend); + EditOperation edit = new EditOperation(mEditor, forceMerge, + source, start, end, dest, dstart, dend); // Fetch the last edit operation and attempt to merge in the new edit. + final UndoManager um = mEditor.mUndoManager; um.beginUpdate("Edit text"); EditOperation lastEdit = um.getLastOperation( EditOperation.class, mEditor.mUndoOwner, UndoManager.MERGE_MODE_UNIQUE); @@ -4266,6 +4614,12 @@ public class Editor { // Add this as the first edit. if (DEBUG_UNDO) Log.d(TAG, "filter: adding first op " + edit); um.addOperation(edit, UndoManager.MERGE_MODE_NONE); + } else if (!mIsUserEdit) { + // An application directly modified the Editable outside of a text edit. Treat this + // as a new change and don't attempt to merge. + if (DEBUG_UNDO) Log.d(TAG, "non-user edit, new op " + edit); + um.commitState(mEditor.mUndoOwner); + um.addOperation(edit, UndoManager.MERGE_MODE_NONE); } else if (lastEdit.mergeWith(edit)) { // Merge succeeded, nothing else to do. if (DEBUG_UNDO) Log.d(TAG, "filter: merge succeeded, created " + lastEdit); @@ -4278,6 +4632,36 @@ public class Editor { um.endUpdate(); return null; // Text not changed. } + + private boolean canUndoEdit(CharSequence source, int start, int end, + Spanned dest, int dstart, int dend) { + if (!mEditor.mAllowUndo) { + if (DEBUG_UNDO) Log.d(TAG, "filter: undo is disabled"); + return false; + } + + if (mEditor.mUndoManager.isInUndo()) { + if (DEBUG_UNDO) Log.d(TAG, "filter: skipping, currently performing undo/redo"); + return false; + } + + // Text filters run before input operations are applied. However, some input operations + // are invalid and will throw exceptions when applied. This is common in tests. Don't + // attempt to undo invalid operations. + if (!isValidRange(source, start, end) || !isValidRange(dest, dstart, dend)) { + if (DEBUG_UNDO) Log.d(TAG, "filter: invalid op"); + return false; + } + + // Earlier filters can rewrite input to be a no-op, for example due to a length limit + // on an input field. Skip no-op changes. + if (start == end && dstart == dend) { + if (DEBUG_UNDO) Log.d(TAG, "filter: skipping no-op"); + return false; + } + + return true; + } } /** @@ -4289,6 +4673,7 @@ public class Editor { private static final int TYPE_REPLACE = 2; private int mType; + private boolean mForceMerge; private String mOldText; private int mOldTextStart; private String mNewText; @@ -4300,10 +4685,12 @@ public class Editor { /** * Constructs an edit operation from a text input operation that replaces the range * (dstart, dend) of dest with (start, end) of source. See {@link InputFilter#filter}. + * If forceMerge is true then always forcibly merge this operation with any previous one. */ - public EditOperation(Editor editor, CharSequence source, int start, int end, - Spanned dest, int dstart, int dend) { + public EditOperation(Editor editor, boolean forceMerge, + CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { super(editor.mUndoOwner); + mForceMerge = forceMerge; mOldText = dest.subSequence(dstart, dend).toString(); mNewText = source.subSequence(start, end).toString(); @@ -4331,6 +4718,7 @@ public class Editor { public EditOperation(Parcel src, ClassLoader loader) { super(src, loader); mType = src.readInt(); + mForceMerge = src.readInt() != 0; mOldText = src.readString(); mOldTextStart = src.readInt(); mNewText = src.readString(); @@ -4342,6 +4730,7 @@ public class Editor { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); + dest.writeInt(mForceMerge ? 1 : 0); dest.writeString(mOldText); dest.writeInt(mOldTextStart); dest.writeString(mNewText); @@ -4350,6 +4739,14 @@ public class Editor { dest.writeInt(mNewCursorPos); } + private int getNewTextEnd() { + return mNewTextStart + mNewText.length(); + } + + private int getOldTextEnd() { + return mOldTextStart + mOldText.length(); + } + @Override public void commit() { } @@ -4358,14 +4755,20 @@ public class Editor { public void undo() { if (DEBUG_UNDO) Log.d(TAG, "undo"); // Remove the new text and insert the old. - modifyText(mNewTextStart, getNewTextEnd(), mOldText, mOldTextStart, mOldCursorPos); + Editor editor = getOwnerData(); + Editable text = (Editable) editor.mTextView.getText(); + modifyText(text, mNewTextStart, getNewTextEnd(), mOldText, mOldTextStart, + mOldCursorPos); } @Override public void redo() { if (DEBUG_UNDO) Log.d(TAG, "redo"); // Remove the old text and insert the new. - modifyText(mOldTextStart, getOldTextEnd(), mNewText, mNewTextStart, mNewCursorPos); + Editor editor = getOwnerData(); + Editable text = (Editable) editor.mTextView.getText(); + modifyText(text, mOldTextStart, getOldTextEnd(), mNewText, mNewTextStart, + mNewCursorPos); } /** @@ -4375,6 +4778,14 @@ public class Editor { * object unchanged. */ private boolean mergeWith(EditOperation edit) { + if (DEBUG_UNDO) { + Log.d(TAG, "mergeWith old " + this); + Log.d(TAG, "mergeWith new " + edit); + } + if (edit.mForceMerge) { + forceMergeWith(edit); + return true; + } switch (mType) { case TYPE_INSERT: return mergeInsertWith(edit); @@ -4388,7 +4799,6 @@ public class Editor { } private boolean mergeInsertWith(EditOperation edit) { - if (DEBUG_UNDO) Log.d(TAG, "mergeInsertWith " + edit); // Only merge continuous insertions. if (edit.mType != TYPE_INSERT) { return false; @@ -4404,7 +4814,6 @@ public class Editor { // TODO: Support forward delete. private boolean mergeDeleteWith(EditOperation edit) { - if (DEBUG_UNDO) Log.d(TAG, "mergeDeleteWith " + edit); // Only merge continuous deletes. if (edit.mType != TYPE_DELETE) { return false; @@ -4420,11 +4829,8 @@ public class Editor { } private boolean mergeReplaceWith(EditOperation edit) { - if (DEBUG_UNDO) Log.d(TAG, "mergeReplaceWith " + edit); - // Replacements can merge only with adjacent inserts and adjacent replacements. - if (edit.mType == TYPE_DELETE || - getNewTextEnd() != edit.mOldTextStart || - edit.mOldTextStart != edit.mNewTextStart) { + // Replacements can merge only with adjacent inserts. + if (edit.mType != TYPE_INSERT || getNewTextEnd() != edit.mNewTextStart) { return false; } mOldText += edit.mOldText; @@ -4433,18 +4839,42 @@ public class Editor { return true; } - private int getNewTextEnd() { - return mNewTextStart + mNewText.length(); - } + /** + * Forcibly creates a single merged edit operation by simulating the entire text + * contents being replaced. + */ + private void forceMergeWith(EditOperation edit) { + if (DEBUG_UNDO) Log.d(TAG, "forceMerge"); + Editor editor = getOwnerData(); - private int getOldTextEnd() { - return mOldTextStart + mOldText.length(); + // Copy the text of the current field. + // NOTE: Using StringBuilder instead of SpannableStringBuilder would be somewhat faster, + // but would require two parallel implementations of modifyText() because Editable and + // StringBuilder do not share an interface for replace/delete/insert. + Editable editable = (Editable) editor.mTextView.getText(); + Editable originalText = new SpannableStringBuilder(editable.toString()); + + // Roll back the last operation. + modifyText(originalText, mNewTextStart, getNewTextEnd(), mOldText, mOldTextStart, + mOldCursorPos); + + // Clone the text again and apply the new operation. + Editable finalText = new SpannableStringBuilder(editable.toString()); + modifyText(finalText, edit.mOldTextStart, edit.getOldTextEnd(), edit.mNewText, + edit.mNewTextStart, edit.mNewCursorPos); + + // Convert this operation into a non-mergeable replacement of the entire string. + mType = TYPE_REPLACE; + mNewText = finalText.toString(); + mNewTextStart = 0; + mOldText = originalText.toString(); + mOldTextStart = 0; + mNewCursorPos = edit.mNewCursorPos; + // mOldCursorPos is unchanged. } - private void modifyText(int deleteFrom, int deleteTo, CharSequence newText, - int newTextInsertAt, int newCursorPos) { - Editor editor = getOwnerData(); - Editable text = (Editable) editor.mTextView.getText(); + private static void modifyText(Editable text, int deleteFrom, int deleteTo, + CharSequence newText, int newTextInsertAt, int newCursorPos) { // Apply the edit if it is still valid. if (isValidRange(text, deleteFrom, deleteTo) && newTextInsertAt <= text.length() - (deleteTo - deleteFrom)) { @@ -4462,10 +4892,22 @@ public class Editor { } } + private String getTypeString() { + switch (mType) { + case TYPE_INSERT: + return "insert"; + case TYPE_DELETE: + return "delete"; + case TYPE_REPLACE: + return "replace"; + default: + return ""; + } + } + @Override public String toString() { - return "EditOperation: [" + - "mType=" + mType + ", " + + return "[mType=" + getTypeString() + ", " + "mOldText=" + mOldText + ", " + "mOldTextStart=" + mOldTextStart + ", " + "mNewText=" + mNewText + ", " + diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index e8dc11f..f6d198b 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -101,15 +101,16 @@ public class FrameLayout extends ViewGroup { super(context); } - public FrameLayout(Context context, AttributeSet attrs) { + public FrameLayout(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } - public FrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { + public FrameLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } - public FrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + public FrameLayout( + Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); final TypedArray a = context.obtainStyledAttributes( diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index e8fe191..6cc4bda 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -141,7 +141,9 @@ import static java.lang.Math.min; * view was alone in a column, that column would itself collapse to zero width if and only if * no gravity was defined on the view. If gravity was defined, then the gone-marked * view has no effect on the layout and the container should be laid out as if the view - * had never been added to it. + * had never been added to it. GONE views are taken to have zero weight during excess space + * distribution. + * <p> * These statements apply equally to rows as well as columns, and to groups of rows or columns. * * <p> @@ -1010,12 +1012,10 @@ public class GridLayout extends ViewGroup { LayoutParams lp = getLayoutParams(c); if (firstPass) { measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height); - mHorizontalAxis.recordOriginalMeasurement(i); - mVerticalAxis.recordOriginalMeasurement(i); } else { boolean horizontal = (mOrientation == HORIZONTAL); Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; - if (spec.alignment == FILL) { + if (spec.getAbsoluteAlignment(horizontal) == FILL) { Interval span = spec.span; Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis; int[] locations = axis.getLocations(); @@ -1091,11 +1091,6 @@ public class GridLayout extends ViewGroup { invalidateValues(); } - final Alignment getAlignment(Alignment alignment, boolean horizontal) { - return (alignment != UNDEFINED_ALIGNMENT) ? alignment : - (horizontal ? START : BASELINE); - } - // Layout container /** @@ -1150,8 +1145,8 @@ public class GridLayout extends ViewGroup { int pWidth = getMeasurement(c, true); int pHeight = getMeasurement(c, false); - Alignment hAlign = getAlignment(columnSpec.alignment, true); - Alignment vAlign = getAlignment(rowSpec.alignment, false); + Alignment hAlign = columnSpec.getAbsoluteAlignment(true); + Alignment vAlign = rowSpec.getAbsoluteAlignment(false); Bounds boundsX = mHorizontalAxis.getGroupBounds().getValue(i); Bounds boundsY = mVerticalAxis.getGroupBounds().getValue(i); @@ -1234,7 +1229,6 @@ public class GridLayout extends ViewGroup { public boolean hasWeights; public boolean hasWeightsValid = false; - public int[] originalMeasurements; public int[] deltas; boolean orderPreserved = DEFAULT_ORDER_PRESERVED; @@ -1297,7 +1291,7 @@ public class GridLayout extends ViewGroup { // we must include views that are GONE here, see introductory javadoc LayoutParams lp = getLayoutParams(c); Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; - Bounds bounds = getAlignment(spec.alignment, horizontal).getBounds(); + Bounds bounds = spec.getAbsoluteAlignment(horizontal).getBounds(); assoc.put(spec, bounds); } return assoc.pack(); @@ -1313,9 +1307,8 @@ public class GridLayout extends ViewGroup { // we must include views that are GONE here, see introductory javadoc LayoutParams lp = getLayoutParams(c); Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; - int size = (spec.weight == 0) ? - getMeasurementIncludingMargin(c, horizontal) : - getOriginalMeasurements()[i] + getDeltas()[i]; + int size = getMeasurementIncludingMargin(c, horizontal) + + ((spec.weight == 0) ? 0 : getDeltas()[i]); groupBounds.getValue(i).include(GridLayout.this, c, spec, this, size); } } @@ -1703,7 +1696,11 @@ public class GridLayout extends ViewGroup { private boolean computeHasWeights() { for (int i = 0, N = getChildCount(); i < N; i++) { - LayoutParams lp = getLayoutParams(getChildAt(i)); + final View child = getChildAt(i); + if (child.getVisibility() == View.GONE) { + continue; + } + LayoutParams lp = getLayoutParams(child); Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; if (spec.weight != 0) { return true; @@ -1720,19 +1717,6 @@ public class GridLayout extends ViewGroup { return hasWeights; } - public int[] getOriginalMeasurements() { - if (originalMeasurements == null) { - originalMeasurements = new int[getChildCount()]; - } - return originalMeasurements; - } - - private void recordOriginalMeasurement(int i) { - if (hasWeights()) { - getOriginalMeasurements()[i] = getMeasurementIncludingMargin(getChildAt(i), horizontal); - } - } - public int[] getDeltas() { if (deltas == null) { deltas = new int[getChildCount()]; @@ -1743,7 +1727,10 @@ public class GridLayout extends ViewGroup { private void shareOutDelta(int totalDelta, float totalWeight) { Arrays.fill(deltas, 0); for (int i = 0, N = getChildCount(); i < N; i++) { - View c = getChildAt(i); + final View c = getChildAt(i); + if (c.getVisibility() == View.GONE) { + continue; + } LayoutParams lp = getLayoutParams(c); Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; float weight = spec.weight; @@ -1796,6 +1783,9 @@ public class GridLayout extends ViewGroup { float totalWeight = 0f; for (int i = 0, N = getChildCount(); i < N; i++) { View c = getChildAt(i); + if (c.getVisibility() == View.GONE) { + continue; + } LayoutParams lp = getLayoutParams(c); Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; totalWeight += spec.weight; @@ -1891,7 +1881,6 @@ public class GridLayout extends ViewGroup { locations = null; - originalMeasurements = null; deltas = null; hasWeightsValid = false; @@ -2011,7 +2000,6 @@ public class GridLayout extends ViewGroup { R.styleable.ViewGroup_MarginLayout_layout_marginRight; private static final int BOTTOM_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginBottom; - private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column; private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan; private static final int COLUMN_WEIGHT = R.styleable.GridLayout_Layout_layout_columnWeight; @@ -2405,7 +2393,7 @@ public class GridLayout extends ViewGroup { protected final void include(GridLayout gl, View c, Spec spec, Axis axis, int size) { this.flexibility &= spec.getFlexibility(); boolean horizontal = axis.horizontal; - Alignment alignment = gl.getAlignment(spec.alignment, horizontal); + Alignment alignment = spec.getAbsoluteAlignment(axis.horizontal); // todo test this works correctly when the returned value is UNDEFINED int before = alignment.getAlignmentValue(c, size, gl.getLayoutMode()); include(before, size - before); @@ -2556,6 +2544,16 @@ public class GridLayout extends ViewGroup { this(startDefined, new Interval(start, start + size), alignment, weight); } + private Alignment getAbsoluteAlignment(boolean horizontal) { + if (alignment != UNDEFINED_ALIGNMENT) { + return alignment; + } + if (weight == 0f) { + return horizontal ? START : BASELINE; + } + return FILL; + } + final Spec copyWriteSpan(Interval span) { return new Spec(startDefined, span, alignment, weight); } diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index f599035..da15302 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -19,6 +19,7 @@ package android.widget; import com.android.internal.R; import android.annotation.IntDef; +import android.annotation.Nullable; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; @@ -186,11 +187,11 @@ public class LinearLayout extends ViewGroup { this(context, null); } - public LinearLayout(Context context, AttributeSet attrs) { + public LinearLayout(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } - public LinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { + public LinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index dd7fa18..a10be11 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.ColorInt; import android.app.ActivityOptions; import android.app.ActivityThread; import android.app.Application; @@ -2263,7 +2264,7 @@ public class RemoteViews implements Parcelable, Filter { * @param color Sets the text color for all the states (normal, selected, * focused) to be this color. */ - public void setTextColor(int viewId, int color) { + public void setTextColor(int viewId, @ColorInt int color) { setInt(viewId, "setTextColor", color); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 632f5c7..faf1c40 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -17,6 +17,7 @@ package android.widget; import android.R; +import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.NonNull; import android.annotation.Nullable; @@ -637,16 +638,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener this(context, null); } - public TextView(Context context, AttributeSet attrs) { + public TextView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, com.android.internal.R.attr.textViewStyle); } - public TextView(Context context, AttributeSet attrs, int defStyleAttr) { + public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } @SuppressWarnings("deprecation") - public TextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + public TextView( + Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mText = ""; @@ -2996,7 +2998,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_textColor */ @android.view.RemotableViewMethod - public void setTextColor(int color) { + public void setTextColor(@ColorInt int color) { mTextColor = ColorStateList.valueOf(color); updateTextColors(); } @@ -3037,6 +3039,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @return Returns the current text color. */ + @ColorInt public final int getCurrentTextColor() { return mCurTextColor; } @@ -3047,7 +3050,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_textColorHighlight */ @android.view.RemotableViewMethod - public void setHighlightColor(int color) { + public void setHighlightColor(@ColorInt int color) { if (mHighlightColor != color) { mHighlightColor = color; invalidate(); @@ -3061,6 +3064,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @attr ref android.R.styleable#TextView_textColorHighlight */ + @ColorInt public int getHighlightColor() { return mHighlightColor; } @@ -3155,6 +3159,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @attr ref android.R.styleable#TextView_shadowColor */ + @ColorInt public int getShadowColor() { return mShadowColor; } @@ -3230,7 +3235,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_textColorHint */ @android.view.RemotableViewMethod - public final void setHintTextColor(int color) { + public final void setHintTextColor(@ColorInt int color) { mHintTextColor = ColorStateList.valueOf(color); updateTextColors(); } @@ -3269,6 +3274,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @return Returns the current hint text color. */ + @ColorInt public final int getCurrentHintTextColor() { return mHintTextColor != null ? mCurHintTextColor : mCurTextColor; } @@ -3282,7 +3288,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_textColorLink */ @android.view.RemotableViewMethod - public final void setLinkTextColor(int color) { + public final void setLinkTextColor(@ColorInt int color) { mLinkTextColor = ColorStateList.valueOf(color); updateTextColors(); } @@ -4119,6 +4125,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (type == BufferType.EDITABLE || getKeyListener() != null || needEditableForNotification) { createEditorIfNeeded(); + mEditor.forgetUndoRedo(); Editable t = mEditableFactory.newEditable(text); text = t; setFilters(t, mFilters); @@ -8098,7 +8105,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public boolean onTouchEvent(MotionEvent event) { final int action = event.getActionMasked(); - if (mEditor != null) mEditor.onTouchEvent(event); + if (mEditor != null) { + mEditor.onTouchEvent(event); + + if (mEditor.mSelectionModifierCursorController != null && + mEditor.mSelectionModifierCursorController.isDragAcceleratorActive()) { + return true; + } + } final boolean superResult = super.onTouchEvent(event); @@ -9104,7 +9118,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return getLayout().getLineForVertical((int) y); } - private int getOffsetAtCoordinate(int line, float x) { + int getOffsetAtCoordinate(int line, float x) { x = convertToLocalHorizontalCoordinate(x); return getLayout().getOffsetForHorizontal(line, x); } @@ -9726,4 +9740,4 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener TextView.this.spanChange(buf, what, s, -1, e, -1); } } -}
\ No newline at end of file +} diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java index 9bc2aab..d2430bc 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActionBar; @@ -685,7 +686,7 @@ public class Toolbar extends ViewGroup { * * @param color The new text color in 0xAARRGGBB format */ - public void setTitleTextColor(int color) { + public void setTitleTextColor(@ColorInt int color) { mTitleTextColor = color; if (mTitleTextView != null) { mTitleTextView.setTextColor(color); @@ -697,7 +698,7 @@ public class Toolbar extends ViewGroup { * * @param color The new text color in 0xAARRGGBB format */ - public void setSubtitleTextColor(int color) { + public void setSubtitleTextColor(@ColorInt int color) { mSubtitleTextColor = color; if (mSubtitleTextView != null) { mSubtitleTextView.setTextColor(color); |