diff options
Diffstat (limited to 'core/java')
77 files changed, 3023 insertions, 1217 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 36f9f4b..b4b3c99 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -17,6 +17,7 @@ package android.app; import android.annotation.NonNull; +import android.os.PersistableBundle; import android.transition.Scene; import android.transition.TransitionManager; import android.util.ArrayMap; @@ -922,6 +923,30 @@ public class Activity extends ContextThemeWrapper } /** + * Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with + * the attribute {@link android.R.attr#persistable} set true. + * + * @param savedInstanceState if the activity is being re-initialized after + * previously being shut down then this Bundle contains the data it most + * recently supplied in {@link #onSaveInstanceState}. + * <b><i>Note: Otherwise it is null.</i></b> + * @param persistentState if the activity is being re-initialized after + * previously being shut down or powered off then this Bundle contains the data it most + * recently supplied to outPersistentState in {@link #onSaveInstanceState}. + * <b><i>Note: Otherwise it is null.</i></b> + * + * @see #onCreate(android.os.Bundle) + * @see #onStart + * @see #onSaveInstanceState + * @see #onRestoreInstanceState + * @see #onPostCreate + */ + protected void onCreate(@Nullable Bundle savedInstanceState, + @Nullable PersistableBundle persistentState) { + onCreate(savedInstanceState); + } + + /** * The hook for {@link ActivityThread} to restore the state of this activity. * * Calls {@link #onSaveInstanceState(android.os.Bundle)} and @@ -935,6 +960,23 @@ public class Activity extends ContextThemeWrapper } /** + * The hook for {@link ActivityThread} to restore the state of this activity. + * + * Calls {@link #onSaveInstanceState(android.os.Bundle)} and + * {@link #restoreManagedDialogs(android.os.Bundle)}. + * + * @param savedInstanceState contains the saved state + * @param persistentState contains the persistable saved state + */ + final void performRestoreInstanceState(Bundle savedInstanceState, + PersistableBundle persistentState) { + onRestoreInstanceState(savedInstanceState, persistentState); + if (savedInstanceState != null) { + restoreManagedDialogs(savedInstanceState); + } + } + + /** * This method is called after {@link #onStart} when the activity is * being re-initialized from a previously saved state, given here in * <var>savedInstanceState</var>. Most implementations will simply use {@link #onCreate} @@ -962,7 +1004,34 @@ public class Activity extends ContextThemeWrapper } } } - + + /** + * This is the same as {@link #onRestoreInstanceState(Bundle)} but is called for activities + * created with the attribute {@link android.R.attr#persistable}. The {@link + * android.os.PersistableBundle} passed came from the restored PersistableBundle first + * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}. + * + * <p>This method is called between {@link #onStart} and + * {@link #onPostCreate}. + * + * <p>If this method is called {@link #onRestoreInstanceState(Bundle)} will not be called. + * + * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}. + * @param persistentState the data most recently supplied in {@link #onSaveInstanceState}. + * + * @see #onRestoreInstanceState(Bundle) + * @see #onCreate + * @see #onPostCreate + * @see #onResume + * @see #onSaveInstanceState + */ + protected void onRestoreInstanceState(Bundle savedInstanceState, + PersistableBundle persistentState) { + if (savedInstanceState != null) { + onRestoreInstanceState(savedInstanceState); + } + } + /** * Restore the state of any saved managed dialogs. * @@ -1039,6 +1108,21 @@ public class Activity extends ContextThemeWrapper } /** + * This is the same as {@link #onPostCreate(Bundle)} but is called for activities + * created with the attribute {@link android.R.attr#persistable}. + * + * @param savedInstanceState The data most recently supplied in {@link #onSaveInstanceState} + * @param persistentState The data caming from the PersistableBundle first + * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}. + * + * @see #onCreate + */ + protected void onPostCreate(@Nullable Bundle savedInstanceState, + @Nullable PersistableBundle persistentState) { + onPostCreate(savedInstanceState); + } + + /** * Called after {@link #onCreate} — or after {@link #onRestart} when * the activity had been stopped, but is now again being displayed to the * user. It will be followed by {@link #onResume}. @@ -1194,6 +1278,22 @@ public class Activity extends ContextThemeWrapper } /** + * The hook for {@link ActivityThread} to save the state of this activity. + * + * Calls {@link #onSaveInstanceState(android.os.Bundle)} + * and {@link #saveManagedDialogs(android.os.Bundle)}. + * + * @param outState The bundle to save the state to. + * @param outPersistentState The bundle to save persistent state to. + */ + final void performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { + onSaveInstanceState(outState, outPersistentState); + saveManagedDialogs(outState); + if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState + + ", " + outPersistentState); + } + + /** * Called to retrieve per-instance state from an activity before being killed * so that the state can be restored in {@link #onCreate} or * {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method @@ -1248,6 +1348,25 @@ public class Activity extends ContextThemeWrapper } /** + * This is the same as {@link #onSaveInstanceState} but is called for activities + * created with the attribute {@link android.R.attr#persistable}. The {@link + * android.os.PersistableBundle} passed in will be saved and presented in + * {@link #onCreate(Bundle, PersistableBundle)} the first time that this activity + * is restarted following the next device reboot. + * + * @param outState Bundle in which to place your saved state. + * @param outPersistentState State which will be saved across reboots. + * + * @see #onSaveInstanceState(Bundle) + * @see #onCreate + * @see #onRestoreInstanceState(Bundle, PersistableBundle) + * @see #onPause + */ + protected void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { + onSaveInstanceState(outState); + } + + /** * Save the state of any managed dialogs. * * @param outState place to store the saved state. @@ -3499,13 +3618,15 @@ public class Activity extends ContextThemeWrapper } // Get the primary color and update the RecentsActivityValues for this activity - TypedArray a = getTheme().obtainStyledAttributes(com.android.internal.R.styleable.Theme); - int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0); - a.recycle(); - if (colorPrimary != 0) { - ActivityManager.RecentsActivityValues v = new ActivityManager.RecentsActivityValues(); - v.colorPrimary = colorPrimary; - setRecentsActivityValues(v); + if (theme != null) { + TypedArray a = theme.obtainStyledAttributes(com.android.internal.R.styleable.Theme); + int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0); + a.recycle(); + if (colorPrimary != 0) { + ActivityManager.RecentsActivityValues v = new ActivityManager.RecentsActivityValues(); + v.colorPrimary = colorPrimary; + setRecentsActivityValues(v); + } } } @@ -4807,6 +4928,7 @@ public class Activity extends ContextThemeWrapper public void setRecentsActivityValues(ActivityManager.RecentsActivityValues values) { ActivityManager.RecentsActivityValues activityValues = new ActivityManager.RecentsActivityValues(values); + // Scale the icon down to something reasonable if (values.icon != null) { final int size = ActivityManager.getLauncherLargeIconSizeInner(this); activityValues.icon = Bitmap.createScaledBitmap(values.icon, size, size, true); @@ -5488,13 +5610,22 @@ public class Activity extends ContextThemeWrapper return mParent != null ? mParent.getActivityToken() : mToken; } - final void performCreate(Bundle icicle) { - onCreate(icicle); + final void performCreateCommon() { mVisibleFromClient = !mWindow.getWindowStyle().getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay, false); mFragments.dispatchActivityCreated(); } + final void performCreate(Bundle icicle) { + onCreate(icicle); + performCreateCommon(); + } + + final void performCreate(Bundle icicle, PersistableBundle persistentState) { + onCreate(icicle, persistentState); + performCreateCommon(); + } + final void performStart() { mFragments.noteStateNotSaved(); mCalled = false; diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 5d809d8..044727d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -20,7 +20,6 @@ import android.os.BatteryStats; import android.os.IBinder; import com.android.internal.app.IUsageStats; import com.android.internal.app.ProcessStats; -import com.android.internal.os.PkgUsageStats; import com.android.internal.os.TransferPipe; import com.android.internal.util.FastPrintWriter; @@ -2130,14 +2129,15 @@ public class ActivityManager { return new HashMap<String, Integer>(); } - PkgUsageStats[] allPkgUsageStats = usageStatsService.getAllPkgUsageStats(); + UsageStats.PackageStats[] allPkgUsageStats = usageStatsService.getAllPkgUsageStats( + ActivityThread.currentPackageName()); if (allPkgUsageStats == null) { return new HashMap<String, Integer>(); } Map<String, Integer> launchCounts = new HashMap<String, Integer>(); - for (PkgUsageStats pkgUsageStats : allPkgUsageStats) { - launchCounts.put(pkgUsageStats.packageName, pkgUsageStats.launchCount); + for (UsageStats.PackageStats pkgUsageStats : allPkgUsageStats) { + launchCounts.put(pkgUsageStats.getPackageName(), pkgUsageStats.getLaunchCount()); } return launchCounts; @@ -2251,17 +2251,17 @@ public class ActivityManager { * * @hide */ - public PkgUsageStats[] getAllPackageUsageStats() { + public UsageStats.PackageStats[] getAllPackageUsageStats() { try { IUsageStats usageStatsService = IUsageStats.Stub.asInterface( ServiceManager.getService("usagestats")); if (usageStatsService != null) { - return usageStatsService.getAllPkgUsageStats(); + return usageStatsService.getAllPkgUsageStats(ActivityThread.currentPackageName()); } } catch (RemoteException e) { Log.w(TAG, "Could not query usage stats", e); } - return new PkgUsageStats[0]; + return new UsageStats.PackageStats[0]; } /** diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 57da21e..ec2868a 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -40,6 +40,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; +import android.os.PersistableBundle; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; @@ -454,7 +455,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case ACTIVITY_PAUSED_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); - activityPaused(token); + PersistableBundle persistentState = data.readPersistableBundle(); + activityPaused(token, persistentState); reply.writeNoException(); return true; } @@ -463,10 +465,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); Bundle map = data.readBundle(); - Bitmap thumbnail = data.readInt() != 0 - ? Bitmap.CREATOR.createFromParcel(data) : null; + PersistableBundle persistentState = data.readPersistableBundle(); CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data); - activityStopped(token, map, thumbnail, description); + activityStopped(token, map, persistentState, description); reply.writeNoException(); return true; } @@ -2583,31 +2584,27 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } - public void activityPaused(IBinder token) throws RemoteException + public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(token); + data.writePersistableBundle(persistentState); mRemote.transact(ACTIVITY_PAUSED_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); reply.recycle(); } public void activityStopped(IBinder token, Bundle state, - Bitmap thumbnail, CharSequence description) throws RemoteException + PersistableBundle persistentState, CharSequence description) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(token); data.writeBundle(state); - if (thumbnail != null) { - data.writeInt(1); - thumbnail.writeToParcel(data, 0); - } else { - data.writeInt(0); - } + data.writePersistableBundle(persistentState); TextUtils.writeToParcel(description, data, 0); mRemote.transact(ACTIVITY_STOPPED_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY); reply.readException(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b606088..161cb76 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -56,11 +56,11 @@ import android.os.DropBoxManager; import android.os.Environment; import android.os.Handler; import android.os.IBinder; -import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.MessageQueue; import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -69,8 +69,6 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; -import android.transition.Scene; -import android.transition.TransitionManager; import android.provider.Settings; import android.util.AndroidRuntimeException; import android.util.ArrayMap; @@ -268,6 +266,7 @@ public final class ActivityThread { Intent intent; IVoiceInteractor voiceInteractor; Bundle state; + PersistableBundle persistentState; Activity activity; Window window; Activity parent; @@ -317,6 +316,10 @@ public final class ActivityThread { return false; } + public boolean isPersistable() { + return (activityInfo.flags & ActivityInfo.FLAG_PERSISTABLE) != 0; + } + public String toString() { ComponentName componentName = intent != null ? intent.getComponent() : null; return "ActivityRecord{" @@ -605,8 +608,8 @@ public final class ActivityThread { // activity itself back to the activity manager. (matters more with ipc) public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, - IVoiceInteractor voiceInteractor, - int procState, Bundle state, List<ResultInfo> pendingResults, + IVoiceInteractor voiceInteractor, int procState, Bundle state, + PersistableBundle persistentState, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler, Bundle resumeArgs) { @@ -622,6 +625,7 @@ public final class ActivityThread { r.activityInfo = info; r.compatInfo = compatInfo; r.state = state; + r.persistentState = persistentState; r.pendingResults = pendingResults; r.pendingIntents = pendingNewIntents; @@ -2205,7 +2209,11 @@ public final class ActivityThread { } activity.mCalled = false; - mInstrumentation.callActivityOnCreate(activity, r.state); + if (r.isPersistable()) { + mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); + } else { + mInstrumentation.callActivityOnCreate(activity, r.state); + } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + @@ -2218,13 +2226,23 @@ public final class ActivityThread { r.stopped = false; } if (!r.activity.mFinished) { - if (r.state != null) { + if (r.isPersistable()) { + if (r.state != null || r.persistentState != null) { + mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, + r.persistentState); + } + } else if (r.state != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); } } if (!r.activity.mFinished) { activity.mCalled = false; - mInstrumentation.callActivityOnPostCreate(activity, r.state); + if (r.isPersistable()) { + mInstrumentation.callActivityOnPostCreate(activity, r.state, + r.persistentState); + } else { + mInstrumentation.callActivityOnPostCreate(activity, r.state); + } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + @@ -2842,6 +2860,7 @@ public final class ActivityThread { r.paused = false; r.stopped = false; r.state = null; + r.persistentState = null; } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( @@ -3069,7 +3088,7 @@ public final class ActivityThread { // Tell the activity manager we have paused. try { - ActivityManagerNative.getDefault().activityPaused(token); + ActivityManagerNative.getDefault().activityPaused(token, r.persistentState); } catch (RemoteException ex) { } } @@ -3099,17 +3118,13 @@ public final class ActivityThread { + r.intent.getComponent().toShortString()); Slog.e(TAG, e.getMessage(), e); } - Bundle state = null; if (finished) { r.activity.mFinished = true; } try { // Next have the activity save its current state and managed dialogs... if (!r.activity.mFinished && saveState) { - state = new Bundle(); - state.setAllowFds(false); - mInstrumentation.callActivityOnSaveInstanceState(r.activity, state); - r.state = state; + callCallActivityOnSaveInstanceState(r); } // Now we are idle. r.activity.mCalled = false; @@ -3145,7 +3160,7 @@ public final class ActivityThread { listeners.get(i).onPaused(r.activity); } - return state; + return !r.activity.mFinished && saveState ? r.state : null; } final void performStopActivity(IBinder token, boolean saveState) { @@ -3156,7 +3171,7 @@ public final class ActivityThread { private static class StopInfo implements Runnable { ActivityClientRecord activity; Bundle state; - Bitmap thumbnail; + PersistableBundle persistentState; CharSequence description; @Override public void run() { @@ -3164,7 +3179,7 @@ public final class ActivityThread { try { if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + activity); ActivityManagerNative.getDefault().activityStopped( - activity.token, state, thumbnail, description); + activity.token, state, persistentState, description); } catch (RemoteException ex) { } } @@ -3203,7 +3218,6 @@ public final class ActivityThread { private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown, boolean saveState) { if (localLOGV) Slog.v(TAG, "Performing stop of " + r); - Bundle state = null; if (r != null) { if (!keepShown && r.stopped) { if (r.activity.mFinished) { @@ -3223,7 +3237,6 @@ public final class ActivityThread { // First create a thumbnail for the activity... // For now, don't create the thumbnail here; we are // doing that by doing a screen snapshot. - info.thumbnail = null; //createThumbnailBitmap(r); info.description = r.activity.onCreateDescription(); } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { @@ -3238,12 +3251,7 @@ public final class ActivityThread { // Next have the activity save its current state and managed dialogs... if (!r.activity.mFinished && saveState) { if (r.state == null) { - state = new Bundle(); - state.setAllowFds(false); - mInstrumentation.callActivityOnSaveInstanceState(r.activity, state); - r.state = state; - } else { - state = r.state; + callCallActivityOnSaveInstanceState(r); } } @@ -3319,6 +3327,7 @@ public final class ActivityThread { // manager to proceed and allow us to go fully into the background. info.activity = r; info.state = r.state; + info.persistentState = r.persistentState; mH.post(info); } @@ -3775,9 +3784,7 @@ public final class ActivityThread { performPauseActivity(r.token, false, r.isPreHoneycomb()); } if (r.state == null && !r.stopped && !r.isPreHoneycomb()) { - r.state = new Bundle(); - r.state.setAllowFds(false); - mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state); + callCallActivityOnSaveInstanceState(r); } handleDestroyActivity(r.token, false, configChanges, true); @@ -3807,6 +3814,18 @@ public final class ActivityThread { handleLaunchActivity(r, currentIntent); } + private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) { + r.state = new Bundle(); + r.state.setAllowFds(false); + if (r.isPersistable()) { + r.persistentState = new PersistableBundle(); + mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state, + r.persistentState); + } else { + mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state); + } + } + ArrayList<ComponentCallbacks2> collectComponentCallbacks( boolean allActivities, Configuration newConfig) { ArrayList<ComponentCallbacks2> callbacks diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index b616c1e..d813dab 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -16,6 +16,7 @@ package android.app; +import android.Manifest; import android.os.Binder; import android.os.IBinder; import android.util.ArrayMap; @@ -184,8 +185,10 @@ public class AppOpsManager { public static final int OP_MONITOR_LOCATION = 41; /** @hide Continually monitoring location data with a relatively high power request. */ public static final int OP_MONITOR_HIGH_POWER_LOCATION = 42; + /** @hide Retrieve current usage stats via {@link UsageStatsManager}. */ + public static final int OP_GET_USAGE_STATS = 43; /** @hide */ - public static final int _NUM_OP = 43; + public static final int _NUM_OP = 44; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = @@ -252,6 +255,7 @@ public class AppOpsManager { OP_WAKE_LOCK, OP_COARSE_LOCATION, OP_COARSE_LOCATION, + OP_GET_USAGE_STATS, }; /** @@ -302,6 +306,7 @@ public class AppOpsManager { null, OPSTR_MONITOR_LOCATION, OPSTR_MONITOR_HIGH_POWER_LOCATION, + null, }; /** @@ -352,6 +357,7 @@ public class AppOpsManager { "WAKE_LOCK", "MONITOR_LOCATION", "MONITOR_HIGH_POWER_LOCATION", + "GET_USAGE_STATS" }; /** @@ -402,6 +408,7 @@ public class AppOpsManager { android.Manifest.permission.WAKE_LOCK, null, // no permission for generic location monitoring null, // no permission for high power location monitoring + android.Manifest.permission.PACKAGE_USAGE_STATS, }; /** @@ -451,6 +458,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS }; /** @@ -504,6 +512,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/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 7f2fb59..e7902a9 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -29,6 +29,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.Debug; import android.os.Parcelable; +import android.os.PersistableBundle; import android.os.RemoteException; import android.os.IBinder; import android.os.Parcel; @@ -141,6 +142,7 @@ public abstract class ApplicationThreadNative extends Binder data.readStrongBinder()); int procState = data.readInt(); Bundle state = data.readBundle(); + PersistableBundle persistentState = data.readPersistableBundle(); List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR); List<Intent> pi = data.createTypedArrayList(Intent.CREATOR); boolean notResumed = data.readInt() != 0; @@ -151,9 +153,9 @@ public abstract class ApplicationThreadNative extends Binder boolean autoStopProfiler = data.readInt() != 0; Bundle resumeArgs = data.readBundle(); scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, - voiceInteractor, procState, state, - ri, pi, notResumed, isForward, profileName, profileFd, autoStopProfiler, - resumeArgs); + voiceInteractor, procState, state, persistentState, + ri, pi, notResumed, isForward, profileName, profileFd, + autoStopProfiler, resumeArgs); return true; } @@ -731,8 +733,8 @@ class ApplicationThreadProxy implements IApplicationThread { public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, - IVoiceInteractor voiceInteractor, - int procState, Bundle state, List<ResultInfo> pendingResults, + IVoiceInteractor voiceInteractor, int procState, Bundle state, + PersistableBundle persistentState, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler, Bundle resumeArgs) @@ -748,6 +750,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null); data.writeInt(procState); data.writeBundle(state); + data.writePersistableBundle(persistentState); data.writeTypedList(pendingResults); data.writeTypedList(pendingNewIntents); data.writeInt(notResumed ? 1 : 0); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 5ed5030..801182d 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -678,6 +678,11 @@ class ContextImpl extends Context { return new NetworkScoreManager(ctx); } }); + + registerService(USAGE_STATS_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new UsageStatsManager(ctx.getOuterContext()); + }}); } static ContextImpl getImpl(Context context) { diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 2e9cdf3b7..074b427 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -45,6 +45,7 @@ import android.os.IInterface; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; +import android.os.PersistableBundle; import android.os.RemoteException; import android.os.StrictMode; import android.service.voice.IVoiceInteractionSession; @@ -104,9 +105,9 @@ public interface IActivityManager extends IInterface { public void activityResumed(IBinder token) throws RemoteException; public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) throws RemoteException; - public void activityPaused(IBinder token) throws RemoteException; + public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException; public void activityStopped(IBinder token, Bundle state, - Bitmap thumbnail, CharSequence description) throws RemoteException; + PersistableBundle persistentState, CharSequence description) throws RemoteException; public void activitySlept(IBinder token) throws RemoteException; public void activityDestroyed(IBinder token) throws RemoteException; public String getCallingPackage(IBinder token) throws RemoteException; diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index fefba8a..a832034 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -28,6 +28,7 @@ import android.content.res.Configuration; import android.os.Bundle; import android.os.Debug; import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; import android.os.RemoteException; import android.os.IBinder; import android.os.IInterface; @@ -58,8 +59,8 @@ public interface IApplicationThread extends IInterface { void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, IVoiceInteractor voiceInteractor, int procState, Bundle state, - List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, - boolean isForward, + PersistableBundle persistentState, List<ResultInfo> pendingResults, + List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler, Bundle resumeArgs) throws RemoteException; diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl index 347de97..8ab9ac3 100644 --- a/core/java/android/app/IUiAutomationConnection.aidl +++ b/core/java/android/app/IUiAutomationConnection.aidl @@ -43,4 +43,5 @@ interface IUiAutomationConnection { WindowContentFrameStats getWindowContentFrameStats(int windowId); void clearWindowAnimationFrameStats(); WindowAnimationFrameStats getWindowAnimationFrameStats(); + void executeShellCommand(String command, in ParcelFileDescriptor fd); } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index e58ccb8..bb3e002 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -30,6 +30,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.MessageQueue; import android.os.PerformanceCollector; +import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -1061,15 +1062,7 @@ public class Instrumentation { return (Activity)cl.loadClass(className).newInstance(); } - /** - * Perform calling of an activity's {@link Activity#onCreate} - * method. The default implementation simply calls through to that method. - * - * @param activity The activity being created. - * @param icicle The previously frozen state (or null) to pass through to - * onCreate(). - */ - public void callActivityOnCreate(Activity activity, Bundle icicle) { + private void prePerformCreate(Activity activity) { if (mWaitingActivities != null) { synchronized (mSync) { final int N = mWaitingActivities.size(); @@ -1083,9 +1076,9 @@ public class Instrumentation { } } } - - activity.performCreate(icicle); - + } + + private void postPerformCreate(Activity activity) { if (mActivityMonitors != null) { synchronized (mSync) { final int N = mActivityMonitors.size(); @@ -1096,6 +1089,33 @@ public class Instrumentation { } } } + + /** + * Perform calling of an activity's {@link Activity#onCreate} + * method. The default implementation simply calls through to that method. + * + * @param activity The activity being created. + * @param icicle The previously frozen state (or null) to pass through to onCreate(). + */ + public void callActivityOnCreate(Activity activity, Bundle icicle) { + prePerformCreate(activity); + activity.performCreate(icicle); + postPerformCreate(activity); + } + + /** + * Perform calling of an activity's {@link Activity#onCreate} + * method. The default implementation simply calls through to that method. + * @param activity The activity being created. + * @param icicle The previously frozen state (or null) to pass through to + * @param persistentState The previously persisted state (or null) + */ + public void callActivityOnCreate(Activity activity, Bundle icicle, + PersistableBundle persistentState) { + prePerformCreate(activity); + activity.performCreate(icicle, persistentState); + postPerformCreate(activity); + } public void callActivityOnDestroy(Activity activity) { // TODO: the following block causes intermittent hangs when using startActivity @@ -1130,7 +1150,7 @@ public class Instrumentation { /** * Perform calling of an activity's {@link Activity#onRestoreInstanceState} * method. The default implementation simply calls through to that method. - * + * * @param activity The activity being restored. * @param savedInstanceState The previously saved state being restored. */ @@ -1139,9 +1159,22 @@ public class Instrumentation { } /** + * Perform calling of an activity's {@link Activity#onRestoreInstanceState} + * method. The default implementation simply calls through to that method. + * + * @param activity The activity being restored. + * @param savedInstanceState The previously saved state being restored. + * @param persistentState The previously persisted state (or null) + */ + public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState, + PersistableBundle persistentState) { + activity.performRestoreInstanceState(savedInstanceState, persistentState); + } + + /** * Perform calling of an activity's {@link Activity#onPostCreate} method. * The default implementation simply calls through to that method. - * + * * @param activity The activity being created. * @param icicle The previously frozen state (or null) to pass through to * onPostCreate(). @@ -1151,6 +1184,19 @@ public class Instrumentation { } /** + * Perform calling of an activity's {@link Activity#onPostCreate} method. + * The default implementation simply calls through to that method. + * + * @param activity The activity being created. + * @param icicle The previously frozen state (or null) to pass through to + * onPostCreate(). + */ + public void callActivityOnPostCreate(Activity activity, Bundle icicle, + PersistableBundle persistentState) { + activity.onPostCreate(icicle, persistentState); + } + + /** * Perform calling of an activity's {@link Activity#onNewIntent} * method. The default implementation simply calls through to that method. * @@ -1215,7 +1261,7 @@ public class Instrumentation { /** * Perform calling of an activity's {@link Activity#onSaveInstanceState} * method. The default implementation simply calls through to that method. - * + * * @param activity The activity being saved. * @param outState The bundle to pass to the call. */ @@ -1224,6 +1270,18 @@ public class Instrumentation { } /** + * Perform calling of an activity's {@link Activity#onSaveInstanceState} + * method. The default implementation simply calls through to that method. + * @param activity The activity being saved. + * @param outState The bundle to pass to the call. + * @param outPersistentState The persistent bundle to pass to the call. + */ + public void callActivityOnSaveInstanceState(Activity activity, Bundle outState, + PersistableBundle outPersistentState) { + activity.performSaveInstanceState(outState, outPersistentState); + } + + /** * Perform calling of an activity's {@link Activity#onPause} method. The * default implementation simply calls through to that method. * @@ -1428,7 +1486,7 @@ public class Instrumentation { } /** - * Like {@link #execStartActivity}, + * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)}, * but accepts an array of activities to be started. Note that active * {@link ActivityMonitor} objects only match against the first activity in * the array. @@ -1442,7 +1500,7 @@ public class Instrumentation { } /** - * Like {@link #execStartActivity}, + * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)}, * but accepts an array of activities to be started. Note that active * {@link ActivityMonitor} objects only match against the first activity in * the array. @@ -1545,7 +1603,8 @@ public class Instrumentation { } /** - * Like {@link #execStartActivity}, but for starting as a particular user. + * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)}, + * but for starting as a particular user. * * @param who The Context from which the activity is being started. * @param contextThread The main thread of the Context from which the activity diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index aab6ed8..db91742a 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -44,8 +44,8 @@ public class KeyguardManager { * you to disable / reenable the keyguard. */ public class KeyguardLock { - private IBinder mToken = new Binder(); - private String mTag; + private final IBinder mToken = new Binder(); + private final String mTag; KeyguardLock(String tag) { mTag = tag; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index bba6caf..76a6a8e 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -659,8 +659,8 @@ public class Notification implements Parcelable /** * @hide - * Extra added by NotificationManagerService to indicate whether a NotificationScorer - * modified the Notifications's score. + * Extra added by NotificationManagerService to indicate whether + * the Notifications's score has been modified. */ public static final String EXTRA_SCORE_MODIFIED = "android.scoreModified"; diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 9405325..64e3484 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -26,6 +26,7 @@ import android.graphics.Canvas; import android.graphics.Point; import android.hardware.display.DisplayManagerGlobal; import android.os.Looper; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; @@ -40,7 +41,9 @@ import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.IAccessibilityInteractionConnection; +import libcore.io.IoUtils; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeoutException; @@ -840,6 +843,44 @@ public final class UiAutomation { return null; } + /** + * Executes a shell command. This method returs a file descriptor that points + * to the standard output stream. The command execution is similar to running + * "adb shell <command>" from a host connected to the device. + * <p> + * <strong>Note:</strong> It is your responsibility to close the retunred file + * descriptor once you are done reading. + * </p> + * + * @param command The command to execute. + * @return A file descriptor to the standard output stream. + */ + public ParcelFileDescriptor executeShellCommand(String command) { + synchronized (mLock) { + throwIfNotConnectedLocked(); + } + + ParcelFileDescriptor source = null; + ParcelFileDescriptor sink = null; + + try { + ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); + source = pipe[0]; + sink = pipe[1]; + + // Calling out without a lock held. + mUiAutomationConnection.executeShellCommand(command, sink); + } catch (IOException ioe) { + Log.e(LOG_TAG, "Error executing shell command!", ioe); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error executing shell command!", re); + } finally { + IoUtils.closeQuietly(sink); + } + + return source; + } + private static float getDegreesForRotation(int value) { switch (value) { case Surface.ROTATION_90: { diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index fa40286..81bcb39 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -23,6 +23,7 @@ import android.graphics.Bitmap; import android.hardware.input.InputManager; import android.os.Binder; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -33,6 +34,12 @@ import android.view.WindowAnimationFrameStats; import android.view.WindowContentFrameStats; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.IAccessibilityManager; +import libcore.io.IoUtils; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; /** * This is a remote object that is passed from the shell to an instrumentation @@ -50,8 +57,8 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface( ServiceManager.getService(Service.WINDOW_SERVICE)); - private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager.Stub.asInterface( - ServiceManager.getService(Service.ACCESSIBILITY_SERVICE)); + private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager.Stub + .asInterface(ServiceManager.getService(Service.ACCESSIBILITY_SERVICE)); private final Object mLock = new Object(); @@ -220,6 +227,41 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } @Override + public void executeShellCommand(String command, ParcelFileDescriptor sink) + throws RemoteException { + synchronized (mLock) { + throwIfCalledByNotTrustedUidLocked(); + throwIfShutdownLocked(); + throwIfNotConnectedLocked(); + } + + InputStream in = null; + OutputStream out = null; + + try { + java.lang.Process process = Runtime.getRuntime().exec(command); + + in = process.getInputStream(); + out = new FileOutputStream(sink.getFileDescriptor()); + + final byte[] buffer = new byte[8192]; + while (true) { + final int readByteCount = in.read(buffer); + if (readByteCount < 0) { + break; + } + out.write(buffer, 0, readByteCount); + } + } catch (IOException ioe) { + throw new RuntimeException("Error running shell command", ioe); + } finally { + IoUtils.closeQuietly(in); + IoUtils.closeQuietly(out); + IoUtils.closeQuietly(sink); + } + } + + @Override public void shutdown() { synchronized (mLock) { if (isConnectedLocked()) { diff --git a/core/java/android/app/UsageStats.aidl b/core/java/android/app/UsageStats.aidl new file mode 100644 index 0000000..7dee70a --- /dev/null +++ b/core/java/android/app/UsageStats.aidl @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +parcelable UsageStats; +parcelable UsageStats.PackageStats; diff --git a/core/java/android/app/UsageStats.java b/core/java/android/app/UsageStats.java new file mode 100644 index 0000000..0aeba59 --- /dev/null +++ b/core/java/android/app/UsageStats.java @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.content.res.Configuration; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.util.ArrayMap; + +import java.util.Map; + +/** + * Snapshot of current usage stats data. + * @hide + */ +public class UsageStats implements Parcelable { + /** @hide */ + public final ArrayMap<String, PackageStats> mPackages = new ArrayMap<String, PackageStats>(); + /** @hide */ + public final ArrayMap<Configuration, ConfigurationStats> mConfigurations + = new ArrayMap<Configuration, ConfigurationStats>(); + + public static class PackageStats implements Parcelable { + private final String mPackageName; + private int mLaunchCount; + private long mUsageTime; + private long mResumedTime; + + /** @hide */ + public final ArrayMap<String, Long> componentResumeTimes; + + public static final Parcelable.Creator<PackageStats> CREATOR + = new Parcelable.Creator<PackageStats>() { + public PackageStats createFromParcel(Parcel in) { + return new PackageStats(in); + } + + public PackageStats[] newArray(int size) { + return new PackageStats[size]; + } + }; + + public String toString() { + return "PackageStats{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + mPackageName + "}"; + } + + /** @hide */ + public PackageStats(String pkgName) { + mPackageName = pkgName; + componentResumeTimes = new ArrayMap<String, Long>(); + } + + /** @hide */ + public PackageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) { + mPackageName = pkgName; + mLaunchCount = count; + mUsageTime = time; + componentResumeTimes = new ArrayMap<String, Long>(); + componentResumeTimes.putAll(lastResumeTimes); + } + + /** @hide */ + public PackageStats(Parcel source) { + mPackageName = source.readString(); + mLaunchCount = source.readInt(); + mUsageTime = source.readLong(); + final int N = source.readInt(); + componentResumeTimes = new ArrayMap<String, Long>(N); + for (int i = 0; i < N; i++) { + String component = source.readString(); + long lastResumeTime = source.readLong(); + componentResumeTimes.put(component, lastResumeTime); + } + } + + /** @hide */ + public PackageStats(PackageStats pStats) { + mPackageName = pStats.mPackageName; + mLaunchCount = pStats.mLaunchCount; + mUsageTime = pStats.mUsageTime; + componentResumeTimes = new ArrayMap<String, Long>(pStats.componentResumeTimes); + } + + /** @hide */ + public void resume(boolean launched) { + if (launched) { + mLaunchCount++; + } + mResumedTime = SystemClock.elapsedRealtime(); + } + + /** @hide */ + public void pause() { + if (mResumedTime > 0) { + mUsageTime += SystemClock.elapsedRealtime() - mResumedTime; + } + mResumedTime = 0; + } + + public final String getPackageName() { + return mPackageName; + } + + public final long getUsageTime(long elapsedRealtime) { + return mUsageTime + (mResumedTime > 0 ? (elapsedRealtime- mResumedTime) : 0); + } + + public final int getLaunchCount() { + return mLaunchCount; + } + + /** @hide */ + public boolean clearUsageTimes() { + mLaunchCount = 0; + mUsageTime = 0; + return mResumedTime <= 0 && componentResumeTimes.isEmpty(); + } + + public final int describeContents() { + return 0; + } + + public final void writeToParcel(Parcel dest, int parcelableFlags) { + writeToParcel(dest, parcelableFlags, 0); + } + + final void writeToParcel(Parcel dest, int parcelableFlags, long elapsedRealtime) { + dest.writeString(mPackageName); + dest.writeInt(mLaunchCount); + dest.writeLong(elapsedRealtime > 0 ? getUsageTime(elapsedRealtime) : mUsageTime); + dest.writeInt(componentResumeTimes.size()); + for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) { + dest.writeString(ent.getKey()); + dest.writeLong(ent.getValue()); + } + } + + /** @hide */ + public void writeExtendedToParcel(Parcel dest, int parcelableFlags) { + } + } + + public static class ConfigurationStats implements Parcelable { + private final Configuration mConfiguration; + private long mLastUsedTime; + private int mUsageCount; + private long mUsageTime; + private long mStartedTime; + + public static final Parcelable.Creator<ConfigurationStats> CREATOR + = new Parcelable.Creator<ConfigurationStats>() { + public ConfigurationStats createFromParcel(Parcel in) { + return new ConfigurationStats(in); + } + + public ConfigurationStats[] newArray(int size) { + return new ConfigurationStats[size]; + } + }; + + public String toString() { + return "ConfigurationStats{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + mConfiguration + "}"; + } + + /** @hide */ + public ConfigurationStats(Configuration config) { + mConfiguration = config; + } + + /** @hide */ + public ConfigurationStats(Parcel source) { + mConfiguration = Configuration.CREATOR.createFromParcel(source); + mLastUsedTime = source.readLong(); + mUsageCount = source.readInt(); + mUsageTime = source.readLong(); + } + + /** @hide */ + public ConfigurationStats(ConfigurationStats pStats) { + mConfiguration = pStats.mConfiguration; + mLastUsedTime = pStats.mLastUsedTime; + mUsageCount = pStats.mUsageCount; + mUsageTime = pStats.mUsageTime; + } + + public final Configuration getConfiguration() { + return mConfiguration; + } + + public final long getLastUsedTime() { + return mLastUsedTime; + } + + public final long getUsageTime(long elapsedRealtime) { + return mUsageTime + (mStartedTime > 0 ? (elapsedRealtime- mStartedTime) : 0); + } + + public final int getUsageCount() { + return mUsageCount; + } + + /** @hide */ + public void start() { + mLastUsedTime = System.currentTimeMillis(); + mUsageCount++; + mStartedTime = SystemClock.elapsedRealtime(); + } + + /** @hide */ + public void stop() { + if (mStartedTime > 0) { + mUsageTime += SystemClock.elapsedRealtime() - mStartedTime; + } + mStartedTime = 0; + } + + /** @hide */ + public boolean clearUsageTimes() { + mUsageCount = 0; + mUsageTime = 0; + return mLastUsedTime == 0 && mStartedTime <= 0; + } + + public final int describeContents() { + return 0; + } + + public final void writeToParcel(Parcel dest, int parcelableFlags) { + writeToParcel(dest, parcelableFlags, 0); + } + + final void writeToParcel(Parcel dest, int parcelableFlags, long elapsedRealtime) { + mConfiguration.writeToParcel(dest, parcelableFlags); + dest.writeLong(mLastUsedTime); + dest.writeInt(mUsageCount); + dest.writeLong(elapsedRealtime > 0 ? getUsageTime(elapsedRealtime) : mUsageTime); + } + + /** @hide */ + public void writeExtendedToParcel(Parcel dest, int parcelableFlags) { + } + } + + /** @hide */ + public UsageStats() { + } + + /** @hide */ + public UsageStats(Parcel source, boolean extended) { + int N = source.readInt(); + for (int i=0; i<N; i++) { + PackageStats pkg = extended ? onNewPackageStats(source) : new PackageStats(source); + mPackages.put(pkg.getPackageName(), pkg); + } + N = source.readInt(); + for (int i=0; i<N; i++) { + ConfigurationStats config = extended ? onNewConfigurationStats(source) + : new ConfigurationStats(source); + mConfigurations.put(config.getConfiguration(), config); + } + } + + public int getPackageStatsCount() { + return mPackages.size(); + } + + public PackageStats getPackageStatsAt(int index) { + return mPackages.valueAt(index); + } + + public PackageStats getPackageStats(String pkgName) { + return mPackages.get(pkgName); + } + + /** @hide */ + public PackageStats getOrCreatePackageStats(String pkgName) { + PackageStats ps = mPackages.get(pkgName); + if (ps == null) { + ps = onNewPackageStats(pkgName); + mPackages.put(pkgName, ps); + } + return ps; + } + + public int getConfigurationStatsCount() { + return mConfigurations.size(); + } + + public ConfigurationStats getConfigurationStatsAt(int index) { + return mConfigurations.valueAt(index); + } + + public ConfigurationStats getConfigurationStats(Configuration config) { + return mConfigurations.get(config); + } + + /** @hide */ + public ConfigurationStats getOrCreateConfigurationStats(Configuration config) { + ConfigurationStats cs = mConfigurations.get(config); + if (cs == null) { + cs = onNewConfigurationStats(config); + mConfigurations.put(config, cs); + } + return cs; + } + + /** @hide */ + public void clearUsageTimes() { + for (int i=mPackages.size()-1; i>=0; i--) { + if (mPackages.valueAt(i).clearUsageTimes()) { + mPackages.removeAt(i); + } + } + for (int i=mConfigurations.size()-1; i>=0; i--) { + if (mConfigurations.valueAt(i).clearUsageTimes()) { + mConfigurations.removeAt(i); + } + } + } + + /** @hide */ + public PackageStats onNewPackageStats(String pkgName) { + return new PackageStats(pkgName); + } + + /** @hide */ + public PackageStats onNewPackageStats(Parcel source) { + return new PackageStats(source); + } + + /** @hide */ + public ConfigurationStats onNewConfigurationStats(Configuration config) { + return new ConfigurationStats(config); + } + + /** @hide */ + public ConfigurationStats onNewConfigurationStats(Parcel source) { + return new ConfigurationStats(source); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int parcelableFlags) { + writeToParcelInner(dest, parcelableFlags, false); + } + + /** @hide */ + public void writeExtendedToParcel(Parcel dest, int parcelableFlags) { + writeToParcelInner(dest, parcelableFlags, true); + } + + private void writeToParcelInner(Parcel dest, int parcelableFlags, boolean extended) { + final long elapsedRealtime = SystemClock.elapsedRealtime(); + + int N = mPackages.size(); + dest.writeInt(N); + for (int i=0; i<N; i++) { + PackageStats ps = mPackages.valueAt(i); + ps.writeToParcel(dest, parcelableFlags, elapsedRealtime); + if (extended) { + ps.writeExtendedToParcel(dest, parcelableFlags); + } + } + N = mConfigurations.size(); + dest.writeInt(N); + for (int i=0; i<N; i++) { + ConfigurationStats cs = mConfigurations.valueAt(i); + cs.writeToParcel(dest, parcelableFlags, elapsedRealtime); + if (extended) { + cs.writeExtendedToParcel(dest, parcelableFlags); + } + } + } + + public static final Parcelable.Creator<UsageStats> CREATOR + = new Parcelable.Creator<UsageStats>() { + public UsageStats createFromParcel(Parcel in) { + return new UsageStats(in, false); + } + + public UsageStats[] newArray(int size) { + return new UsageStats[size]; + } + }; +} diff --git a/core/java/android/app/UsageStatsManager.java b/core/java/android/app/UsageStatsManager.java new file mode 100644 index 0000000..fbf9c3b --- /dev/null +++ b/core/java/android/app/UsageStatsManager.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.content.Context; +import android.os.ParcelableParcel; +import android.os.RemoteException; +import android.os.ServiceManager; +import com.android.internal.app.IUsageStats; + +/** + * Access to usage stats data. + * @hide + */ +public class UsageStatsManager { + final Context mContext; + final IUsageStats mService; + + /** @hide */ + public UsageStatsManager(Context context) { + mContext = context; + mService = IUsageStats.Stub.asInterface(ServiceManager.getService( + Context.USAGE_STATS_SERVICE)); + } + + public UsageStats getCurrentStats() { + try { + ParcelableParcel in = mService.getCurrentStats(mContext.getOpPackageName()); + if (in != null) { + return new UsageStats(in.getParcel(), false); + } + } catch (RemoteException e) { + // About to die. + } + return new UsageStats(); + } +} diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 61ff60a..58049fd 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -16,8 +16,6 @@ package android.app.admin; -import org.xmlpull.v1.XmlPullParserException; - import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; @@ -39,6 +37,8 @@ import android.util.Log; import com.android.org.conscrypt.TrustedCertificateStore; +import org.xmlpull.v1.XmlPullParserException; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.InetSocketAddress; @@ -359,8 +359,8 @@ public class DevicePolicyManager { } /** - * Retrieve the current minimum password quality for all admins - * or a particular one. + * Retrieve the current minimum password quality for all admins of this user + * and its profiles or a particular one. * @param admin The name of the admin component to check, or null to aggregate * all admins. */ @@ -412,8 +412,8 @@ public class DevicePolicyManager { } /** - * Retrieve the current minimum password length for all admins - * or a particular one. + * Retrieve the current minimum password length for all admins of this + * user and its profiles or a particular one. * @param admin The name of the admin component to check, or null to aggregate * all admins. */ @@ -467,8 +467,9 @@ public class DevicePolicyManager { /** * Retrieve the current number of upper case letters required in the - * password for all admins or a particular one. This is the same value as - * set by {#link {@link #setPasswordMinimumUpperCase(ComponentName, int)} + * password for all admins of this user and its profiles or a particular one. + * This is the same value as set by + * {#link {@link #setPasswordMinimumUpperCase(ComponentName, int)} * and only applies when the password quality is * {@link #PASSWORD_QUALITY_COMPLEX}. * @@ -527,8 +528,9 @@ public class DevicePolicyManager { /** * Retrieve the current number of lower case letters required in the - * password for all admins or a particular one. This is the same value as - * set by {#link {@link #setPasswordMinimumLowerCase(ComponentName, int)} + * password for all admins of this user and its profiles or a particular one. + * This is the same value as set by + * {#link {@link #setPasswordMinimumLowerCase(ComponentName, int)} * and only applies when the password quality is * {@link #PASSWORD_QUALITY_COMPLEX}. * @@ -644,8 +646,9 @@ public class DevicePolicyManager { /** * Retrieve the current number of numerical digits required in the password - * for all admins or a particular one. This is the same value as - * set by {#link {@link #setPasswordMinimumNumeric(ComponentName, int)} + * for all admins of this user and its profiles or a particular one. + * This is the same value as set by + * {#link {@link #setPasswordMinimumNumeric(ComponentName, int)} * and only applies when the password quality is * {@link #PASSWORD_QUALITY_COMPLEX}. * @@ -760,8 +763,9 @@ public class DevicePolicyManager { /** * Retrieve the current number of non-letter characters required in the - * password for all admins or a particular one. This is the same value as - * set by {#link {@link #setPasswordMinimumNonLetter(ComponentName, int)} + * password for all admins of this user and its profiles or a particular one. + * This is the same value as set by + * {#link {@link #setPasswordMinimumNonLetter(ComponentName, int)} * and only applies when the password quality is * {@link #PASSWORD_QUALITY_COMPLEX}. * @@ -868,9 +872,10 @@ public class DevicePolicyManager { /** * Get the current password expiration time for the given admin or an aggregate of - * all admins if admin is null. If the password is expired, this will return the time since - * the password expired as a negative number. If admin is null, then a composite of all - * expiration timeouts is returned - which will be the minimum of all timeouts. + * all admins of this user and its profiles if admin is null. If the password is + * expired, this will return the time since the password expired as a negative number. + * If admin is null, then a composite of all expiration timeouts is returned + * - which will be the minimum of all timeouts. * * @param admin The name of the admin component to check, or null to aggregate all admins. * @return The password expiration time, in ms. @@ -887,8 +892,8 @@ public class DevicePolicyManager { } /** - * Retrieve the current password history length for all admins - * or a particular one. + * Retrieve the current password history length for all admins of this + * user and its profiles or a particular one. * @param admin The name of the admin component to check, or null to aggregate * all admins. * @return The length of the password history @@ -923,14 +928,13 @@ public class DevicePolicyManager { /** * Determine whether the current password the user has set is sufficient * to meet the policy requirements (quality, minimum length) that have been - * requested. + * requested by the admins of this user and its profiles. * * <p>The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call * this method; if it has not, a security exception will be thrown. * - * @return Returns true if the password meets the current requirements, - * else false. + * @return Returns true if the password meets the current requirements, else false. */ public boolean isActivePasswordSufficient() { if (mService != null) { @@ -993,7 +997,7 @@ public class DevicePolicyManager { /** * Retrieve the current maximum number of login attempts that are allowed - * before the device wipes itself, for all admins + * before the device wipes itself, for all admins of this user and its profiles * or a particular one. * @param admin The name of the admin component to check, or null to aggregate * all admins. @@ -1037,6 +1041,8 @@ public class DevicePolicyManager { * {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD} to be able to call * this method; if it has not, a security exception will be thrown. * + * Can not be called from a managed profile. + * * @param password The new password for the user. * @param flags May be 0 or {@link #RESET_PASSWORD_REQUIRE_ENTRY}. * @return Returns true if the password was applied, or false if it is @@ -1077,8 +1083,8 @@ public class DevicePolicyManager { } /** - * Retrieve the current maximum time to unlock for all admins - * or a particular one. + * Retrieve the current maximum time to unlock for all admins of this user + * and its profiles or a particular one. * @param admin The name of the admin component to check, or null to aggregate * all admins. */ @@ -2125,4 +2131,51 @@ public class DevicePolicyManager { return null; } + + /** + * Sets which components may enter lock task mode. + * + * This function can only be called by the device owner or the profile owner. + * @param components The list of components allowed to enter lock task mode + */ + public void setLockTaskComponents(ComponentName[] components) throws SecurityException { + if (mService != null) { + try { + mService.setLockTaskComponents(components); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } + + /** + * This function returns the list of components allowed to start the lock task mode. + * @hide + */ + public ComponentName[] getLockTaskComponents() { + if (mService != null) { + try { + return mService.getLockTaskComponents(); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return null; + } + + /** + * This function lets the caller know whether the given component is allowed to start the + * lock task mode. + * @param component The component to check + */ + public boolean isLockTaskPermitted(ComponentName component) { + if (mService != null) { + try { + return mService.isLockTaskPermitted(component); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return false; + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 0096580..03ced0f 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -129,4 +129,8 @@ interface IDevicePolicyManager { void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled); String[] getAccountTypesWithManagementDisabled(); + + void setLockTaskComponents(in ComponentName[] components); + ComponentName[] getLockTaskComponents(); + boolean isLockTaskPermitted(in ComponentName component); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 042ee28..a059e48 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2683,6 +2683,16 @@ public abstract class Context { public static final String NETWORK_SCORE_SERVICE = "network_score"; /** + * Use with {@link #getSystemService} to retrieve a {@link + * android.app.UsageStatsManager} for interacting with the status bar. + * + * @see #getSystemService + * @see android.app.UsageStatsManager + * @hide + */ + public static final String USAGE_STATS_SERVICE = "usagestats"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 52a169b..eb2c11f 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -936,13 +936,21 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device has at least one camera pointing in - * some direction. + * some direction, or can support an external camera being connected to it. */ @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_CAMERA_ANY = "android.hardware.camera.any"; /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device can support having an external camera connected to it. + * The external camera may not always be connected or available to applications to use. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device's camera supports flash. */ @SdkConstant(SdkConstantType.FEATURE) diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index f161f3a..0ca9161 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -458,7 +458,19 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { * brightness</p> * <p>For example, if EV step is 0.333, '6' will mean an * exposure compensation of +2 EV; -3 will mean an exposure - * compensation of -1</p> + * compensation of -1 EV. Note that this control will only be effective + * if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF. This control will take effect even when + * {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} <code>== true</code>.</p> + * <p>In the event of exposure compensation value being changed, camera device + * may take several frames to reach the newly requested exposure target. + * During that time, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} field will be in the SEARCHING + * state. Once the new exposure target is reached, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} will + * change from SEARCHING to either CONVERGED, LOCKED (if AE lock is enabled), or + * FLASH_REQUIRED (if the scene is too dark for still capture).</p> + * + * @see CaptureRequest#CONTROL_AE_LOCK + * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureResult#CONTROL_AE_STATE */ public static final Key<Integer> CONTROL_AE_EXPOSURE_COMPENSATION = new Key<Integer>("android.control.aeExposureCompensation", int.class); @@ -469,6 +481,8 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { * <p>Note that even when AE is locked, the flash may be * fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_AUTO_FLASH / ON_ALWAYS_FLASH / * ON_AUTO_FLASH_REDEYE.</p> + * <p>When {@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION android.control.aeExposureCompensation} is changed, even if the AE lock + * is ON, the camera device will still adjust its exposure value.</p> * <p>If AE precapture is triggered (see {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) * when AE is already locked, the camera device will not change the exposure time * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}) and sensitivity ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}) @@ -477,6 +491,7 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_ALWAYS_FLASH, the scene may become overexposed.</p> * <p>See {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE lock related state transition details.</p> * + * @see CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER * @see CaptureResult#CONTROL_AE_STATE diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 51ea447..42a3de8 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -275,7 +275,19 @@ public final class CaptureResult extends CameraMetadata { * brightness</p> * <p>For example, if EV step is 0.333, '6' will mean an * exposure compensation of +2 EV; -3 will mean an exposure - * compensation of -1</p> + * compensation of -1 EV. Note that this control will only be effective + * if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF. This control will take effect even when + * {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} <code>== true</code>.</p> + * <p>In the event of exposure compensation value being changed, camera device + * may take several frames to reach the newly requested exposure target. + * During that time, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} field will be in the SEARCHING + * state. Once the new exposure target is reached, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} will + * change from SEARCHING to either CONVERGED, LOCKED (if AE lock is enabled), or + * FLASH_REQUIRED (if the scene is too dark for still capture).</p> + * + * @see CaptureRequest#CONTROL_AE_LOCK + * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureResult#CONTROL_AE_STATE */ public static final Key<Integer> CONTROL_AE_EXPOSURE_COMPENSATION = new Key<Integer>("android.control.aeExposureCompensation", int.class); @@ -286,6 +298,8 @@ public final class CaptureResult extends CameraMetadata { * <p>Note that even when AE is locked, the flash may be * fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_AUTO_FLASH / ON_ALWAYS_FLASH / * ON_AUTO_FLASH_REDEYE.</p> + * <p>When {@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION android.control.aeExposureCompensation} is changed, even if the AE lock + * is ON, the camera device will still adjust its exposure value.</p> * <p>If AE precapture is triggered (see {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) * when AE is already locked, the camera device will not change the exposure time * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}) and sensitivity ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}) @@ -294,6 +308,7 @@ public final class CaptureResult extends CameraMetadata { * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_ALWAYS_FLASH, the scene may become overexposed.</p> * <p>See {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE lock related state transition details.</p> * + * @see CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER * @see CaptureResult#CONTROL_AE_STATE diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index cec90cd..e58c54d 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -156,6 +156,9 @@ public abstract class DisplayManagerInternal { // If true, enables automatic brightness control. public boolean useAutoBrightness; + //If true, scales the brightness to half of desired. + public boolean lowPowerMode; + // If true, prevents the screen from completely turning on if it is currently off. // The display does not enter a "ready" state if this flag is true and screen on is // blocked. The window manager policy blocks screen on while it prepares the keyguard to @@ -203,6 +206,7 @@ public abstract class DisplayManagerInternal { screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment; useAutoBrightness = other.useAutoBrightness; blockScreenOn = other.blockScreenOn; + lowPowerMode = other.lowPowerMode; } @Override @@ -218,7 +222,8 @@ public abstract class DisplayManagerInternal { && screenBrightness == other.screenBrightness && screenAutoBrightnessAdjustment == other.screenAutoBrightnessAdjustment && useAutoBrightness == other.useAutoBrightness - && blockScreenOn == other.blockScreenOn; + && blockScreenOn == other.blockScreenOn + && lowPowerMode == other.lowPowerMode; } @Override @@ -233,7 +238,8 @@ public abstract class DisplayManagerInternal { + ", screenBrightness=" + screenBrightness + ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment + ", useAutoBrightness=" + useAutoBrightness - + ", blockScreenOn=" + blockScreenOn; + + ", blockScreenOn=" + blockScreenOn + + ", lowPowerMode=" + lowPowerMode; } } diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index 8437228..ed223d1 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -36,6 +36,7 @@ import android.view.MotionEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.InputMethodSession; +import android.view.inputmethod.CursorAnchorInfo; class IInputMethodSessionWrapper extends IInputMethodSession.Stub implements HandlerCaller.Callback { @@ -46,6 +47,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub private static final int DO_UPDATE_EXTRACTED_TEXT = 67; private static final int DO_UPDATE_SELECTION = 90; private static final int DO_UPDATE_CURSOR = 95; + private static final int DO_UPDATE_CURSOR_ANCHOR_INFO = 99; private static final int DO_APP_PRIVATE_COMMAND = 100; private static final int DO_TOGGLE_SOFT_INPUT = 105; private static final int DO_FINISH_SESSION = 110; @@ -108,6 +110,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub mInputMethodSession.updateCursor((Rect)msg.obj); return; } + case DO_UPDATE_CURSOR_ANCHOR_INFO: { + mInputMethodSession.updateCursorAnchorInfo((CursorAnchorInfo)msg.obj); + return; + } case DO_APP_PRIVATE_COMMAND: { SomeArgs args = (SomeArgs)msg.obj; mInputMethodSession.appPrivateCommand((String)args.arg1, @@ -181,6 +187,12 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub } @Override + public void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) { + mCaller.executeOrSendMessage( + mCaller.obtainMessageO(DO_UPDATE_CURSOR_ANCHOR_INFO, cursorAnchorInfo)); + } + + @Override public void appPrivateCommand(String action, Bundle data) { mCaller.executeOrSendMessage( mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data)); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index f6438b4..4bccaf1 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -51,6 +51,7 @@ import android.view.WindowManager; import android.view.WindowManager.BadTokenException; import android.view.animation.AnimationUtils; import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; @@ -545,6 +546,17 @@ public class InputMethodService extends AbstractInputMethodService { public void toggleSoftInput(int showFlags, int hideFlags) { InputMethodService.this.onToggleSoftInput(showFlags, hideFlags); } + + /** + * Call {@link InputMethodService#onUpdateCursorAnchorInfo + * InputMethodService.onUpdateCursorAnchorInfo()}. + */ + public void updateCursorAnchorInfo(CursorAnchorInfo info) { + if (!isEnabled()) { + return; + } + InputMethodService.this.onUpdateCursorAnchorInfo(info); + } } /** @@ -1717,6 +1729,17 @@ public class InputMethodService extends AbstractInputMethodService { } /** + * Called when the application has reported a new location of its text insertion point and + * characters in the composition string. This is only called if explicitly requested by the + * input method. The default implementation does nothing. + * @param cursorAnchorInfo The positional information of the text insertion point and the + * composition string. + */ + public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) { + // Intentionally empty + } + + /** * Update the cursor/anthor monitor mode. */ public void setCursorAnchorMonitorMode(int monitorMode) { diff --git a/core/java/android/net/BaseNetworkStateTracker.java b/core/java/android/net/BaseNetworkStateTracker.java index 804f8ee..862d59e 100644 --- a/core/java/android/net/BaseNetworkStateTracker.java +++ b/core/java/android/net/BaseNetworkStateTracker.java @@ -45,6 +45,7 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker { protected NetworkInfo mNetworkInfo; protected LinkProperties mLinkProperties; protected LinkCapabilities mLinkCapabilities; + protected Network mNetwork = new Network(ConnectivityManager.INVALID_NET_ID); private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); @@ -201,4 +202,14 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker { public void stopSampling(SamplingDataTracker.SamplingSnapshot s) { // nothing to do } + + @Override + public void setNetId(int netId) { + mNetwork = new Network(netId); + } + + @Override + public Network getNetwork() { + return mNetwork; + } } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 30d7043..3e00250 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -408,6 +408,11 @@ public class ConnectivityManager { */ public static final int CONNECTIVITY_CHANGE_DELAY_DEFAULT = 3000; + /** + * @hide + */ + public final static int INVALID_NET_ID = 0; + private final IConnectivityManager mService; private final String mPackageName; diff --git a/core/java/com/android/internal/os/PkgUsageStats.aidl b/core/java/android/net/Network.aidl index 8305271..73ba1af 100644 --- a/core/java/com/android/internal/os/PkgUsageStats.aidl +++ b/core/java/android/net/Network.aidl @@ -1,20 +1,20 @@ -/* //device/java/android/android/content/Intent.aidl +/* ** -** Copyright 2007, The Android Open Source Project +** Copyright (C) 2014 The Android Open Source Project ** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at +** 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 +** 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 +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and ** limitations under the License. */ -package com.android.internal.os; +package android.net; -parcelable PkgUsageStats; +parcelable Network; diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java new file mode 100644 index 0000000..f82bc22 --- /dev/null +++ b/core/java/android/net/Network.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.os.Parcelable; +import android.os.Parcel; + + +/** + * Identifies the Network. + * @hide + */ +public class Network implements Parcelable { + + public final int netId; + + public Network(int netId) { + this.netId = netId; + } + + public Network(Network that) { + this.netId = that.netId; + } + + // implement the Parcelable interface + public int describeContents() { + return 0; + } + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(netId); + } + + public static final Creator<Network> CREATOR = + new Creator<Network>() { + public Network createFromParcel(Parcel in) { + int netId = in.readInt(); + + return new Network(netId); + } + + public Network[] newArray(int size) { + return new Network[size]; + } + }; +} diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java index c49b1d1..29b57a5 100644 --- a/core/java/android/net/NetworkStateTracker.java +++ b/core/java/android/net/NetworkStateTracker.java @@ -250,4 +250,14 @@ public interface NetworkStateTracker { */ public void stopSampling(SamplingDataTracker.SamplingSnapshot s); + /* + * Record the current netId + */ + public void setNetId(int netId); + + /* + * ? + */ + public Network getNetwork(); + } diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java index 8f41e85..daf0065 100644 --- a/core/java/android/net/Proxy.java +++ b/core/java/android/net/Proxy.java @@ -278,7 +278,9 @@ public final class Proxy { host = p.getHost(); port = Integer.toString(p.getPort()); exclList = p.getExclusionListAsString(); - pacFileUrl = p.getPacFileUrl().toString(); + if (p.getPacFileUrl() != null) { + pacFileUrl = p.getPacFileUrl().toString(); + } } setHttpProxySystemProperty(host, port, exclList, pacFileUrl); } diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl index ae9796b..521f4fd 100644 --- a/core/java/android/nfc/INfcCardEmulation.aidl +++ b/core/java/android/nfc/INfcCardEmulation.aidl @@ -34,4 +34,6 @@ interface INfcCardEmulation AidGroup getAidGroupForService(int userHandle, in ComponentName service, String category); boolean removeAidGroupForService(int userHandle, in ComponentName service, String category); List<ApduServiceInfo> getServices(int userHandle, in String category); + boolean setPreferredService(in ComponentName service); + boolean unsetPreferredService(); } diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java index 2820f40..b0449224 100644 --- a/core/java/android/nfc/cardemulation/AidGroup.java +++ b/core/java/android/nfc/cardemulation/AidGroup.java @@ -12,10 +12,15 @@ import android.os.Parcelable; import android.util.Log; /** - * The AidGroup class represents a group of ISO/IEC 7816-4 - * Application Identifiers (AIDs) for a specific application - * category, along with a description resource describing - * the group. + * The AidGroup class represents a group of Application Identifiers (AIDs). + * + * <p>An instance of this object can be used with + * {@link CardEmulation#registerAidGroupForService(android.content.ComponentName, AidGroup)} + * to tell the OS which AIDs are handled by your HCE- or SE-based service. + * + * <p>The format of AIDs is defined in the ISO/IEC 7816-4 specification. This class + * requires the AIDs to be input as a hexadecimal string, with an even amount of + * hexadecimal characters, e.g. "F014811481". */ public final class AidGroup implements Parcelable { /** @@ -33,7 +38,7 @@ public final class AidGroup implements Parcelable { * Creates a new AidGroup object. * * @param aids The list of AIDs present in the group - * @param category The category of this group + * @param category The category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT} */ public AidGroup(ArrayList<String> aids, String category) { if (aids == null || aids.size() == 0) { @@ -42,11 +47,12 @@ public final class AidGroup implements Parcelable { if (aids.size() > MAX_NUM_AIDS) { throw new IllegalArgumentException("Too many AIDs in AID group."); } - if (!isValidCategory(category)) { - throw new IllegalArgumentException("Category specified is not valid."); + if (isValidCategory(category)) { + this.category = category; + } else { + this.category = CardEmulation.CATEGORY_OTHER; } this.aids = aids; - this.category = category; this.description = null; } @@ -158,7 +164,7 @@ public final class AidGroup implements Parcelable { } } - boolean isValidCategory(String category) { + static boolean isValidCategory(String category) { return CardEmulation.CATEGORY_PAYMENT.equals(category) || CardEmulation.CATEGORY_OTHER.equals(category); } diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index 94f35ed..f379ee8 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -290,6 +290,20 @@ public final class ApduServiceInfo implements Parcelable { return groups; } + /** + * Returns the category to which this service has attributed the AID that is passed in, + * or null if we don't know this AID. + */ + public String getCategoryForAid(String aid) { + ArrayList<AidGroup> groups = getAidGroups(); + for (AidGroup group : groups) { + if (group.aids.contains(aid)) { + return group.category; + } + } + return null; + } + public boolean hasCategory(String category) { return (mStaticAidGroups.containsKey(category) || mDynamicAidGroups.containsKey(category)); } diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java index 41f039c..e24a22a 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -18,6 +18,7 @@ package android.nfc.cardemulation; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.Activity; import android.app.ActivityThread; import android.content.ComponentName; import android.content.Context; @@ -28,6 +29,7 @@ import android.nfc.NfcAdapter; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; import android.util.Log; import java.util.HashMap; @@ -248,6 +250,33 @@ public final class CardEmulation { } /** + * Returns whether the user has allowed AIDs registered in the + * specified category to be handled by a service that is preferred + * by the foreground application, instead of by a pre-configured default. + * + * Foreground applications can set such preferences using the + * {@link #setPreferredService(Activity, ComponentName)} method. + * + * @param category The category, e.g. {@link #CATEGORY_PAYMENT} + * @return whether AIDs in the category can be handled by a service + * specified by the foreground app. + */ + public boolean categoryAllowsForegroundPreference(String category) { + if (CATEGORY_PAYMENT.equals(category)) { + boolean preferForeground = false; + try { + preferForeground = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0; + } catch (SettingNotFoundException e) { + } + return preferForeground; + } else { + // Allowed for all other categories + return true; + } + } + + /** * Returns the service selection mode for the passed in category. * Valid return values are: * <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default @@ -269,7 +298,6 @@ public final class CardEmulation { return SELECTION_MODE_ALWAYS_ASK; } } else { - // All other categories are in "only ask if conflict" mode return SELECTION_MODE_ASK_IF_CONFLICT; } } @@ -283,7 +311,7 @@ public final class CardEmulation { * that AID group will be replaced with this one. * * <p>Note that you can only register AIDs for a service that - * is running under the same UID as you are. Typically + * is running under the same UID as the caller of this API. Typically * this means you need to call this from the same * package as the service itself, though UIDs can also * be shared between packages using shared UIDs. @@ -352,7 +380,7 @@ public final class CardEmulation { * method. It will *not* remove AID groups that were statically registered in * the manifest. If a dynamically registered AID group is removed using * this method, and a statically registered AID group for the same category - * exists in the manifest, that AID group will become active again. + * exists in the manifest, the static AID group will become active again. * * @param service The component name of the service * @param category The category of the AID group to be removed, e.g. {@link #CATEGORY_PAYMENT} @@ -378,6 +406,96 @@ public final class CardEmulation { } /** + * Allows a foreground application to specify which card emulation service + * should be preferred while a specific Activity is in the foreground. + * + * <p>The specified Activity must currently be in resumed state. A good + * paradigm is to call this method in your {@link Activity#onResume}, and to call + * {@link #unsetPreferredService(Activity)} in your {@link Activity#onPause}. + * + * <p>This method call will fail in two specific scenarios: + * <ul> + * <li> If the service registers one or more AIDs in the {@link #CATEGORY_PAYMENT} + * category, but the user has indicated that foreground apps are not allowed + * to override the default payment service. + * <li> If the service registers one or more AIDs in the {@link #CATEGORY_OTHER} + * category that are also handled by the default payment service, and the + * user has indicated that foreground apps are not allowed to override the + * default payment service. + * </ul> + * + * <p> Use {@link #categoryAllowsForegroundPreference(String)} to determine + * whether foreground apps can override the default payment service. + * + * <p>Note that this preference is not persisted by the OS, and hence must be + * called every time the Activity is resumed. + * + * @param activity The activity which prefers this service to be invoked + * @param service The service to be preferred while this activity is in the foreground + * @return whether the registration was successful + */ + public boolean setPreferredService(Activity activity, ComponentName service) { + // Verify the activity is in the foreground before calling into NfcService + if (activity == null || service == null) { + throw new NullPointerException("activity or service or category is null"); + } + if (!activity.isResumed()) { + throw new IllegalArgumentException("Activity must be resumed."); + } + try { + return sService.setPreferredService(service); + } catch (RemoteException e) { + // Try one more time + recoverService(); + if (sService == null) { + Log.e(TAG, "Failed to recover CardEmulationService."); + return false; + } + try { + return sService.setPreferredService(service); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to reach CardEmulationService."); + return false; + } + } + } + + /** + * Unsets the preferred service for the specified Activity. + * + * <p>Note that the specified Activity must still be in resumed + * state at the time of this call. A good place to call this method + * is in your {@link Activity#onPause} implementation. + * + * @param activity The activity which the service was registered for + * @return true when successful + */ + public boolean unsetPreferredService(Activity activity) { + if (activity == null) { + throw new NullPointerException("activity is null"); + } + if (!activity.isResumed()) { + throw new IllegalArgumentException("Activity must be resumed."); + } + try { + return sService.unsetPreferredService(); + } catch (RemoteException e) { + // Try one more time + recoverService(); + if (sService == null) { + Log.e(TAG, "Failed to recover CardEmulationService."); + return false; + } + try { + return sService.unsetPreferredService(); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to reach CardEmulationService."); + return false; + } + } + } + + /** * @hide */ public boolean setDefaultServiceForCategory(ComponentName service, String category) { diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index e78ce33..4857533 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Formatter; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -30,6 +31,8 @@ import android.telephony.SignalStrength; import android.text.format.DateFormat; import android.util.Printer; import android.util.SparseArray; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import android.util.TimeUtils; import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; @@ -135,7 +138,7 @@ public abstract class BatteryStats implements Parcelable { /** * Bump the version on this if the checkin format changes. */ - private static final int BATTERY_STATS_CHECKIN_VERSION = 7; + private static final int BATTERY_STATS_CHECKIN_VERSION = 8; private static final long BYTES_PER_KB = 1024; private static final long BYTES_PER_MB = 1048576; // 1024^2 @@ -537,6 +540,7 @@ public abstract class BatteryStats implements Parcelable { public static final byte CMD_START = 4; public static final byte CMD_CURRENT_TIME = 5; public static final byte CMD_OVERFLOW = 6; + public static final byte CMD_RESET = 7; public byte cmd = CMD_NULL; @@ -620,6 +624,8 @@ public abstract class BatteryStats implements Parcelable { public static final int EVENT_SYNC = 0x0004; // Number of event types. public static final int EVENT_COUNT = 0x0005; + // Mask to extract out only the type part of the event. + public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH); public static final int EVENT_PROC_START = EVENT_PROC | EVENT_FLAG_START; public static final int EVENT_PROC_FINISH = EVENT_PROC | EVENT_FLAG_FINISH; @@ -684,7 +690,7 @@ public abstract class BatteryStats implements Parcelable { dest.writeInt(eventCode); eventTag.writeToParcel(dest, flags); } - if (cmd == CMD_CURRENT_TIME) { + if (cmd == CMD_CURRENT_TIME || cmd == CMD_RESET) { dest.writeLong(currentTime); } } @@ -722,7 +728,7 @@ public abstract class BatteryStats implements Parcelable { eventCode = EVENT_NONE; eventTag = null; } - if (cmd == CMD_CURRENT_TIME) { + if (cmd == CMD_CURRENT_TIME || cmd == CMD_RESET) { currentTime = src.readLong(); } else { currentTime = 0; @@ -833,7 +839,59 @@ public abstract class BatteryStats implements Parcelable { return true; } } - + + public final static class HistoryEventTracker { + private final HashMap<String, SparseIntArray>[] mActiveEvents + = (HashMap<String, SparseIntArray>[]) new HashMap[HistoryItem.EVENT_COUNT]; + + public boolean updateState(int code, String name, int uid, int poolIdx) { + if ((code&HistoryItem.EVENT_FLAG_START) != 0) { + int idx = code&HistoryItem.EVENT_TYPE_MASK; + HashMap<String, SparseIntArray> active = mActiveEvents[idx]; + if (active == null) { + active = new HashMap<String, SparseIntArray>(); + mActiveEvents[idx] = active; + } + SparseIntArray uids = active.get(name); + if (uids == null) { + uids = new SparseIntArray(); + active.put(name, uids); + } + if (uids.indexOfKey(uid) >= 0) { + // Already set, nothing to do! + return false; + } + uids.put(uid, poolIdx); + } else if ((code&HistoryItem.EVENT_FLAG_FINISH) != 0) { + int idx = code&HistoryItem.EVENT_TYPE_MASK; + HashMap<String, SparseIntArray> active = mActiveEvents[idx]; + if (active == null) { + // not currently active, nothing to do. + return false; + } + SparseIntArray uids = active.get(name); + if (uids == null) { + // not currently active, nothing to do. + return false; + } + idx = uids.indexOfKey(uid); + if (idx < 0) { + // not currently active, nothing to do. + return false; + } + uids.removeAt(idx); + if (uids.size() <= 0) { + active.remove(name); + } + } + return true; + } + + public HashMap<String, SparseIntArray> getStateForEvent(int code) { + return mActiveEvents[code]; + } + } + public static final class BitDescription { public final int mask; public final int shift; @@ -861,7 +919,7 @@ public abstract class BatteryStats implements Parcelable { this.shortValues = shortValues; } } - + public abstract int getHistoryTotalSize(); public abstract int getHistoryUsedSize(); @@ -2958,10 +3016,14 @@ public abstract class BatteryStats implements Parcelable { pw.print(":"); } pw.println("START"); - } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) { + } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME + || rec.cmd == HistoryItem.CMD_RESET) { if (checkin) { pw.print(":"); } + if (rec.cmd == HistoryItem.CMD_RESET) { + pw.print("RESET:"); + } pw.print("TIME:"); if (checkin) { pw.println(rec.currentTime); @@ -3187,6 +3249,86 @@ public abstract class BatteryStats implements Parcelable { public static final int DUMP_INCLUDE_HISTORY = 1<<3; public static final int DUMP_VERBOSE = 1<<4; + private void dumpHistoryLocked(PrintWriter pw, int flags, long histStart, boolean checkin) { + final HistoryPrinter hprinter = new HistoryPrinter(); + final HistoryItem rec = new HistoryItem(); + long lastTime = -1; + long baseTime = -1; + boolean printed = false; + HistoryEventTracker tracker = null; + while (getNextHistoryLocked(rec)) { + lastTime = rec.time; + if (baseTime < 0) { + baseTime = lastTime; + } + if (rec.time >= histStart) { + if (histStart >= 0 && !printed) { + if (rec.cmd == HistoryItem.CMD_CURRENT_TIME + || rec.cmd == HistoryItem.CMD_RESET) { + printed = true; + } else if (rec.currentTime != 0) { + printed = true; + byte cmd = rec.cmd; + rec.cmd = HistoryItem.CMD_CURRENT_TIME; + if (checkin) { + pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); + pw.print(HISTORY_DATA); pw.print(','); + } + hprinter.printNextItem(pw, rec, baseTime, checkin, + (flags&DUMP_VERBOSE) != 0); + rec.cmd = cmd; + } + if (tracker != null) { + int oldCode = rec.eventCode; + HistoryTag oldTag = rec.eventTag; + rec.eventTag = new HistoryTag(); + for (int i=0; i<HistoryItem.EVENT_COUNT; i++) { + HashMap<String, SparseIntArray> active + = tracker.getStateForEvent(i); + if (active == null) { + continue; + } + for (HashMap.Entry<String, SparseIntArray> ent + : active.entrySet()) { + SparseIntArray uids = ent.getValue(); + for (int j=0; j<uids.size(); j++) { + rec.eventCode = i; + rec.eventTag.string = ent.getKey(); + rec.eventTag.uid = uids.keyAt(j); + rec.eventTag.poolIdx = uids.valueAt(j); + if (checkin) { + pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); + pw.print(HISTORY_DATA); pw.print(','); + } + hprinter.printNextItem(pw, rec, baseTime, checkin, + (flags&DUMP_VERBOSE) != 0); + } + } + } + rec.eventCode = oldCode; + rec.eventTag = oldTag; + tracker = null; + } + } + if (checkin) { + pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); + pw.print(HISTORY_DATA); pw.print(','); + } + hprinter.printNextItem(pw, rec, baseTime, checkin, + (flags&DUMP_VERBOSE) != 0); + } else if (rec.eventCode != HistoryItem.EVENT_NONE) { + if (tracker == null) { + tracker = new HistoryEventTracker(); + } + tracker.updateState(rec.eventCode, rec.eventTag.string, + rec.eventTag.uid, rec.eventTag.poolIdx); + } + } + if (histStart >= 0) { + pw.print(checkin ? "NEXT: " : " NEXT: "); pw.println(lastTime+1); + } + } + /** * Dumps a human-readable summary of the battery statistics to the given PrintWriter. * @@ -3200,9 +3342,6 @@ public abstract class BatteryStats implements Parcelable { (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0; if ((flags&DUMP_HISTORY_ONLY) != 0 || !filtering) { - long now = getHistoryBaseTime() + SystemClock.elapsedRealtime(); - - final HistoryItem rec = new HistoryItem(); final long historyTotalSize = getHistoryTotalSize(); final long historyUsedSize = getHistoryUsedSize(); if (startIteratingHistoryLocked()) { @@ -3218,35 +3357,7 @@ public abstract class BatteryStats implements Parcelable { pw.print(" strings using "); printSizeValue(pw, getHistoryStringPoolBytes()); pw.println("):"); - HistoryPrinter hprinter = new HistoryPrinter(); - long lastTime = -1; - long baseTime = -1; - boolean printed = false; - while (getNextHistoryLocked(rec)) { - lastTime = rec.time; - if (baseTime < 0) { - baseTime = lastTime; - } - if (rec.time >= histStart) { - if (histStart >= 0 && !printed) { - if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) { - printed = true; - } else if (rec.currentTime != 0) { - printed = true; - byte cmd = rec.cmd; - rec.cmd = HistoryItem.CMD_CURRENT_TIME; - hprinter.printNextItem(pw, rec, baseTime, false, - (flags&DUMP_VERBOSE) != 0); - rec.cmd = cmd; - } - } - hprinter.printNextItem(pw, rec, baseTime, false, - (flags&DUMP_VERBOSE) != 0); - } - } - if (histStart >= 0) { - pw.print(" NEXT: "); pw.println(lastTime+1); - } + dumpHistoryLocked(pw, flags, histStart, false); pw.println(); } finally { finishIteratingHistoryLocked(); @@ -3255,6 +3366,7 @@ public abstract class BatteryStats implements Parcelable { if (startIteratingOldHistoryLocked()) { try { + final HistoryItem rec = new HistoryItem(); pw.println("Old battery History:"); HistoryPrinter hprinter = new HistoryPrinter(); long baseTime = -1; @@ -3348,7 +3460,6 @@ public abstract class BatteryStats implements Parcelable { (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0; if ((flags&DUMP_INCLUDE_HISTORY) != 0 || (flags&DUMP_HISTORY_ONLY) != 0) { - final HistoryItem rec = new HistoryItem(); if (startIteratingHistoryLocked()) { try { for (int i=0; i<getHistoryStringPoolSize(); i++) { @@ -3365,37 +3476,7 @@ public abstract class BatteryStats implements Parcelable { pw.print("\""); pw.println(); } - HistoryPrinter hprinter = new HistoryPrinter(); - long lastTime = -1; - long baseTime = -1; - boolean printed = false; - while (getNextHistoryLocked(rec)) { - lastTime = rec.time; - if (baseTime < 0) { - baseTime = lastTime; - } - if (rec.time >= histStart) { - if (histStart >= 0 && !printed) { - if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) { - printed = true; - } else if (rec.currentTime != 0) { - printed = true; - byte cmd = rec.cmd; - rec.cmd = HistoryItem.CMD_CURRENT_TIME; - pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); - pw.print(HISTORY_DATA); pw.print(','); - hprinter.printNextItem(pw, rec, baseTime, true, false); - rec.cmd = cmd; - } - } - pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); - pw.print(HISTORY_DATA); pw.print(','); - hprinter.printNextItem(pw, rec, baseTime, true, false); - } - } - if (histStart >= 0) { - pw.print("NEXT: "); pw.println(lastTime+1); - } + dumpHistoryLocked(pw, flags, histStart, true); } finally { finishIteratingHistoryLocked(); } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index f5ff185..9e03f95 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -455,4 +455,14 @@ interface INetworkManagementService * Check whether the mobile radio is currently active. */ boolean isNetworkActive(); + + /** + * setup a new network + */ + void createNetwork(int netId, String iface); + + /** + * remove a network + */ + void removeNetwork(int netId); } diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 899a958..cd47099 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -36,6 +36,7 @@ interface IUserManager { Bitmap getUserIcon(int userHandle); List<UserInfo> getUsers(boolean excludeDying); List<UserInfo> getProfiles(int userHandle, boolean enabledOnly); + UserInfo getProfileParent(int userHandle); UserInfo getUserInfo(int userHandle); boolean isRestricted(); void setGuestEnabled(boolean enable); diff --git a/core/java/android/os/ParcelableParcel.aidl b/core/java/android/os/ParcelableParcel.aidl new file mode 100644 index 0000000..61f730c --- /dev/null +++ b/core/java/android/os/ParcelableParcel.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.os; + +parcelable ParcelableParcel; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index e379621..312cdbe 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -301,7 +301,8 @@ public class UserManager { } /** - * Returns the user handle for the user that this application is running for. + * Returns the user handle for the user that the calling process is running on. + * * @return the user handle of the user making this call. * @hide */ @@ -617,7 +618,8 @@ public class UserManager { } /** - * Returns a list of UserHandles for profiles associated with this user, including this user. + * Returns a list of UserHandles for profiles associated with the user that the calling process + * is running on, including the user itself. * * @return A non-empty list of UserHandles associated with the calling user. */ @@ -638,6 +640,21 @@ public class UserManager { } /** + * Returns the parent of the profile which this method is called from + * or null if called from a user that is not a profile. + * + * @hide + */ + public UserInfo getProfileParent(int userHandle) { + try { + return mService.getProfileParent(userHandle); + } catch (RemoteException re) { + Log.w(TAG, "Could not get profile parent", re); + return null; + } + } + + /** * If the target user is a managed profile of the calling user or the caller * is itself a managed profile, then this returns a badged copy of the given * icon to be able to distinguish it from the original icon. @@ -664,7 +681,7 @@ public class UserManager { private int getBadgeResIdForUser(int userHandle) { // Return the framework-provided badge. - List<UserInfo> userProfiles = getProfiles(UserHandle.myUserId()); + List<UserInfo> userProfiles = getProfiles(getUserHandle()); for (UserInfo user : userProfiles) { if (user.id == userHandle && user.isManagedProfile()) { diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index e4f73cb..811751d 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -167,7 +167,7 @@ public final class PrintManager { /** * Callback notifying that a print job state changed. - * + * * @param printJobId The print job id. */ public void onPrintJobStateChanged(PrintJobId printJobId); @@ -175,7 +175,7 @@ public final class PrintManager { /** * Creates a new instance. - * + * * @param context The current context in which to operate. * @param service The backing system service. * @hide @@ -207,13 +207,17 @@ public final class PrintManager { /** * Creates an instance that can access all print jobs. - * + * * @param userId The user id for which to get all print jobs. * @return An instance if the caller has the permission to access all print * jobs, null otherwise. * @hide */ public PrintManager getGlobalPrintManagerForUser(int userId) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return null; + } return new PrintManager(mContext, mService, userId, APP_ID_ANY); } @@ -228,11 +232,15 @@ public final class PrintManager { /** * Adds a listener for observing the state of print jobs. - * + * * @param listener The listener to add. * @hide */ public void addPrintJobStateChangeListener(PrintJobStateChangeListener listener) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return; + } if (mPrintJobStateChangeListeners == null) { mPrintJobStateChangeListeners = new ArrayMap<PrintJobStateChangeListener, PrintJobStateChangeListenerWrapper>(); @@ -249,11 +257,15 @@ public final class PrintManager { /** * Removes a listener for observing the state of print jobs. - * + * * @param listener The listener to remove. * @hide */ public void removePrintJobStateChangeListener(PrintJobStateChangeListener listener) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return; + } if (mPrintJobStateChangeListeners == null) { return; } @@ -275,12 +287,16 @@ public final class PrintManager { /** * Gets a print job given its id. - * + * * @return The print job list. * @see PrintJob * @hide */ public PrintJob getPrintJob(PrintJobId printJobId) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return null; + } try { PrintJobInfo printJob = mService.getPrintJobInfo(printJobId, mAppId, mUserId); if (printJob != null) { @@ -294,11 +310,15 @@ public final class PrintManager { /** * Gets the print jobs for this application. - * + * * @return The print job list. * @see PrintJob */ public List<PrintJob> getPrintJobs() { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return Collections.emptyList(); + } try { List<PrintJobInfo> printJobInfos = mService.getPrintJobInfos(mAppId, mUserId); if (printJobInfos == null) { @@ -317,6 +337,10 @@ public final class PrintManager { } void cancelPrintJob(PrintJobId printJobId) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return; + } try { mService.cancelPrintJob(printJobId, mAppId, mUserId); } catch (RemoteException re) { @@ -325,6 +349,10 @@ public final class PrintManager { } void restartPrintJob(PrintJobId printJobId) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return; + } try { mService.restartPrintJob(printJobId, mAppId, mUserId); } catch (RemoteException re) { @@ -383,6 +411,10 @@ public final class PrintManager { */ public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter, PrintAttributes attributes) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return null; + } if (!(mContext instanceof Activity)) { throw new IllegalStateException("Can print only from an activity"); } @@ -418,11 +450,15 @@ public final class PrintManager { /** * Gets the list of enabled print services. - * + * * @return The enabled service list or an empty list. * @hide */ public List<PrintServiceInfo> getEnabledPrintServices() { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return Collections.emptyList(); + } try { List<PrintServiceInfo> enabledServices = mService.getEnabledPrintServices(mUserId); if (enabledServices != null) { @@ -436,11 +472,15 @@ public final class PrintManager { /** * Gets the list of installed print services. - * + * * @return The installed service list or an empty list. * @hide */ public List<PrintServiceInfo> getInstalledPrintServices() { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return Collections.emptyList(); + } try { List<PrintServiceInfo> installedServices = mService.getInstalledPrintServices(mUserId); if (installedServices != null) { @@ -456,6 +496,10 @@ public final class PrintManager { * @hide */ public PrinterDiscoverySession createPrinterDiscoverySession() { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return null; + } return new PrinterDiscoverySession(mService, mContext, mUserId); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index dc618c8..1847b55 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4529,6 +4529,12 @@ public final class Settings { public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component"; /** + * Whether NFC payment is handled by the foreground application or a default. + * @hide + */ + public static final String NFC_PAYMENT_FOREGROUND = "nfc_payment_foreground"; + + /** * Specifies the package name currently configured to be the primary sms application * @hide */ diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index d4b29d8..d4919eb 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -17,11 +17,15 @@ package android.service.notification; import android.service.notification.StatusBarNotification; +import android.service.notification.NotificationOrderUpdate; /** @hide */ oneway interface INotificationListener { - void onListenerConnected(in String[] notificationKeys); - void onNotificationPosted(in StatusBarNotification notification); - void onNotificationRemoved(in StatusBarNotification notification); + void onListenerConnected(in NotificationOrderUpdate update); + void onNotificationPosted(in StatusBarNotification notification, + in NotificationOrderUpdate update); + void onNotificationRemoved(in StatusBarNotification notification, + in NotificationOrderUpdate update); + void onNotificationOrderUpdate(in NotificationOrderUpdate update); }
\ No newline at end of file diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 3673f03..a94f45a 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -22,10 +22,13 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; +import android.os.RemoteException; import android.os.ServiceManager; -import android.os.UserHandle; import android.util.Log; +import java.util.Comparator; +import java.util.HashMap; + /** * A service that receives calls from the system when new notifications are posted or removed. * <p>To extend this class, you must declare the service in your manifest file with @@ -46,6 +49,7 @@ public abstract class NotificationListenerService extends Service { + "[" + getClass().getSimpleName() + "]"; private INotificationListenerWrapper mWrapper = null; + private String[] mNotificationKeys; private INotificationManager mNoMan; @@ -95,6 +99,15 @@ public abstract class NotificationListenerService extends Service { // optional } + /** + * Implement this method to be notified when the notification order cahnges. + * + * Call {@link #getOrderedNotificationKeys()} to retrieve the new order. + */ + public void onNotificationOrderUpdate() { + // optional + } + private final INotificationManager getNotificationInterface() { if (mNoMan == null) { mNoMan = INotificationManager.Stub.asInterface( @@ -202,7 +215,7 @@ public abstract class NotificationListenerService extends Service { * Request the list of outstanding notifications (that is, those that are visible to the * current user). Useful when you don't know what's already been posted. * - * @return An array of active notifications. + * @return An array of active notifications, sorted in natural order. */ public StatusBarNotification[] getActiveNotifications() { return getActiveNotifications(null /*all*/); @@ -213,7 +226,8 @@ public abstract class NotificationListenerService extends Service { * current user). Useful when you don't know what's already been posted. * * @param keys A specific list of notification keys, or {@code null} for all. - * @return An array of active notifications. + * @return An array of active notifications, sorted in natural order + * if {@code keys} is {@code null}. */ public StatusBarNotification[] getActiveNotifications(String[] keys) { if (!isBound()) return null; @@ -226,21 +240,15 @@ public abstract class NotificationListenerService extends Service { } /** - * Request the list of outstanding notification keys(that is, those that are visible to the - * current user). You can use the notification keys for subsequent retrieval via + * Request the list of notification keys in their current natural order. + * You can use the notification keys for subsequent retrieval via * {@link #getActiveNotifications(String[]) or dismissal via * {@link #cancelNotifications(String[]). * - * @return An array of active notification keys. + * @return An array of active notification keys, in their natural order. */ - public String[] getActiveNotificationKeys() { - if (!isBound()) return null; - try { - return getNotificationInterface().getActiveNotificationKeysFromListener(mWrapper); - } catch (android.os.RemoteException ex) { - Log.v(TAG, "Unable to contact notification manager", ex); - } - return null; + public String[] getOrderedNotificationKeys() { + return mNotificationKeys; } @Override @@ -261,28 +269,60 @@ public abstract class NotificationListenerService extends Service { private class INotificationListenerWrapper extends INotificationListener.Stub { @Override - public void onNotificationPosted(StatusBarNotification sbn) { + public void onNotificationPosted(StatusBarNotification sbn, + NotificationOrderUpdate update) { try { - NotificationListenerService.this.onNotificationPosted(sbn); + // protect subclass from concurrent modifications of (@link mNotificationKeys}. + synchronized (mWrapper) { + updateNotificationKeys(update); + NotificationListenerService.this.onNotificationPosted(sbn); + } } catch (Throwable t) { - Log.w(TAG, "Error running onNotificationPosted", t); + Log.w(TAG, "Error running onOrderedNotificationPosted", t); } } @Override - public void onNotificationRemoved(StatusBarNotification sbn) { + public void onNotificationRemoved(StatusBarNotification sbn, + NotificationOrderUpdate update) { try { - NotificationListenerService.this.onNotificationRemoved(sbn); + // protect subclass from concurrent modifications of (@link mNotificationKeys}. + synchronized (mWrapper) { + updateNotificationKeys(update); + NotificationListenerService.this.onNotificationRemoved(sbn); + } } catch (Throwable t) { Log.w(TAG, "Error running onNotificationRemoved", t); } } @Override - public void onListenerConnected(String[] notificationKeys) { + public void onListenerConnected(NotificationOrderUpdate update) { try { - NotificationListenerService.this.onListenerConnected(notificationKeys); + // protect subclass from concurrent modifications of (@link mNotificationKeys}. + synchronized (mWrapper) { + updateNotificationKeys(update); + NotificationListenerService.this.onListenerConnected(mNotificationKeys); + } } catch (Throwable t) { Log.w(TAG, "Error running onListenerConnected", t); } } + @Override + public void onNotificationOrderUpdate(NotificationOrderUpdate update) + throws RemoteException { + try { + // protect subclass from concurrent modifications of (@link mNotificationKeys}. + synchronized (mWrapper) { + updateNotificationKeys(update); + NotificationListenerService.this.onNotificationOrderUpdate(); + } + } catch (Throwable t) { + Log.w(TAG, "Error running onNotificationOrderUpdate", t); + } + } + } + + private void updateNotificationKeys(NotificationOrderUpdate update) { + // TODO: avoid garbage by comparing the lists + mNotificationKeys = update.getOrderedKeys(); } } diff --git a/core/java/android/service/notification/NotificationOrderUpdate.aidl b/core/java/android/service/notification/NotificationOrderUpdate.aidl new file mode 100644 index 0000000..5d50641 --- /dev/null +++ b/core/java/android/service/notification/NotificationOrderUpdate.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.notification; + +parcelable NotificationOrderUpdate; diff --git a/core/java/android/service/notification/NotificationOrderUpdate.java b/core/java/android/service/notification/NotificationOrderUpdate.java new file mode 100644 index 0000000..20e19a3 --- /dev/null +++ b/core/java/android/service/notification/NotificationOrderUpdate.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.service.notification; + +import android.os.Parcel; +import android.os.Parcelable; + +public class NotificationOrderUpdate implements Parcelable { + // TODO replace this with an update instead of the whole array + private final String[] mKeys; + + /** @hide */ + public NotificationOrderUpdate(String[] keys) { + this.mKeys = keys; + } + + public NotificationOrderUpdate(Parcel in) { + this.mKeys = in.readStringArray(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeStringArray(this.mKeys); + } + + public static final Parcelable.Creator<NotificationOrderUpdate> CREATOR + = new Parcelable.Creator<NotificationOrderUpdate>() { + public NotificationOrderUpdate createFromParcel(Parcel parcel) { + return new NotificationOrderUpdate(parcel); + } + + public NotificationOrderUpdate[] newArray(int size) { + return new NotificationOrderUpdate[size]; + } + }; + + /** + * @hide + * @return ordered list of keys + */ + String[] getOrderedKeys() { + return mKeys; + } +} diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 05e202b..2d1016a 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1685,10 +1685,6 @@ public class KeyEvent extends InputEvent implements Parcelable { case KeyEvent.KEYCODE_BRIGHTNESS_DOWN: case KeyEvent.KEYCODE_BRIGHTNESS_UP: case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: - case KeyEvent.KEYCODE_DPAD_UP: - case KeyEvent.KEYCODE_DPAD_RIGHT: - case KeyEvent.KEYCODE_DPAD_DOWN: - case KeyEvent.KEYCODE_DPAD_LEFT: return true; } diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index a675821..be3b6ce 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -21,6 +21,8 @@ import android.graphics.CanvasProperty; import android.graphics.Paint; import android.util.SparseIntArray; +import com.android.internal.util.VirtualRefBasePtr; + import java.lang.ref.WeakReference; /** @@ -70,28 +72,32 @@ public final class RenderNodeAnimator { public static final int DELTA_TYPE_DELTA = 1; private RenderNode mTarget; - private long mNativePtr; + private VirtualRefBasePtr mNativePtr; public int mapViewPropertyToRenderProperty(int viewProperty) { return sViewPropertyAnimatorMap.get(viewProperty); } public RenderNodeAnimator(int property, int deltaType, float deltaValue) { - mNativePtr = nCreateAnimator(new WeakReference<RenderNodeAnimator>(this), - property, deltaType, deltaValue); + init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this), + property, deltaType, deltaValue)); } public RenderNodeAnimator(CanvasProperty<Float> property, int deltaType, float deltaValue) { - mNativePtr = nCreateCanvasPropertyFloatAnimator( + init(nCreateCanvasPropertyFloatAnimator( new WeakReference<RenderNodeAnimator>(this), - property.getNativeContainer(), deltaType, deltaValue); + property.getNativeContainer(), deltaType, deltaValue)); } public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, int deltaType, float deltaValue) { - mNativePtr = nCreateCanvasPropertyPaintAnimator( + init(nCreateCanvasPropertyPaintAnimator( new WeakReference<RenderNodeAnimator>(this), - property.getNativeContainer(), paintField, deltaType, deltaValue); + property.getNativeContainer(), paintField, deltaType, deltaValue)); + } + + private void init(long ptr) { + mNativePtr = new VirtualRefBasePtr(ptr); } public void start(View target) { @@ -115,11 +121,11 @@ public final class RenderNodeAnimator { } public void setDuration(int duration) { - nSetDuration(mNativePtr, duration); + nSetDuration(mNativePtr.get(), duration); } long getNativeAnimator() { - return mNativePtr; + return mNativePtr.get(); } private void onFinished() { @@ -134,16 +140,6 @@ public final class RenderNodeAnimator { } } - @Override - protected void finalize() throws Throwable { - try { - nUnref(mNativePtr); - mNativePtr = 0; - } finally { - super.finalize(); - } - } - private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis, int property, int deltaValueType, float deltaValue); private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis, @@ -151,5 +147,4 @@ public final class RenderNodeAnimator { private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis, long canvasProperty, int paintField, int deltaValueType, float deltaValue); private static native void nSetDuration(long nativePtr, int duration); - private static native void nUnref(long nativePtr); } diff --git a/core/java/android/view/inputmethod/CorrectionInfo.java b/core/java/android/view/inputmethod/CorrectionInfo.java index 1b04e49..a43dfe8 100644 --- a/core/java/android/view/inputmethod/CorrectionInfo.java +++ b/core/java/android/view/inputmethod/CorrectionInfo.java @@ -88,16 +88,15 @@ public final class CorrectionInfo implements Parcelable { /** * Used to make this class parcelable. */ - public static final Parcelable.Creator<CorrectionInfo> CREATOR - = new Parcelable.Creator<CorrectionInfo>() { - public CorrectionInfo createFromParcel(Parcel source) { - return new CorrectionInfo(source); - } - - public CorrectionInfo[] newArray(int size) { - return new CorrectionInfo[size]; - } - }; + public static final Parcelable.Creator<CorrectionInfo> CREATOR = + new Parcelable.Creator<CorrectionInfo>() { + public CorrectionInfo createFromParcel(Parcel source) { + return new CorrectionInfo(source); + } + public CorrectionInfo[] newArray(int size) { + return new CorrectionInfo[size]; + } + }; public int describeContents() { return 0; diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.aidl b/core/java/android/view/inputmethod/CursorAnchorInfo.aidl new file mode 100644 index 0000000..2ee9edb --- /dev/null +++ b/core/java/android/view/inputmethod/CursorAnchorInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.inputmethod; + +parcelable CursorAnchorInfo; diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java new file mode 100644 index 0000000..92455df --- /dev/null +++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.view.inputmethod; + +import android.graphics.Matrix; +import android.graphics.RectF; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.Layout; +import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder; + +import java.util.Objects; + +/** + * Positional information about the text insertion point and characters in the composition string. + * + * <p>This class encapsulates locations of the text insertion point and the composition string in + * the screen coordinates so that IMEs can render their UI components near where the text is + * actually inserted.</p> + */ +public final class CursorAnchorInfo implements Parcelable { + private final int mSelectionStart; + private final int mSelectionEnd; + private final int mCandidatesStart; + private final int mCandidatesEnd; + + /** + * Horizontal position of the insertion marker, in the local coordinates that will be + * transformed with the transformation matrix when rendered on the screen. This should be + * calculated or compatible with {@link Layout#getPrimaryHorizontal(int)}. This can be + * {@code java.lang.Float.NaN} when no value is specified. + */ + private final float mInsertionMarkerHorizontal; + /** + * Vertical position of the insertion marker, in the local coordinates that will be + * transformed with the transformation matrix when rendered on the screen. This should be + * calculated or compatible with {@link Layout#getLineTop(int)}. This can be + * {@code java.lang.Float.NaN} when no value is specified. + */ + private final float mInsertionMarkerTop; + /** + * Vertical position of the insertion marker, in the local coordinates that will be + * transformed with the transformation matrix when rendered on the screen. This should be + * calculated or compatible with {@link Layout#getLineBaseline(int)}. This can be + * {@code java.lang.Float.NaN} when no value is specified. + */ + private final float mInsertionMarkerBaseline; + /** + * Vertical position of the insertion marker, in the local coordinates that will be + * transformed with the transformation matrix when rendered on the screen. This should be + * calculated or compatible with {@link Layout#getLineBottom(int)}. This can be + * {@code java.lang.Float.NaN} when no value is specified. + */ + private final float mInsertionMarkerBottom; + + /** + * Container of rectangular position of characters, keyed with character index in a unit of + * Java chars, in the local coordinates that will be transformed with the transformation matrix + * when rendered on the screen. + */ + private final SparseRectFArray mCharacterRects; + + /** + * Transformation matrix that is applied to any positional information of this class to + * transform local coordinates into screen coordinates. + */ + private final Matrix mMatrix; + + public CursorAnchorInfo(final Parcel source) { + mSelectionStart = source.readInt(); + mSelectionEnd = source.readInt(); + mCandidatesStart = source.readInt(); + mCandidatesEnd = source.readInt(); + mInsertionMarkerHorizontal = source.readFloat(); + mInsertionMarkerTop = source.readFloat(); + mInsertionMarkerBaseline = source.readFloat(); + mInsertionMarkerBottom = source.readFloat(); + mCharacterRects = source.readParcelable(SparseRectFArray.class.getClassLoader()); + mMatrix = new Matrix(); + mMatrix.setValues(source.createFloatArray()); + } + + /** + * Used to package this object into a {@link Parcel}. + * + * @param dest The {@link Parcel} to be written. + * @param flags The flags used for parceling. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSelectionStart); + dest.writeInt(mSelectionEnd); + dest.writeInt(mCandidatesStart); + dest.writeInt(mCandidatesEnd); + dest.writeFloat(mInsertionMarkerHorizontal); + dest.writeFloat(mInsertionMarkerTop); + dest.writeFloat(mInsertionMarkerBaseline); + dest.writeFloat(mInsertionMarkerBottom); + dest.writeParcelable(mCharacterRects, flags); + final float[] matrixArray = new float[9]; + mMatrix.getValues(matrixArray); + dest.writeFloatArray(matrixArray); + } + + @Override + public int hashCode(){ + // TODO: Improve the hash function. + final float floatHash = mSelectionStart + mSelectionEnd + mCandidatesStart + mCandidatesEnd + + mInsertionMarkerHorizontal + mInsertionMarkerTop + mInsertionMarkerBaseline + + mInsertionMarkerBottom; + int hash = floatHash > 0 ? (int) floatHash : (int)(-floatHash); + if (mCharacterRects != null) { + hash += mCharacterRects.hashCode(); + } + hash += mMatrix.hashCode(); + return hash; + } + + @Override + public boolean equals(Object obj){ + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (!(obj instanceof CursorAnchorInfo)) { + return false; + } + final CursorAnchorInfo that = (CursorAnchorInfo) obj; + if (hashCode() != that.hashCode()) { + return false; + } + if (mSelectionStart != that.mSelectionStart + || mSelectionEnd != that.mSelectionEnd + || mCandidatesStart != that.mCandidatesStart + || mCandidatesEnd != that.mCandidatesEnd) { + return false; + } + if (!Objects.equals(mCharacterRects, that.mCharacterRects)) { + return false; + } + if (!Objects.equals(mMatrix, that.mMatrix)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "SelectionInfo{mSelection=" + mSelectionStart + "," + mSelectionEnd + + " mCandiadtes=" + mCandidatesStart + "," + mCandidatesEnd + + " mInsertionMarkerHorizontal=" + mInsertionMarkerHorizontal + + " mInsertionMarkerTop=" + mInsertionMarkerTop + + " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline + + " mInsertionMarkerBottom=" + mInsertionMarkerBottom + + " mCharacterRects=" + (mCharacterRects != null ? mCharacterRects : "null") + + " mMatrix=" + mMatrix + + "}"; + } + + /** + * Builder for {@link CursorAnchorInfo}. This class is not designed to be thread-safe. + */ + public static final class CursorAnchorInfoBuilder { + /** + * Sets the text range of the selection. Calling this can be skipped if there is no + * selection. + */ + public CursorAnchorInfoBuilder setSelectionRange(final int newStart, final int newEnd) { + mSelectionStart = newStart; + mSelectionEnd = newEnd; + return this; + } + private int mSelectionStart = -1; + private int mSelectionEnd = -1; + + /** + * Sets the text range of the composition string. Calling this can be skipped if there is + * no composition. + */ + public CursorAnchorInfoBuilder setCandidateRange(final int start, final int end) { + mCandidateStart = start; + mCandidateEnd = end; + return this; + } + private int mCandidateStart = -1; + private int mCandidateEnd = -1; + + /** + * Sets the location of the text insertion point (zero width cursor) as a rectangle in + * local coordinates. Calling this can be skipped when there is no text insertion point; + * however if there is an insertion point, editors must call this method. + * @param horizontalPosition horizontal position of the insertion marker, in the local + * coordinates that will be transformed with the transformation matrix when rendered on the + * screen. This should be calculated or compatible with + * {@link Layout#getPrimaryHorizontal(int)}. + * @param lineTop vertical position of the insertion marker, in the local coordinates that + * will be transformed with the transformation matrix when rendered on the screen. This + * should be calculated or compatible with {@link Layout#getLineTop(int)}. + * @param lineBaseline vertical position of the insertion marker, in the local coordinates + * that will be transformed with the transformation matrix when rendered on the screen. This + * should be calculated or compatible with {@link Layout#getLineBaseline(int)}. + * @param lineBottom vertical position of the insertion marker, in the local coordinates + * that will be transformed with the transformation matrix when rendered on the screen. This + * should be calculated or compatible with {@link Layout#getLineBottom(int)}. + */ + public CursorAnchorInfoBuilder setInsertionMarkerLocation( + final float horizontalPosition, final float lineTop, final float lineBaseline, + final float lineBottom){ + mInsertionMarkerHorizontal = horizontalPosition; + mInsertionMarkerTop = lineTop; + mInsertionMarkerBaseline = lineBaseline; + mInsertionMarkerBottom = lineBottom; + return this; + } + private float mInsertionMarkerHorizontal = Float.NaN; + private float mInsertionMarkerTop = Float.NaN; + private float mInsertionMarkerBaseline = Float.NaN; + private float mInsertionMarkerBottom = Float.NaN; + + /** + * Adds the bounding box of the character specified with the index. + * <p> + * Editor authors should not call this method for characters that are invisible. + * </p> + * + * @param index index of the character in Java chars units. Must be specified in + * ascending order across successive calls. + * @param leadingEdgeX x coordinate of the leading edge of the character in local + * coordinates, that is, left edge for LTR text and right edge for RTL text. + * @param leadingEdgeY y coordinate of the leading edge of the character in local + * coordinates. + * @param trailingEdgeX x coordinate of the trailing edge of the character in local + * coordinates, that is, right edge for LTR text and left edge for RTL text. + * @param trailingEdgeY y coordinate of the trailing edge of the character in local + * coordinates. + * @throws IllegalArgumentException If the index is a negative value, or not greater than + * all of the previously called indices. + */ + public CursorAnchorInfoBuilder addCharacterRect(final int index, + final float leadingEdgeX, final float leadingEdgeY, final float trailingEdgeX, + final float trailingEdgeY) { + if (index < 0) { + throw new IllegalArgumentException("index must not be a negative integer."); + } + if (mCharacterRectBuilder == null) { + mCharacterRectBuilder = new SparseRectFArrayBuilder(); + } + mCharacterRectBuilder.append(index, leadingEdgeX, leadingEdgeY, trailingEdgeX, + trailingEdgeY); + return this; + } + private SparseRectFArrayBuilder mCharacterRectBuilder = null; + + /** + * Sets the matrix that transforms local coordinates into screen coordinates. + * @param matrix transformation matrix from local coordinates into screen coordinates. null + * is interpreted as an identity matrix. + */ + public CursorAnchorInfoBuilder setMatrix(final Matrix matrix) { + if (matrix != null) { + mMatrix = matrix; + } else { + mMatrix = Matrix.IDENTITY_MATRIX; + } + return this; + } + private Matrix mMatrix = Matrix.IDENTITY_MATRIX; + + /** + * @return {@link CursorAnchorInfo} using parameters in this + * {@link CursorAnchorInfoBuilder}. + */ + public CursorAnchorInfo build() { + return new CursorAnchorInfo(this); + } + + /** + * Resets the internal state so that this instance can be reused to build another + * instance of {@link CursorAnchorInfo}. + */ + public void reset() { + mSelectionStart = -1; + mSelectionEnd = -1; + mCandidateStart = -1; + mCandidateEnd = -1; + mInsertionMarkerHorizontal = Float.NaN; + mInsertionMarkerTop = Float.NaN; + mInsertionMarkerBaseline = Float.NaN; + mInsertionMarkerBottom = Float.NaN; + mMatrix = Matrix.IDENTITY_MATRIX; + if (mCharacterRectBuilder != null) { + mCharacterRectBuilder.reset(); + } + } + } + + private CursorAnchorInfo(final CursorAnchorInfoBuilder builder) { + mSelectionStart = builder.mSelectionStart; + mSelectionEnd = builder.mSelectionEnd; + mCandidatesStart = builder.mCandidateStart; + mCandidatesEnd = builder.mCandidateEnd; + mInsertionMarkerHorizontal = builder.mInsertionMarkerHorizontal; + mInsertionMarkerTop = builder.mInsertionMarkerTop; + mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline; + mInsertionMarkerBottom = builder.mInsertionMarkerBottom; + mCharacterRects = builder.mCharacterRectBuilder != null ? + builder.mCharacterRectBuilder.build() : null; + mMatrix = builder.mMatrix; + } + + /** + * Returns the index where the selection starts. + * @return -1 if there is no selection. + */ + public int getSelectionStart() { + return mSelectionStart; + } + + /** + * Returns the index where the selection ends. + * @return -1 if there is no selection. + */ + public int getSelectionEnd() { + return mSelectionEnd; + } + + /** + * Returns the index where the composition starts. + * @return -1 if there is no composition. + */ + public int getCandidatesStart() { + return mCandidatesStart; + } + + /** + * Returns the index where the composition ends. + * @return -1 if there is no composition. + */ + public int getCandidatesEnd() { + return mCandidatesEnd; + } + + /** + * Returns the horizontal start of the insertion marker, in the local coordinates that will + * be transformed with {@link #getMatrix()} when rendered on the screen. + * @return x coordinate that is compatible with {@link Layout#getPrimaryHorizontal(int)}. + * Pay special care to RTL/LTR handling. + * {@code java.lang.Float.NaN} if not specified. + * @see Layout#getPrimaryHorizontal(int) + */ + public float getInsertionMarkerHorizontal() { + return mInsertionMarkerHorizontal; + } + /** + * Returns the vertical top position of the insertion marker, in the local coordinates that + * will be transformed with {@link #getMatrix()} when rendered on the screen. + * @return y coordinate that is compatible with {@link Layout#getLineTop(int)}. + * {@code java.lang.Float.NaN} if not specified. + */ + public float getInsertionMarkerTop() { + return mInsertionMarkerTop; + } + /** + * Returns the vertical baseline position of the insertion marker, in the local coordinates + * that will be transformed with {@link #getMatrix()} when rendered on the screen. + * @return y coordinate that is compatible with {@link Layout#getLineBaseline(int)}. + * {@code java.lang.Float.NaN} if not specified. + */ + public float getInsertionMarkerBaseline() { + return mInsertionMarkerBaseline; + } + /** + * Returns the vertical bottom position of the insertion marker, in the local coordinates + * that will be transformed with {@link #getMatrix()} when rendered on the screen. + * @return y coordinate that is compatible with {@link Layout#getLineBottom(int)}. + * {@code java.lang.Float.NaN} if not specified. + */ + public float getInsertionMarkerBottom() { + return mInsertionMarkerBottom; + } + + /** + * Returns a new instance of {@link RectF} that indicates the location of the character + * specified with the index. + * <p> + * Note that coordinates are not necessarily contiguous or even monotonous, especially when + * RTL text and LTR text are mixed. + * </p> + * @param index index of the character in a Java chars. + * @return a new instance of {@link RectF} that represents the location of the character in + * local coordinates. null if the character is invisible or the application did not provide + * the location. Note that the {@code left} field can be greater than the {@code right} field + * if the character is in RTL text. + */ + // TODO: Prepare a document about the expected behavior for surrogate pairs, combining + // characters, and non-graphical chars. + public RectF getCharacterRect(final int index) { + if (mCharacterRects == null) { + return null; + } + return mCharacterRects.get(index); + } + + /** + * Returns a new instance of {@link android.graphics.Matrix} that indicates the transformation + * matrix that is to be applied other positional data in this class. + * @return a new instance (copy) of the transformation matrix. + */ + public Matrix getMatrix() { + return new Matrix(mMatrix); + } + + /** + * Used to make this class parcelable. + */ + public static final Parcelable.Creator<CursorAnchorInfo> CREATOR + = new Parcelable.Creator<CursorAnchorInfo>() { + @Override + public CursorAnchorInfo createFromParcel(Parcel source) { + return new CursorAnchorInfo(source); + } + + @Override + public CursorAnchorInfo[] newArray(int size) { + return new CursorAnchorInfo[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 0227873..be0c27d 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -321,7 +321,6 @@ public final class InputMethodManager { * The buffer to retrieve the view location in screen coordinates in {@link #updateCursor}. */ private final int[] mViewTopLeft = new int[2]; - // ----------------------------------------------------------- /** @@ -1435,7 +1434,7 @@ public final class InputMethodManager { || mCurrentTextBoxAttribute == null || mCurMethod == null) { return; } - + if (mCursorSelStart != selStart || mCursorSelEnd != selEnd || mCursorCandStart != candidatesStart || mCursorCandEnd != candidatesEnd) { @@ -1556,6 +1555,31 @@ public final class InputMethodManager { } /** + * Report positional change of the text insertion point and/or characters in the composition + * string. + */ + public void updateCursorAnchorInfo(View view, final CursorAnchorInfo cursorAnchorInfo) { + if (view == null || cursorAnchorInfo == null) { + return; + } + checkFocus(); + synchronized (mH) { + if ((mServedView != view && + (mServedView == null || !mServedView.checkInputConnectionProxy(view))) + || mCurrentTextBoxAttribute == null || mCurMethod == null) { + return; + } + if (DEBUG) Log.d(TAG, "updateCursorAnchorInfo"); + + try { + mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo); + } catch (RemoteException e) { + Log.w(TAG, "IME died: " + mCurId, e); + } + } + } + + /** * Call {@link InputMethodSession#appPrivateCommand(String, Bundle) * InputMethodSession.appPrivateCommand()} on the current Input Method. * @param view Optional View that is sending the command, or null if diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java index 6386299..74fbbc7 100644 --- a/core/java/android/view/inputmethod/InputMethodSession.java +++ b/core/java/android/view/inputmethod/InputMethodSession.java @@ -165,7 +165,7 @@ public interface InputMethodSession { public void appPrivateCommand(String action, Bundle data); /** - * Toggle the soft input window. + * Toggle the soft input window. * Applications can toggle the state of the soft input window. * @param showFlags Provides additional operating flags. May be * 0 or have the {@link InputMethodManager#SHOW_IMPLICIT}, @@ -175,4 +175,14 @@ public interface InputMethodSession { * {@link InputMethodManager#HIDE_NOT_ALWAYS} bit set. */ public void toggleSoftInput(int showFlags, int hideFlags); + + /** + * This method is called when the cursor and/or the character position relevant to text input + * is changed on the screen. This is not called by default. It will only be reported if + * requested by the input method. + * + * @param cursorAnchorInfo Positional information relevant to text input, such as text + * insertion point and composition string. + */ + public void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo); } diff --git a/core/java/android/view/inputmethod/SparseRectFArray.java b/core/java/android/view/inputmethod/SparseRectFArray.java new file mode 100644 index 0000000..40cade7 --- /dev/null +++ b/core/java/android/view/inputmethod/SparseRectFArray.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.view.inputmethod; + +import android.graphics.RectF; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; + +/** + * An implementation of SparseArray specialized for {@link android.graphics.RectF}. + * <p> + * As this is a sparse array, it represents an array of {@link RectF} most of which are null. This + * class could be in some other packages like android.graphics or android.util but currently + * belong to android.view.inputmethod because this class is hidden and used only in input method + * framework. + * </p> + * @hide + */ +public final class SparseRectFArray implements Parcelable { + /** + * The keys, in ascending order, of those {@link RectF} that are not null. For example, + * {@code [null, null, null, Rect1, null, Rect2]} would be represented by {@code [3,5]}. + * @see #mCoordinates + */ + private final int[] mKeys; + + /** + * Stores coordinates of the rectangles, in the order of + * {@code rects[mKeys[0]].left}, {@code rects[mKeys[0]].top}, + * {@code rects[mKeys[0]].right}, {@code rects[mKeys[0]].bottom}, + * {@code rects[mKeys[1]].left}, {@code rects[mKeys[1]].top}, + * {@code rects[mKeys[1]].right}, {@code rects[mKeys[1]].bottom}, + * {@code rects[mKeys[2]].left}, {@code rects[mKeys[2]].top}, .... + */ + private final float[] mCoordinates; + + public SparseRectFArray(final Parcel source) { + mKeys = source.createIntArray(); + mCoordinates = source.createFloatArray(); + } + + /** + * Used to package this object into a {@link Parcel}. + * + * @param dest The {@link Parcel} to be written. + * @param flags The flags used for parceling. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeIntArray(mKeys); + dest.writeFloatArray(mCoordinates); + } + + @Override + public int hashCode() { + // TODO: Improve the hash function. + if (mKeys == null || mKeys.length == 0) { + return 0; + } + int hash = mKeys.length; + // For performance reasons, only the first rectangle is used for the hash code now. + for (int i = 0; i < 4; i++) { + hash *= 31; + hash += mCoordinates[i]; + } + return hash; + } + + @Override + public boolean equals(Object obj){ + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (!(obj instanceof SparseRectFArray)) { + return false; + } + final SparseRectFArray that = (SparseRectFArray) obj; + + return Arrays.equals(mKeys, that.mKeys) && Arrays.equals(mCoordinates, that.mCoordinates); + } + + @Override + public String toString() { + if (mKeys == null || mCoordinates == null) { + return "SparseRectFArray{}"; + } + final StringBuilder sb = new StringBuilder(); + sb.append("SparseRectFArray{"); + for (int i = 0; i < mKeys.length; i++) { + if (i != 0) { + sb.append(", "); + } + final int baseIndex = i * 4; + sb.append(mKeys[i]); + sb.append(":["); + sb.append(mCoordinates[baseIndex + 0]); + sb.append(","); + sb.append(mCoordinates[baseIndex + 1]); + sb.append("],["); + sb.append(mCoordinates[baseIndex + 2]); + sb.append(","); + sb.append(mCoordinates[baseIndex + 3]); + sb.append("]"); + } + sb.append("}"); + return sb.toString(); + } + + /** + * Builder for {@link SparseRectFArray}. This class is not designed to be thread-safe. + * @hide + */ + public static final class SparseRectFArrayBuilder { + /** + * Throws {@link IllegalArgumentException} to make sure that this class is correctly used. + * @param key key to be checked. + */ + private void checkIndex(final int key) { + if (mCount == 0) { + return; + } + if (mKeys[mCount - 1] >= key) { + throw new IllegalArgumentException("key must be greater than all existing keys."); + } + } + + /** + * Extends the internal array if necessary. + */ + private void ensureBufferSize() { + if (mKeys == null) { + mKeys = new int[INITIAL_SIZE]; + } + if (mCoordinates == null) { + mCoordinates = new float[INITIAL_SIZE * 4]; + } + final int requiredIndexArraySize = mCount + 1; + if (mKeys.length <= requiredIndexArraySize) { + final int[] newArray = new int[requiredIndexArraySize * 2]; + System.arraycopy(mKeys, 0, newArray, 0, mCount); + mKeys = newArray; + } + final int requiredCoordinatesArraySize = (mCount + 1) * 4; + if (mCoordinates.length <= requiredCoordinatesArraySize) { + final float[] newArray = new float[requiredCoordinatesArraySize * 2]; + System.arraycopy(mCoordinates, 0, newArray, 0, mCount * 4); + mCoordinates = newArray; + } + } + + /** + * Puts the rectangle with an integer key. + * @param key the key to be associated with the rectangle. It must be greater than all + * existing keys that have been previously specified. + * @param left left of the rectangle. + * @param top top of the rectangle. + * @param right right of the rectangle. + * @param bottom bottom of the rectangle. + * @return the receiver object itself for chaining method calls. + * @throws IllegalArgumentException If the index is not greater than all of existing keys. + */ + public SparseRectFArrayBuilder append(final int key, + final float left, final float top, final float right, final float bottom) { + checkIndex(key); + ensureBufferSize(); + final int baseCoordinatesIndex = mCount * 4; + mCoordinates[baseCoordinatesIndex + 0] = left; + mCoordinates[baseCoordinatesIndex + 1] = top; + mCoordinates[baseCoordinatesIndex + 2] = right; + mCoordinates[baseCoordinatesIndex + 3] = bottom; + mKeys[mCount] = key; + ++mCount; + return this; + } + private int mCount = 0; + private int[] mKeys = null; + private float[] mCoordinates = null; + private static int INITIAL_SIZE = 16; + + /** + * @return {@link SparseRectFArray} using parameters in this {@link SparseRectFArray}. + */ + public SparseRectFArray build() { + return new SparseRectFArray(this); + } + + public void reset() { + if (mCount == 0) { + mKeys = null; + mCoordinates = null; + } + mCount = 0; + } + } + + private SparseRectFArray(final SparseRectFArrayBuilder builder) { + if (builder.mCount == 0) { + mKeys = null; + mCoordinates = null; + } else { + mKeys = new int[builder.mCount]; + mCoordinates = new float[builder.mCount * 4]; + System.arraycopy(builder.mKeys, 0, mKeys, 0, builder.mCount); + System.arraycopy(builder.mCoordinates, 0, mCoordinates, 0, builder.mCount * 4); + } + } + + public RectF get(final int index) { + if (mKeys == null) { + return null; + } + if (index < 0) { + return null; + } + final int arrayIndex = Arrays.binarySearch(mKeys, index); + if (arrayIndex < 0) { + return null; + } + final int baseCoordIndex = arrayIndex * 4; + return new RectF(mCoordinates[baseCoordIndex], + mCoordinates[baseCoordIndex + 1], + mCoordinates[baseCoordIndex + 2], + mCoordinates[baseCoordIndex + 3]); + } + + /** + * Used to make this class parcelable. + */ + public static final Parcelable.Creator<SparseRectFArray> CREATOR = + new Parcelable.Creator<SparseRectFArray>() { + @Override + public SparseRectFArray createFromParcel(Parcel source) { + return new SparseRectFArray(source); + } + @Override + public SparseRectFArray[] newArray(int size) { + return new SparseRectFArray[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } +} + diff --git a/core/java/android/webkit/EventLogTags.logtags b/core/java/android/webkit/EventLogTags.logtags index b0b5493..a90aebd 100644 --- a/core/java/android/webkit/EventLogTags.logtags +++ b/core/java/android/webkit/EventLogTags.logtags @@ -8,3 +8,4 @@ option java_package android.webkit; # 70103- used by the browser app itself 70150 browser_snap_center +70151 exp_det_attempt_to_call_object_getclass (app_signature|3) diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java index 3e33498..fa760b7 100644 --- a/core/java/android/webkit/PermissionRequest.java +++ b/core/java/android/webkit/PermissionRequest.java @@ -28,6 +28,7 @@ import android.net.Uri; public interface PermissionRequest { /** * Resource belongs to geolocation service. + * @hide - see b/14668406 */ public final static long RESOURCE_GEOLOCATION = 1 << 0; /** diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 25bcd44..ac12357 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -50,28 +50,6 @@ public final class WebViewFactory { private static WebViewFactoryProvider sProviderInstance; private static final Object sProviderLock = new Object(); - public static boolean isExperimentalWebViewAvailable() { - // TODO: Remove callers of this method then remove it. - return false; // Hide the toggle in Developer Settings. - } - - /** @hide */ - public static void setUseExperimentalWebView(boolean enable) { - // TODO: Remove callers of this method then remove it. - } - - /** @hide */ - public static boolean useExperimentalWebView() { - // TODO: Remove callers of this method then remove it. - return true; - } - - /** @hide */ - public static boolean isUseExperimentalWebViewSet() { - // TODO: Remove callers of this method then remove it. - return false; // User has not modifed Developer Settings - } - static WebViewFactoryProvider getProvider() { synchronized (sProviderLock) { // For now the main purpose of this function (and the factory abstraction) is to keep diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index f4cd5fc..d9a4f57 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -743,7 +743,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * * @param view The view whose scroll state is being reported * - * @param scrollState The current scroll state. One of + * @param scrollState The current scroll state. One of * {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}. */ public void onScrollStateChanged(AbsListView view, int scrollState); @@ -3267,7 +3267,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } - private boolean startScrollIfNeeded(int y, MotionEvent vtev) { + private boolean startScrollIfNeeded(int x, int y, MotionEvent vtev) { // Check if we have moved far enough that it looks more like a // scroll than a tap final int deltaY = y - mMotionY; @@ -3296,27 +3296,31 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); } - scrollIfNeeded(y, vtev); + scrollIfNeeded(x, y, vtev); return true; } return false; } - private void scrollIfNeeded(int y, MotionEvent vtev) { + private void scrollIfNeeded(int x, int y, MotionEvent vtev) { int rawDeltaY = y - mMotionY; + int scrollOffsetCorrection = 0; + int scrollConsumedCorrection = 0; + if (mLastY == Integer.MIN_VALUE) { + rawDeltaY -= mMotionCorrection; + } if (dispatchNestedPreScroll(0, rawDeltaY, mScrollConsumed, mScrollOffset)) { rawDeltaY -= mScrollConsumed[1]; - mMotionCorrection -= mScrollOffset[1]; - if (mLastY != Integer.MIN_VALUE) { - mLastY -= mScrollOffset[1] + mScrollConsumed[1]; - } + scrollOffsetCorrection -= mScrollOffset[1]; + scrollConsumedCorrection -= mScrollConsumed[1]; if (vtev != null) { vtev.offsetLocation(0, mScrollOffset[1]); } } - final int deltaY = rawDeltaY - mMotionCorrection; - int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY; + final int deltaY = rawDeltaY; + int incrementalDeltaY = + mLastY != Integer.MIN_VALUE ? y - mLastY + scrollConsumedCorrection : deltaY; int lastYCorrection = 0; if (mTouchMode == TOUCH_MODE_SCROLL) { @@ -3378,46 +3382,51 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te (motionViewRealTop - motionViewPrevTop); if (dispatchNestedScroll(0, overscroll - incrementalDeltaY, 0, overscroll, mScrollOffset)) { - mMotionCorrection -= mScrollOffset[1]; lastYCorrection -= mScrollOffset[1]; if (vtev != null) { vtev.offsetLocation(0, mScrollOffset[1]); } } else { - overScrollBy(0, overscroll, 0, mScrollY, 0, 0, - 0, mOverscrollDistance, true); - if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) { - // Don't allow overfling if we're at the edge. - if (mVelocityTracker != null) { - mVelocityTracker.clear(); - } + final boolean atOverscrollEdge = overScrollBy(0, overscroll, + 0, mScrollY, 0, 0, 0, mOverscrollDistance, true); + + if (atOverscrollEdge && mVelocityTracker != null) { + // Don't allow overfling if we're at the edge + mVelocityTracker.clear(); } final int overscrollMode = getOverScrollMode(); if (overscrollMode == OVER_SCROLL_ALWAYS || (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && !contentFits())) { - mDirection = 0; // Reset when entering overscroll. - mTouchMode = TOUCH_MODE_OVERSCROLL; - if (deltaY > 0) { - mEdgeGlowTop.onPull((float) overscroll / getHeight()); + if (!atOverscrollEdge) { + mDirection = 0; // Reset when entering overscroll. + mTouchMode = TOUCH_MODE_OVERSCROLL; + } + if (incrementalDeltaY > 0) { + mEdgeGlowTop.onPull((float) overscroll / getHeight(), + (float) x / getWidth()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } - invalidate(mEdgeGlowTop.getBounds(false)); - } else if (deltaY < 0) { - mEdgeGlowBottom.onPull((float) overscroll / getHeight()); + invalidate(0, 0, getWidth(), + mEdgeGlowTop.getMaxHeight() + getPaddingTop()); + } else if (incrementalDeltaY < 0) { + mEdgeGlowBottom.onPull((float) overscroll / getHeight(), + 1.f - (float) x / getWidth()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); } - invalidate(mEdgeGlowBottom.getBounds(true)); + invalidate(0, getHeight() - getPaddingBottom() - + mEdgeGlowBottom.getMaxHeight(), getWidth(), + getHeight()); } } } } - mMotionY = y; + mMotionY = y + scrollOffsetCorrection; } - mLastY = y + lastYCorrection; + mLastY = y + lastYCorrection + scrollOffsetCorrection; } } else if (mTouchMode == TOUCH_MODE_OVERSCROLL) { if (y != mLastY) { @@ -3445,17 +3454,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && !contentFits())) { if (rawDeltaY > 0) { - mEdgeGlowTop.onPull((float) overScrollDistance / getHeight()); + mEdgeGlowTop.onPull((float) overScrollDistance / getHeight(), + (float) x / getWidth()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } - invalidate(mEdgeGlowTop.getBounds(false)); + invalidate(0, 0, getWidth(), + mEdgeGlowTop.getMaxHeight() + getPaddingTop()); } else if (rawDeltaY < 0) { - mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight()); + mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight(), + 1.f - (float) x / getWidth()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); } - invalidate(mEdgeGlowBottom.getBounds(true)); + invalidate(0, getHeight() - getPaddingBottom() - + mEdgeGlowBottom.getMaxHeight(), getWidth(), + getHeight()); } } } @@ -3703,7 +3717,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te case TOUCH_MODE_DONE_WAITING: // Check if we have moved far enough that it looks more like a // scroll than a tap. If so, we'll enter scrolling mode. - if (startScrollIfNeeded(y, vtev)) { + if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, vtev)) { break; } // Otherwise, check containment within list bounds. If we're @@ -3723,7 +3737,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te break; case TOUCH_MODE_SCROLL: case TOUCH_MODE_OVERSCROLL: - scrollIfNeeded(y, vtev); + scrollIfNeeded((int) ev.getX(pointerIndex), y, vtev); break; } } @@ -4022,8 +4036,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te canvas.translate(leftPadding, edgeY); mEdgeGlowTop.setSize(width, getHeight()); if (mEdgeGlowTop.draw(canvas)) { - mEdgeGlowTop.setPosition(leftPadding, edgeY); - invalidate(mEdgeGlowTop.getBounds(false)); + invalidate(0, 0, getWidth(), + mEdgeGlowTop.getMaxHeight() + getPaddingTop()); } canvas.restoreToCount(restoreCount); } @@ -4040,9 +4054,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te canvas.rotate(180, width, 0); mEdgeGlowBottom.setSize(width, height); if (mEdgeGlowBottom.draw(canvas)) { - // Account for the rotation - mEdgeGlowBottom.setPosition(edgeX + width, edgeY); - invalidate(mEdgeGlowBottom.getBounds(true)); + invalidate(0, getHeight() - getPaddingBottom() - + mEdgeGlowBottom.getMaxHeight(), getWidth(), + getHeight()); } canvas.restoreToCount(restoreCount); } @@ -4161,7 +4175,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final int y = (int) ev.getY(pointerIndex); initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); - if (startScrollIfNeeded(y, null)) { + if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) { return true; } break; diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 225cd6d..4f2d9c6 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -19,7 +19,9 @@ package android.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Insets; import android.graphics.Rect; +import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.util.AttributeSet; @@ -32,9 +34,12 @@ import android.view.accessibility.AccessibilityNodeInfo; import com.android.internal.R; public abstract class AbsSeekBar extends ProgressBar { + private final Rect mTempRect = new Rect(); + private Drawable mThumb; private int mThumbOffset; - + private boolean mSplitTrack; + /** * On touch, this offset plus the scaled value from the position of the * touch will form the progress value. Usually 0. @@ -51,10 +56,10 @@ public abstract class AbsSeekBar extends ProgressBar { * progress. */ private int mKeyProgressIncrement = 1; - + private static final int NO_ALPHA = 0xFF; private float mDisabledAlpha; - + private int mScaledTouchSlop; private float mTouchDownX; private boolean mIsDragging; @@ -76,12 +81,16 @@ public abstract class AbsSeekBar extends ProgressBar { TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.SeekBar, defStyleAttr, defStyleRes); - Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb); - setThumb(thumb); // will guess mThumbOffset if thumb != null... - // ...but allow layout to override this - int thumbOffset = a.getDimensionPixelOffset( + + final Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb); + setThumb(thumb); + + // Guess thumb offset if thumb != null, but allow layout to override. + final int thumbOffset = a.getDimensionPixelOffset( com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset()); setThumbOffset(thumbOffset); + + mSplitTrack = a.getBoolean(com.android.internal.R.styleable.SeekBar_splitTrack, false); a.recycle(); a = context.obtainStyledAttributes(attrs, @@ -97,7 +106,7 @@ public abstract class AbsSeekBar extends ProgressBar { * <p> * If the thumb is a valid drawable (i.e. not null), half its width will be * used as the new thumb offset (@see #setThumbOffset(int)). - * + * * @param thumb Drawable representing the thumb */ public void setThumb(Drawable thumb) { @@ -132,7 +141,7 @@ public abstract class AbsSeekBar extends ProgressBar { mThumb = thumb; invalidate(); if (needUpdate) { - updateThumbPos(getWidth(), getHeight()); + updateThumbAndTrackPos(getWidth(), getHeight()); if (thumb != null && thumb.isStateful()) { // Note that if the states are different this won't work. // For now, let's consider that an app bug. @@ -162,7 +171,7 @@ public abstract class AbsSeekBar extends ProgressBar { /** * Sets the thumb offset that allows the thumb to extend out of the range of * the track. - * + * * @param thumbOffset The offset amount in pixels. */ public void setThumbOffset(int thumbOffset) { @@ -171,8 +180,27 @@ public abstract class AbsSeekBar extends ProgressBar { } /** + * Specifies whether the track should be split by the thumb. When true, + * the thumb's optical bounds will be clipped out of the track drawable, + * then the thumb will be drawn into the resulting gap. + * + * @param splitTrack Whether the track should be split by the thumb + */ + public void setSplitTrack(boolean splitTrack) { + mSplitTrack = splitTrack; + invalidate(); + } + + /** + * Returns whether the track should be split by the thumb. + */ + public boolean getSplitTrack() { + return mSplitTrack; + } + + /** * Sets the amount of progress changed via the arrow keys. - * + * * @param increment The amount to increment or decrement when the user * presses the arrow keys. */ @@ -184,14 +212,14 @@ public abstract class AbsSeekBar extends ProgressBar { * Returns the amount of progress changed via the arrow keys. * <p> * By default, this will be a value that is derived from the max progress. - * + * * @return The amount to increment or decrement when the user presses the * arrow keys. This will be positive. */ public int getKeyProgressIncrement() { return mKeyProgressIncrement; } - + @Override public synchronized void setMax(int max) { super.setMax(max); @@ -217,79 +245,85 @@ public abstract class AbsSeekBar extends ProgressBar { @Override protected void drawableStateChanged() { super.drawableStateChanged(); - - Drawable progressDrawable = getProgressDrawable(); + + final Drawable progressDrawable = getProgressDrawable(); if (progressDrawable != null) { progressDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha)); } - - if (mThumb != null && mThumb.isStateful()) { - int[] state = getDrawableState(); - mThumb.setState(state); + + final Drawable thumb = mThumb; + if (thumb != null && thumb.isStateful()) { + thumb.setState(getDrawableState()); } } - + @Override void onProgressRefresh(float scale, boolean fromUser) { super.onProgressRefresh(scale, fromUser); - Drawable thumb = mThumb; + + final Drawable thumb = mThumb; if (thumb != null) { setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE); - /* - * Since we draw translated, the drawable's bounds that it signals - * for invalidation won't be the actual bounds we want invalidated, - * so just invalidate this whole view. - */ + + // Since we draw translated, the drawable's bounds that it signals + // for invalidation won't be the actual bounds we want invalidated, + // so just invalidate this whole view. invalidate(); } } - - + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); - updateThumbPos(w, h); + + updateThumbAndTrackPos(w, h); } - private void updateThumbPos(int w, int h) { - Drawable d = getCurrentDrawable(); - Drawable thumb = mThumb; - int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight(); + private void updateThumbAndTrackPos(int w, int h) { + final Drawable track = getCurrentDrawable(); + final Drawable thumb = mThumb; + // The max height does not incorporate padding, whereas the height - // parameter does - int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom); - - int max = getMax(); - float scale = max > 0 ? (float) getProgress() / (float) max : 0; - + // parameter does. + final int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom); + final int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight(); + + // Apply offset to whichever item is taller. + final int trackOffset; + final int thumbOffset; if (thumbHeight > trackHeight) { - if (thumb != null) { - setThumbPos(w, thumb, scale, 0); - } - int gapForCenteringTrack = (thumbHeight - trackHeight) / 2; - if (d != null) { - // Canvas will be translated by the padding, so 0,0 is where we start drawing - d.setBounds(0, gapForCenteringTrack, - w - mPaddingRight - mPaddingLeft, h - mPaddingBottom - gapForCenteringTrack - - mPaddingTop); - } + trackOffset = (thumbHeight - trackHeight) / 2; + thumbOffset = 0; } else { - if (d != null) { - // Canvas will be translated by the padding, so 0,0 is where we start drawing - d.setBounds(0, 0, w - mPaddingRight - mPaddingLeft, h - mPaddingBottom - - mPaddingTop); - } - int gap = (trackHeight - thumbHeight) / 2; - if (thumb != null) { - setThumbPos(w, thumb, scale, gap); - } + trackOffset = 0; + thumbOffset = (trackHeight - thumbHeight) / 2; + } + + if (track != null) { + track.setBounds(0, trackOffset, w - mPaddingRight - mPaddingLeft, + h - mPaddingBottom - trackOffset - mPaddingTop); + } + + if (thumb != null) { + setThumbPos(w, thumb, getScale(), thumbOffset); } } + private float getScale() { + final int max = getMax(); + return max > 0 ? getProgress() / (float) max : 0; + } + /** - * @param gap If set to {@link Integer#MIN_VALUE}, this will be ignored and + * Updates the thumb drawable bounds. + * + * @param w Width of the view, including padding + * @param thumb Drawable used for the thumb + * @param scale Current progress between 0 and 1 + * @param offset Vertical offset for centering. If set to + * {@link Integer#MIN_VALUE}, the current offset will be used. */ - private void setThumbPos(int w, Drawable thumb, float scale, int gap) { + private void setThumbPos(int w, Drawable thumb, float scale, int offset) { int available = w - mPaddingLeft - mPaddingRight; final int thumbWidth = thumb.getIntrinsicWidth(); final int thumbHeight = thumb.getIntrinsicHeight(); @@ -301,13 +335,13 @@ public abstract class AbsSeekBar extends ProgressBar { final int thumbPos = (int) (scale * available + 0.5f); final int top, bottom; - if (gap == Integer.MIN_VALUE) { + if (offset == Integer.MIN_VALUE) { final Rect oldBounds = thumb.getBounds(); top = oldBounds.top; bottom = oldBounds.bottom; } else { - top = gap; - bottom = gap + thumbHeight; + top = offset; + bottom = offset + thumbHeight; } final int left = (isLayoutRtl() && mMirrorForRtl) ? available - thumbPos : thumbPos; @@ -342,6 +376,33 @@ public abstract class AbsSeekBar extends ProgressBar { protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); + drawThumb(canvas); + } + + @Override + void drawTrack(Canvas canvas) { + final Drawable thumbDrawable = mThumb; + if (thumbDrawable != null && mSplitTrack) { + final Insets insets = thumbDrawable.getOpticalInsets(); + final Rect tempRect = mTempRect; + thumbDrawable.copyBounds(tempRect); + tempRect.offset(mPaddingLeft - mThumbOffset, mPaddingTop); + tempRect.left += insets.left; + tempRect.right -= insets.right; + + final int saveCount = canvas.save(); + canvas.clipRect(tempRect, Op.DIFFERENCE); + super.drawTrack(canvas); + canvas.restoreToCount(saveCount); + } else { + super.drawTrack(canvas); + } + } + + /** + * Draw the thumb. + */ + void drawThumb(Canvas canvas) { if (mThumb != null) { canvas.save(); // Translate the padding. For the x, we need to allow the thumb to @@ -366,17 +427,17 @@ public abstract class AbsSeekBar extends ProgressBar { } dw += mPaddingLeft + mPaddingRight; dh += mPaddingTop + mPaddingBottom; - + setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0), resolveSizeAndState(dh, heightMeasureSpec, 0)); } - + @Override public boolean onTouchEvent(MotionEvent event) { if (!mIsUserSeekable || !isEnabled()) { return false; } - + switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (isInScrollingContainer()) { @@ -391,7 +452,7 @@ public abstract class AbsSeekBar extends ProgressBar { attemptClaimDrag(); } break; - + case MotionEvent.ACTION_MOVE: if (mIsDragging) { trackTouchEvent(event); @@ -408,7 +469,7 @@ public abstract class AbsSeekBar extends ProgressBar { } } break; - + case MotionEvent.ACTION_UP: if (mIsDragging) { trackTouchEvent(event); @@ -426,7 +487,7 @@ public abstract class AbsSeekBar extends ProgressBar { // value has not apparently changed) invalidate(); break; - + case MotionEvent.ACTION_CANCEL: if (mIsDragging) { onStopTrackingTouch(); @@ -493,7 +554,7 @@ public abstract class AbsSeekBar extends ProgressBar { mParent.requestDisallowInterceptTouchEvent(true); } } - + /** * This is called when the user has started touching this widget. */ @@ -526,7 +587,7 @@ public abstract class AbsSeekBar extends ProgressBar { setProgress(progress - mKeyProgressIncrement, true); onKeyChange(); return true; - + case KeyEvent.KEYCODE_DPAD_RIGHT: if (progress >= getMax()) break; setProgress(progress + mKeyProgressIncrement, true); @@ -595,17 +656,13 @@ public abstract class AbsSeekBar extends ProgressBar { public void onRtlPropertiesChanged(int layoutDirection) { super.onRtlPropertiesChanged(layoutDirection); - int max = getMax(); - float scale = max > 0 ? (float) getProgress() / (float) max : 0; - - Drawable thumb = mThumb; + final Drawable thumb = mThumb; if (thumb != null) { - setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE); - /* - * Since we draw translated, the drawable's bounds that it signals - * for invalidation won't be the actual bounds we want invalidated, - * so just invalidate this whole view. - */ + setThumbPos(getWidth(), thumb, getScale(), Integer.MIN_VALUE); + + // Since we draw translated, the drawable's bounds that it signals + // for invalidation won't be the actual bounds we want invalidated, + // so just invalidate this whole view. invalidate(); } } diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index fa37443..83fbe8f 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -16,7 +16,14 @@ package android.widget; +import android.content.res.TypedArray; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Xfermode; +import android.util.Log; import com.android.internal.R; import android.content.Context; @@ -59,12 +66,10 @@ public class EdgeEffect { private static final int PULL_DECAY_TIME = 1000; private static final float MAX_ALPHA = 1.f; - private static final float HELD_EDGE_SCALE_Y = 0.5f; - private static final float MAX_GLOW_HEIGHT = 4.f; + private static final float MAX_GLOW_HEIGHT = 1.5f; - private static final float PULL_GLOW_BEGIN = 1.f; - private static final float PULL_EDGE_BEGIN = 0.6f; + private static final float PULL_GLOW_BEGIN = 0.f; // Minimum velocity that will be absorbed private static final int MIN_VELOCITY = 100; @@ -73,24 +78,11 @@ public class EdgeEffect { private static final float EPSILON = 0.001f; - private final Drawable mEdge; - private final Drawable mGlow; - private int mWidth; - private int mHeight; - private int mX; - private int mY; - private static final int MIN_WIDTH = 300; - private final int mMinWidth; - - private float mEdgeAlpha; - private float mEdgeScaleY; + private static final float SIN_45 = (float) Math.sin(Math.PI / 4); + private float mGlowAlpha; private float mGlowScaleY; - private float mEdgeAlphaStart; - private float mEdgeAlphaFinish; - private float mEdgeScaleYStart; - private float mEdgeScaleYFinish; private float mGlowAlphaStart; private float mGlowAlphaFinish; private float mGlowScaleYStart; @@ -107,16 +99,11 @@ public class EdgeEffect { private static final int STATE_RECEDE = 3; private static final int STATE_PULL_DECAY = 4; - // How much dragging should effect the height of the edge image. - // Number determined by user testing. - private static final int PULL_DISTANCE_EDGE_FACTOR = 7; - // How much dragging should effect the height of the glow image. // Number determined by user testing. private static final int PULL_DISTANCE_GLOW_FACTOR = 7; private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 1.1f; - private static final int VELOCITY_EDGE_FACTOR = 8; private static final int VELOCITY_GLOW_FACTOR = 12; private int mState = STATE_IDLE; @@ -124,30 +111,26 @@ public class EdgeEffect { private float mPullDistance; private final Rect mBounds = new Rect(); - - private final int mEdgeHeight; - private final int mGlowHeight; - private final int mGlowWidth; - private final int mMaxEffectHeight; + private final RectF mArcRect = new RectF(); + private final Paint mPaint = new Paint(); + private float mRadius; + private float mDisplacement = 0.5f; + private float mTargetDisplacement = 0.5f; /** * Construct a new EdgeEffect with a theme appropriate for the provided context. * @param context Context used to provide theming and resource information for the EdgeEffect */ public EdgeEffect(Context context) { - final Resources res = context.getResources(); - mEdge = context.getDrawable(R.drawable.overscroll_edge); - mGlow = context.getDrawable(R.drawable.overscroll_glow); - - mEdgeHeight = mEdge.getIntrinsicHeight(); - mGlowHeight = mGlow.getIntrinsicHeight(); - mGlowWidth = mGlow.getIntrinsicWidth(); - - mMaxEffectHeight = (int) (Math.min( - mGlowHeight * MAX_GLOW_HEIGHT * mGlowHeight / mGlowWidth * 0.6f, - mGlowHeight * MAX_GLOW_HEIGHT) + 0.5f); - - mMinWidth = (int) (res.getDisplayMetrics().density * MIN_WIDTH + 0.5f); + mPaint.setAntiAlias(true); + final TypedArray a = context.obtainStyledAttributes( + com.android.internal.R.styleable.EdgeEffect); + final int themeColor = a.getColor( + com.android.internal.R.styleable.EdgeEffect_colorPrimaryLight, 0xff666666); + a.recycle(); + mPaint.setColor((themeColor & 0xffffff) | 0x66000000); + mPaint.setStyle(Paint.Style.FILL); + mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); mInterpolator = new DecelerateInterpolator(); } @@ -158,20 +141,12 @@ public class EdgeEffect { * @param height Effect height in pixels */ public void setSize(int width, int height) { - mWidth = width; - mHeight = height; - } + final float r = width * 0.5f / SIN_45; + final float y = SIN_45 * r; + final float h = r - y; + mRadius = r; - /** - * Set the position of this edge effect in pixels. This position is - * only used by {@link #getBounds(boolean)}. - * - * @param x The position of the edge effect on the X axis - * @param y The position of the edge effect on the Y axis - */ - void setPosition(int x, int y) { - mX = x; - mY = y; + mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h)); } /** @@ -199,17 +174,38 @@ public class EdgeEffect { * The host view should always {@link android.view.View#invalidate()} after this * and draw the results accordingly. * + * <p>Views using EdgeEffect should favor {@link #onPull(float, float)} when the displacement + * of the pull point is known.</p> + * * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to * 1.f (full length of the view) or negative values to express change * back toward the edge reached to initiate the effect. */ public void onPull(float deltaDistance) { + onPull(deltaDistance, 0.5f); + } + + /** + * A view should call this when content is pulled away from an edge by the user. + * This will update the state of the current visual effect and its associated animation. + * The host view should always {@link android.view.View#invalidate()} after this + * and draw the results accordingly. + * + * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to + * 1.f (full length of the view) or negative values to express change + * back toward the edge reached to initiate the effect. + * @param displacement The displacement from the starting side of the effect of the point + * initiating the pull. In the case of touch this is the finger position. + * Values may be from 0-1. + */ + public void onPull(float deltaDistance, float displacement) { final long now = AnimationUtils.currentAnimationTimeMillis(); + mTargetDisplacement = displacement; if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) { return; } if (mState != STATE_PULL) { - mGlowScaleY = PULL_GLOW_BEGIN; + mGlowScaleY = Math.max(PULL_GLOW_BEGIN, mGlowScaleY); } mState = STATE_PULL; @@ -217,15 +213,10 @@ public class EdgeEffect { mDuration = PULL_TIME; mPullDistance += deltaDistance; - float distance = Math.abs(mPullDistance); - - mEdgeAlpha = mEdgeAlphaStart = Math.max(PULL_EDGE_BEGIN, Math.min(distance, MAX_ALPHA)); - mEdgeScaleY = mEdgeScaleYStart = Math.max( - HELD_EDGE_SCALE_Y, Math.min(distance * PULL_DISTANCE_EDGE_FACTOR, 1.f)); mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA, mGlowAlpha + - (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR)); + (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR)); float glowChange = Math.abs(deltaDistance); if (deltaDistance > 0 && mPullDistance < 0) { @@ -239,8 +230,6 @@ public class EdgeEffect { mGlowScaleY = mGlowScaleYStart = Math.min(MAX_GLOW_HEIGHT, Math.max( 0, mGlowScaleY + glowChange * PULL_DISTANCE_GLOW_FACTOR)); - mEdgeAlphaFinish = mEdgeAlpha; - mEdgeScaleYFinish = mEdgeScaleY; mGlowAlphaFinish = mGlowAlpha; mGlowScaleYFinish = mGlowScaleY; } @@ -259,13 +248,9 @@ public class EdgeEffect { } mState = STATE_RECEDE; - mEdgeAlphaStart = mEdgeAlpha; - mEdgeScaleYStart = mEdgeScaleY; mGlowAlphaStart = mGlowAlpha; mGlowScaleYStart = mGlowScaleY; - mEdgeAlphaFinish = 0.f; - mEdgeScaleYFinish = 0.f; mGlowAlphaFinish = 0.f; mGlowScaleYFinish = 0.f; @@ -290,30 +275,21 @@ public class EdgeEffect { mStartTime = AnimationUtils.currentAnimationTimeMillis(); mDuration = 0.15f + (velocity * 0.02f); - // The edge should always be at least partially visible, regardless - // of velocity. - mEdgeAlphaStart = 0.f; - mEdgeScaleY = mEdgeScaleYStart = 0.f; // The glow depends more on the velocity, and therefore starts out // nearly invisible. mGlowAlphaStart = 0.3f; - mGlowScaleYStart = 0.f; + mGlowScaleYStart = Math.max(mGlowScaleY, 0.f); - // Factor the velocity by 8. Testing on device shows this works best to - // reflect the strength of the user's scrolling. - mEdgeAlphaFinish = Math.max(0, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1)); - // Edge should never get larger than the size of its asset. - mEdgeScaleYFinish = Math.max( - HELD_EDGE_SCALE_Y, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1.f)); // Growth for the size of the glow should be quadratic to properly // respond // to a user's scrolling speed. The faster the scrolling speed, the more // intense the effect should be for both the size and the saturation. - mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f); + mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2, 1.f); // Alpha should change for the glow as well as size. mGlowAlphaFinish = Math.max( mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA)); + mTargetDisplacement = 0.5f; } @@ -330,52 +306,42 @@ public class EdgeEffect { public boolean draw(Canvas canvas) { update(); - mGlow.setAlpha((int) (Math.max(0, Math.min(mGlowAlpha, 1)) * 255)); - - int glowBottom = (int) Math.min( - mGlowHeight * mGlowScaleY * mGlowHeight / mGlowWidth * 0.6f, - mGlowHeight * MAX_GLOW_HEIGHT); - if (mWidth < mMinWidth) { - // Center the glow and clip it. - int glowLeft = (mWidth - mMinWidth)/2; - mGlow.setBounds(glowLeft, 0, mWidth - glowLeft, glowBottom); - } else { - // Stretch the glow to fit. - mGlow.setBounds(0, 0, mWidth, glowBottom); - } + final int count = canvas.save(); - mGlow.draw(canvas); + final float y = mBounds.height(); + final float centerY = y - mRadius; + final float centerX = mBounds.centerX(); + mArcRect.set(centerX - mRadius, centerY - mRadius, centerX + mRadius, centerY + mRadius); + canvas.scale(1.f, Math.min(mGlowScaleY, 1.f), centerX, 0); - mEdge.setAlpha((int) (Math.max(0, Math.min(mEdgeAlpha, 1)) * 255)); - - int edgeBottom = (int) (mEdgeHeight * mEdgeScaleY); - if (mWidth < mMinWidth) { - // Center the edge and clip it. - int edgeLeft = (mWidth - mMinWidth)/2; - mEdge.setBounds(edgeLeft, 0, mWidth - edgeLeft, edgeBottom); - } else { - // Stretch the edge to fit. - mEdge.setBounds(0, 0, mWidth, edgeBottom); + final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f; + float translateX = mBounds.width() * displacement; + float translateY = 0; + if (mGlowScaleY > 1.f) { + translateY = (mGlowScaleY - 1.f) * mBounds.height(); } - mEdge.draw(canvas); - - if (mState == STATE_RECEDE && glowBottom == 0 && edgeBottom == 0) { + canvas.clipRect(Float.MIN_VALUE, mBounds.top, + Float.MAX_VALUE, Float.MAX_VALUE); + canvas.translate(translateX, translateY); + canvas.drawArc(mArcRect, 0, 180, true, mPaint); + canvas.restoreToCount(count); + + boolean oneLastFrame = false; + if (mState == STATE_RECEDE && mGlowScaleY == 0) { mState = STATE_IDLE; + oneLastFrame = true; } - return mState != STATE_IDLE; + return mState != STATE_IDLE || oneLastFrame; } /** - * Returns the bounds of the edge effect. - * - * @hide + * Return the maximum height that the edge effect will be drawn at given the original + * {@link #setSize(int, int) input size}. + * @return The maximum height of the edge effect */ - public Rect getBounds(boolean reverse) { - mBounds.set(0, 0, mWidth, mMaxEffectHeight); - mBounds.offset(mX, mY - (reverse ? mMaxEffectHeight : 0)); - - return mBounds; + public int getMaxHeight() { + return (int) (mBounds.height() * MAX_GLOW_HEIGHT + 0.5f); } private void update() { @@ -384,10 +350,9 @@ public class EdgeEffect { final float interp = mInterpolator.getInterpolation(t); - mEdgeAlpha = mEdgeAlphaStart + (mEdgeAlphaFinish - mEdgeAlphaStart) * interp; - mEdgeScaleY = mEdgeScaleYStart + (mEdgeScaleYFinish - mEdgeScaleYStart) * interp; mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp; mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp; + mDisplacement = (mDisplacement + mTargetDisplacement) / 2; if (t >= 1.f - EPSILON) { switch (mState) { @@ -396,14 +361,10 @@ public class EdgeEffect { mStartTime = AnimationUtils.currentAnimationTimeMillis(); mDuration = RECEDE_TIME; - mEdgeAlphaStart = mEdgeAlpha; - mEdgeScaleYStart = mEdgeScaleY; mGlowAlphaStart = mGlowAlpha; mGlowScaleYStart = mGlowScaleY; - // After absorb, the glow and edge should fade to nothing. - mEdgeAlphaFinish = 0.f; - mEdgeScaleYFinish = 0.f; + // After absorb, the glow should fade to nothing. mGlowAlphaFinish = 0.f; mGlowScaleYFinish = 0.f; break; @@ -412,26 +373,14 @@ public class EdgeEffect { mStartTime = AnimationUtils.currentAnimationTimeMillis(); mDuration = PULL_DECAY_TIME; - mEdgeAlphaStart = mEdgeAlpha; - mEdgeScaleYStart = mEdgeScaleY; mGlowAlphaStart = mGlowAlpha; mGlowScaleYStart = mGlowScaleY; - // After pull, the glow and edge should fade to nothing. - mEdgeAlphaFinish = 0.f; - mEdgeScaleYFinish = 0.f; + // After pull, the glow should fade to nothing. mGlowAlphaFinish = 0.f; mGlowScaleYFinish = 0.f; break; case STATE_PULL_DECAY: - // When receding, we want edge to decrease more slowly - // than the glow. - float factor = mGlowScaleYFinish != 0 ? 1 - / (mGlowScaleYFinish * mGlowScaleYFinish) - : Float.MAX_VALUE; - mEdgeScaleY = mEdgeScaleYStart + - (mEdgeScaleYFinish - mEdgeScaleYStart) * - interp * factor; mState = STATE_RECEDE; break; case STATE_RECEDE: diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 25d4f42..0c65c50 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -616,12 +616,14 @@ public class HorizontalScrollView extends FrameLayout { if (canOverscroll) { final int pulledToX = oldX + deltaX; if (pulledToX < 0) { - mEdgeGlowLeft.onPull((float) deltaX / getWidth()); + mEdgeGlowLeft.onPull((float) deltaX / getWidth(), + 1.f - ev.getY(activePointerIndex) / getHeight()); if (!mEdgeGlowRight.isFinished()) { mEdgeGlowRight.onRelease(); } } else if (pulledToX > range) { - mEdgeGlowRight.onPull((float) deltaX / getWidth()); + mEdgeGlowRight.onPull((float) deltaX / getWidth(), + ev.getY(activePointerIndex) / getHeight()); if (!mEdgeGlowLeft.isFinished()) { mEdgeGlowLeft.onRelease(); } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index f7e81b8..0c3715d 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -1066,21 +1066,30 @@ public class ProgressBar extends View { protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); - Drawable d = mCurrentDrawable; + drawTrack(canvas); + } + + /** + * Draws the progress bar track. + */ + void drawTrack(Canvas canvas) { + final Drawable d = mCurrentDrawable; if (d != null) { // Translate canvas so a indeterminate circular progress bar with padding // rotates properly in its animation - canvas.save(); + final int saveCount = canvas.save(); + if(isLayoutRtl() && mMirrorForRtl) { canvas.translate(getWidth() - mPaddingRight, mPaddingTop); canvas.scale(-1.0f, 1.0f); } else { canvas.translate(mPaddingLeft, mPaddingTop); } - long time = getDrawingTime(); + + final long time = getDrawingTime(); if (mHasAnimation) { mAnimation.getTransformation(time, mTransformation); - float scale = mTransformation.getAlpha(); + final float scale = mTransformation.getAlpha(); try { mInDrawing = true; d.setLevel((int) (scale * MAX_LEVEL)); @@ -1089,8 +1098,10 @@ public class ProgressBar extends View { } postInvalidateOnAnimation(); } + d.draw(canvas); - canvas.restore(); + canvas.restoreToCount(saveCount); + if (mShouldStartAnimationDrawable && d instanceof Animatable) { ((Animatable) d).start(); mShouldStartAnimationDrawable = false; diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 0fa75a6..fd04890 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -669,12 +669,14 @@ public class ScrollView extends FrameLayout { } else if (canOverscroll) { final int pulledToY = oldY + deltaY; if (pulledToY < 0) { - mEdgeGlowTop.onPull((float) deltaY / getHeight()); + mEdgeGlowTop.onPull((float) deltaY / getHeight(), + ev.getX(activePointerIndex) / getWidth()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } } else if (pulledToY > range) { - mEdgeGlowBottom.onPull((float) deltaY / getHeight()); + mEdgeGlowBottom.onPull((float) deltaY / getHeight(), + 1.f - ev.getX(activePointerIndex) / getWidth()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); } diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index 08af4de..438e164 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -22,9 +22,11 @@ import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Insets; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; +import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.text.Layout; import android.text.StaticLayout; @@ -85,6 +87,7 @@ public class Switch extends CompoundButton { private int mThumbTextPadding; private int mSwitchMinWidth; private int mSwitchPadding; + private boolean mSplitTrack; private CharSequence mTextOn; private CharSequence mTextOff; @@ -174,13 +177,13 @@ public class Switch extends CompoundButton { super(context, attrs, defStyleAttr, defStyleRes); mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - Resources res = getResources(); + + final Resources res = getResources(); mTextPaint.density = res.getDisplayMetrics().density; mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale); final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.Switch, defStyleAttr, defStyleRes); - mThumbDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_thumb); mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track); mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn); @@ -191,15 +194,16 @@ public class Switch extends CompoundButton { com.android.internal.R.styleable.Switch_switchMinWidth, 0); mSwitchPadding = a.getDimensionPixelSize( com.android.internal.R.styleable.Switch_switchPadding, 0); + mSplitTrack = a.getBoolean(com.android.internal.R.styleable.Switch_splitTrack, false); - int appearance = a.getResourceId( + final int appearance = a.getResourceId( com.android.internal.R.styleable.Switch_switchTextAppearance, 0); if (appearance != 0) { setSwitchTextAppearance(context, appearance); } a.recycle(); - ViewConfiguration config = ViewConfiguration.get(context); + final ViewConfiguration config = ViewConfiguration.get(context); mTouchSlop = config.getScaledTouchSlop(); mMinFlingVelocity = config.getScaledMinimumFlingVelocity(); @@ -469,6 +473,29 @@ public class Switch extends CompoundButton { } /** + * Specifies whether the track should be split by the thumb. When true, + * the thumb's optical bounds will be clipped out of the track drawable, + * then the thumb will be drawn into the resulting gap. + * + * @param splitTrack Whether the track should be split by the thumb + * + * @attr ref android.R.styleable#Switch_splitTrack + */ + public void setSplitTrack(boolean splitTrack) { + mSplitTrack = splitTrack; + invalidate(); + } + + /** + * Returns whether the track should be split by the thumb. + * + * @attr ref android.R.styleable#Switch_splitTrack + */ + public boolean getSplitTrack() { + return mSplitTrack; + } + + /** * Returns the text displayed when the button is in the checked state. * * @attr ref android.R.styleable#Switch_textOn @@ -518,13 +545,15 @@ public class Switch extends CompoundButton { mTrackDrawable.getPadding(mTempRect); - final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth()); + final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth()) + + mThumbTextPadding * 2; + mThumbWidth = Math.max(maxTextWidth, mThumbDrawable.getIntrinsicWidth()); + final int switchWidth = Math.max(mSwitchMinWidth, - maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right); + 2 * mThumbWidth + mTempRect.left + mTempRect.right); final int switchHeight = Math.max(mTrackDrawable.getIntrinsicHeight(), mThumbDrawable.getIntrinsicHeight()); - mThumbWidth = maxTextWidth + mThumbTextPadding * 2; mSwitchWidth = switchWidth; mSwitchHeight = switchHeight; @@ -777,7 +806,7 @@ public class Switch extends CompoundButton { final Drawable trackDrawable = mTrackDrawable; final Drawable thumbDrawable = mThumbDrawable; - // Draw the switch + // Layout the track. final int switchLeft = mSwitchLeft; final int switchTop = mSwitchTop; final int switchRight = mSwitchRight; @@ -793,9 +822,10 @@ public class Switch extends CompoundButton { // Relies on mTempRect, MUST be called first! final int thumbPos = getThumbOffset(); + // Layout the thumb. thumbDrawable.getPadding(tempRect); - int thumbLeft = switchInnerLeft - tempRect.left + thumbPos; - int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right; + final int thumbLeft = switchInnerLeft - tempRect.left + thumbPos; + final int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right; thumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom); final Drawable background = getBackground(); @@ -805,20 +835,32 @@ public class Switch extends CompoundButton { super.onDraw(canvas); - trackDrawable.draw(canvas); + if (mSplitTrack) { + final Insets insets = thumbDrawable.getOpticalInsets(); + thumbDrawable.copyBounds(tempRect); + tempRect.left += insets.left; + tempRect.right -= insets.right; + + final int saveCount = canvas.save(); + canvas.clipRect(tempRect, Op.DIFFERENCE); + trackDrawable.draw(canvas); + canvas.restoreToCount(saveCount); + } else { + trackDrawable.draw(canvas); + } final int saveCount = canvas.save(); canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom); thumbDrawable.draw(canvas); - final int drawableState[] = getDrawableState(); - if (mTextColors != null) { - mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0)); - } - mTextPaint.drawableState = drawableState; - final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout; if (switchText != null) { + final int drawableState[] = getDrawableState(); + if (mTextColors != null) { + mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0)); + } + mTextPaint.drawableState = drawableState; + final int left = (thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2; final int top = (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2; canvas.translate(left, top); @@ -889,12 +931,16 @@ public class Switch extends CompoundButton { protected void drawableStateChanged() { super.drawableStateChanged(); - int[] myDrawableState = getDrawableState(); + final int[] myDrawableState = getDrawableState(); + + if (mThumbDrawable != null && mThumbDrawable.setState(myDrawableState)) { + // Handle changes to thumb width and height. + requestLayout(); + } - // Set the state of the Drawable - // Drawable may be null when checked state is set from XML, from super constructor - if (mThumbDrawable != null) mThumbDrawable.setState(myDrawableState); - if (mTrackDrawable != null) mTrackDrawable.setState(myDrawableState); + if (mTrackDrawable != null) { + mTrackDrawable.setState(myDrawableState); + } invalidate(); } diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index 4726da7..b568121 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -26,6 +26,7 @@ import android.content.DialogInterface; import android.content.res.TypedArray; import android.database.Cursor; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Handler; import android.os.Message; import android.text.TextUtils; @@ -38,6 +39,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.Window; +import android.view.WindowInsets; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; @@ -240,6 +242,7 @@ public class AlertController { mWindow.requestFeature(Window.FEATURE_NO_TITLE); mWindow.setContentView(mAlertDialogLayout); setupView(); + setupDecor(); } public void setTitle(CharSequence title) { @@ -415,7 +418,28 @@ public class AlertController { public boolean onKeyUp(int keyCode, KeyEvent event) { return mScrollView != null && mScrollView.executeKeyEvent(event); } - + + private void setupDecor() { + final View decor = mWindow.getDecorView(); + final View parent = mWindow.findViewById(R.id.parentPanel); + if (parent != null && decor != null) { + decor.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { + @Override + public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) { + if (insets.isRound()) { + // TODO: Get the padding as a function of the window size. + int roundOffset = mContext.getResources().getDimensionPixelOffset( + R.dimen.alert_dialog_round_padding); + parent.setPadding(roundOffset, roundOffset, roundOffset, roundOffset); + } + return insets.consumeSystemWindowInsets(); + } + }); + decor.setFitsSystemWindows(true); + decor.requestApplyInsets(); + } + } + private void setupView() { LinearLayout contentPanel = (LinearLayout) mWindow.findViewById(R.id.contentPanel); setupContent(contentPanel); @@ -636,14 +660,31 @@ public class AlertController { private void setBackground(TypedArray a, View topPanel, View contentPanel, View customPanel, View buttonPanel, boolean hasTitle, boolean hasCustomView, boolean hasButtons) { - final int topBright = a.getResourceId( - R.styleable.AlertDialog_topBright, R.drawable.popup_top_bright); - final int topDark = a.getResourceId( - R.styleable.AlertDialog_topDark, R.drawable.popup_top_dark); - final int centerBright = a.getResourceId( - R.styleable.AlertDialog_centerBright, R.drawable.popup_center_bright); - final int centerDark = a.getResourceId( - R.styleable.AlertDialog_centerDark, R.drawable.popup_center_dark); + int fullDark = 0; + int topDark = 0; + int centerDark = 0; + int bottomDark = 0; + int fullBright = 0; + int topBright = 0; + int centerBright = 0; + int bottomBright = 0; + int bottomMedium = 0; + if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.KITKAT) { + fullDark = R.drawable.popup_full_dark; + topDark = R.drawable.popup_top_dark; + centerDark = R.drawable.popup_center_dark; + bottomDark = R.drawable.popup_bottom_dark; + fullBright = R.drawable.popup_full_bright; + topBright = R.drawable.popup_top_bright; + centerBright = R.drawable.popup_center_bright; + bottomBright = R.drawable.popup_bottom_bright; + bottomMedium = R.drawable.popup_bottom_medium; + } + topBright = a.getResourceId(R.styleable.AlertDialog_topBright, topBright); + topDark = a.getResourceId(R.styleable.AlertDialog_topDark, topDark); + centerBright = a.getResourceId(R.styleable.AlertDialog_centerBright, centerBright); + centerDark = a.getResourceId(R.styleable.AlertDialog_centerDark, centerDark); + /* We now set the background of all of the sections of the alert. * First collect together each section that is being displayed along @@ -707,22 +748,17 @@ public class AlertController { if (lastView != null) { if (setView) { - final int bottomBright = a.getResourceId( - R.styleable.AlertDialog_bottomBright, R.drawable.popup_bottom_bright); - final int bottomMedium = a.getResourceId( - R.styleable.AlertDialog_bottomMedium, R.drawable.popup_bottom_medium); - final int bottomDark = a.getResourceId( - R.styleable.AlertDialog_bottomDark, R.drawable.popup_bottom_dark); + bottomBright = a.getResourceId(R.styleable.AlertDialog_bottomBright, bottomBright); + bottomMedium = a.getResourceId(R.styleable.AlertDialog_bottomMedium, bottomMedium); + bottomDark = a.getResourceId(R.styleable.AlertDialog_bottomDark, bottomDark); // ListViews will use the Bright background, but buttons use the // Medium background. lastView.setBackgroundResource( lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark); } else { - final int fullBright = a.getResourceId( - R.styleable.AlertDialog_fullBright, R.drawable.popup_full_bright); - final int fullDark = a.getResourceId( - R.styleable.AlertDialog_fullDark, R.drawable.popup_full_dark); + fullBright = a.getResourceId(R.styleable.AlertDialog_fullBright, fullBright); + fullDark = a.getResourceId(R.styleable.AlertDialog_fullDark, fullDark); lastView.setBackgroundResource(lastLight ? fullBright : fullDark); } diff --git a/core/java/com/android/internal/app/IUsageStats.aidl b/core/java/com/android/internal/app/IUsageStats.aidl index 1ea7409..7e7f0e1 100644 --- a/core/java/com/android/internal/app/IUsageStats.aidl +++ b/core/java/com/android/internal/app/IUsageStats.aidl @@ -16,13 +16,17 @@ package com.android.internal.app; +import android.app.UsageStats; import android.content.ComponentName; -import com.android.internal.os.PkgUsageStats; +import android.content.res.Configuration; +import android.os.ParcelableParcel; interface IUsageStats { void noteResumeComponent(in ComponentName componentName); void notePauseComponent(in ComponentName componentName); void noteLaunchTime(in ComponentName componentName, int millis); - PkgUsageStats getPkgUsageStats(in ComponentName componentName); - PkgUsageStats[] getAllPkgUsageStats(); + void noteStartConfig(in Configuration config); + UsageStats.PackageStats getPkgUsageStats(String callingPkg, in ComponentName componentName); + UsageStats.PackageStats[] getAllPkgUsageStats(String callingPkg); + ParcelableParcel getCurrentStats(String callingPkg); } diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java index 882bec9..41f3337 100644 --- a/core/java/com/android/internal/app/ProcessStats.java +++ b/core/java/com/android/internal/app/ProcessStats.java @@ -1108,13 +1108,6 @@ public final class ProcessStats implements Parcelable { mRuntime = runtime; } } - String webview = WebViewFactory.useExperimentalWebView() ? "chromeview" : "webview"; - if (!Objects.equals(webview, mWebView)) { - changed = true; - if (update) { - mWebView = webview; - } - } return changed; } diff --git a/core/java/com/android/internal/notification/DemoContactNotificationScorer.java b/core/java/com/android/internal/notification/DemoContactNotificationScorer.java deleted file mode 100644 index f484724..0000000 --- a/core/java/com/android/internal/notification/DemoContactNotificationScorer.java +++ /dev/null @@ -1,188 +0,0 @@ -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -package com.android.internal.notification; - -import android.app.Notification; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.provider.ContactsContract; -import android.provider.Settings; -import android.text.SpannableString; -import android.util.Slog; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * This NotificationScorer bumps up the priority of notifications that contain references to the - * display names of starred contacts. The references it picks up are spannable strings which, in - * their entirety, match the display name of some starred contact. The magnitude of the bump ranges - * from 0 to 15 (assuming NOTIFICATION_PRIORITY_MULTIPLIER = 10) depending on the initial score, and - * the mapping is defined by priorityBumpMap. In a production version of this scorer, a notification - * extra will be used to specify contact identifiers. - */ - -public class DemoContactNotificationScorer implements NotificationScorer { - private static final String TAG = "DemoContactNotificationScorer"; - private static final boolean DBG = false; - - protected static final boolean ENABLE_CONTACT_SCORER = true; - private static final String SETTING_ENABLE_SCORER = "contact_scorer_enabled"; - protected boolean mEnabled; - - // see NotificationManagerService - private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; - - private Context mContext; - - private static final List<String> RELEVANT_KEYS_LIST = Arrays.asList( - Notification.EXTRA_INFO_TEXT, Notification.EXTRA_TEXT, Notification.EXTRA_TEXT_LINES, - Notification.EXTRA_SUB_TEXT, Notification.EXTRA_TITLE - ); - - private static final String[] PROJECTION = new String[] { - ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME - }; - - private static final Uri CONTACTS_URI = ContactsContract.Contacts.CONTENT_URI; - - private static List<String> extractSpannedStrings(CharSequence charSequence) { - if (charSequence == null) return Collections.emptyList(); - if (!(charSequence instanceof SpannableString)) { - return Arrays.asList(charSequence.toString()); - } - SpannableString spannableString = (SpannableString)charSequence; - // get all spans - Object[] ssArr = spannableString.getSpans(0, spannableString.length(), Object.class); - // spanned string sequences - ArrayList<String> sss = new ArrayList<String>(); - for (Object spanObj : ssArr) { - try { - sss.add(spannableString.subSequence(spannableString.getSpanStart(spanObj), - spannableString.getSpanEnd(spanObj)).toString()); - } catch(StringIndexOutOfBoundsException e) { - Slog.e(TAG, "Bad indices when extracting spanned subsequence", e); - } - } - return sss; - }; - - private static String getQuestionMarksInParens(int n) { - StringBuilder sb = new StringBuilder("("); - for (int i = 0; i < n; i++) { - if (sb.length() > 1) sb.append(','); - sb.append('?'); - } - sb.append(")"); - return sb.toString(); - } - - private boolean hasStarredContact(Bundle extras) { - if (extras == null) return false; - ArrayList<String> qStrings = new ArrayList<String>(); - // build list to query against the database for display names. - for (String rk: RELEVANT_KEYS_LIST) { - if (extras.get(rk) == null) { - continue; - } else if (extras.get(rk) instanceof CharSequence) { - qStrings.addAll(extractSpannedStrings((CharSequence) extras.get(rk))); - } else if (extras.get(rk) instanceof CharSequence[]) { - // this is intended for Notification.EXTRA_TEXT_LINES - for (CharSequence line: (CharSequence[]) extras.get(rk)){ - qStrings.addAll(extractSpannedStrings(line)); - } - } else { - Slog.w(TAG, "Strange, the extra " + rk + " is of unexpected type."); - } - } - if (qStrings.isEmpty()) return false; - String[] qStringsArr = qStrings.toArray(new String[qStrings.size()]); - - String selection = ContactsContract.Contacts.DISPLAY_NAME + " IN " - + getQuestionMarksInParens(qStringsArr.length) + " AND " - + ContactsContract.Contacts.STARRED+" ='1'"; - - Cursor c = null; - try { - c = mContext.getContentResolver().query( - CONTACTS_URI, PROJECTION, selection, qStringsArr, null); - if (c != null) return c.getCount() > 0; - } catch(Throwable t) { - Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); - } finally { - if (c != null) { - c.close(); - } - } - return false; - } - - private final static int clamp(int x, int low, int high) { - return (x < low) ? low : ((x > high) ? high : x); - } - - private static int priorityBumpMap(int incomingScore) { - //assumption is that scale runs from [-2*pm, 2*pm] - int pm = NOTIFICATION_PRIORITY_MULTIPLIER; - int theScore = incomingScore; - // enforce input in range - theScore = clamp(theScore, -2 * pm, 2 * pm); - if (theScore != incomingScore) return incomingScore; - // map -20 -> -20 and -10 -> 5 (when pm = 10) - if (theScore <= -pm) { - theScore += 1.5 * (theScore + 2 * pm); - } else { - // map 0 -> 10, 10 -> 15, 20 -> 20; - theScore += 0.5 * (2 * pm - theScore); - } - if (DBG) Slog.v(TAG, "priorityBumpMap: score before: " + incomingScore - + ", score after " + theScore + "."); - return theScore; - } - - @Override - public void initialize(Context context) { - if (DBG) Slog.v(TAG, "Initializing " + getClass().getSimpleName() + "."); - mContext = context; - mEnabled = ENABLE_CONTACT_SCORER && 1 == Settings.Global.getInt( - mContext.getContentResolver(), SETTING_ENABLE_SCORER, 0); - } - - @Override - public int getScore(Notification notification, int score) { - if (notification == null || !mEnabled) { - if (DBG) Slog.w(TAG, "empty notification? scorer disabled?"); - return score; - } - boolean hasStarredPriority = hasStarredContact(notification.extras); - - if (DBG) { - if (hasStarredPriority) { - Slog.v(TAG, "Notification references starred contact. Promoted!"); - } else { - Slog.v(TAG, "Notification lacks any starred contact reference. Not promoted!"); - } - } - if (hasStarredPriority) score = priorityBumpMap(score); - return score; - } -} - diff --git a/core/java/com/android/internal/notification/NotificationScorer.java b/core/java/com/android/internal/notification/NotificationScorer.java deleted file mode 100644 index 863c08c..0000000 --- a/core/java/com/android/internal/notification/NotificationScorer.java +++ /dev/null @@ -1,27 +0,0 @@ -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -package com.android.internal.notification; - -import android.app.Notification; -import android.content.Context; - -public interface NotificationScorer { - - public void initialize(Context context); - public int getScore(Notification notification, int score); - -} diff --git a/core/java/com/android/internal/notification/PeopleNotificationScorer.java b/core/java/com/android/internal/notification/PeopleNotificationScorer.java deleted file mode 100644 index efb5f63..0000000 --- a/core/java/com/android/internal/notification/PeopleNotificationScorer.java +++ /dev/null @@ -1,227 +0,0 @@ -/* -* Copyright (C) 2014 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -package com.android.internal.notification; - -import android.app.Notification; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.provider.ContactsContract; -import android.provider.ContactsContract.Contacts; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.LruCache; -import android.util.Slog; - -/** - * This {@link NotificationScorer} attempts to validate people references. - * Also elevates the priority of real people. - */ -public class PeopleNotificationScorer implements NotificationScorer { - private static final String TAG = "PeopleNotificationScorer"; - private static final boolean DBG = false; - - private static final boolean ENABLE_PEOPLE_SCORER = true; - private static final String SETTING_ENABLE_PEOPLE_SCORER = "people_scorer_enabled"; - private static final String[] LOOKUP_PROJECTION = { Contacts._ID }; - private static final int MAX_PEOPLE = 10; - private static final int PEOPLE_CACHE_SIZE = 200; - // see NotificationManagerService - private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; - - protected boolean mEnabled; - private Context mContext; - - // maps raw person handle to resolved person object - private LruCache<String, LookupResult> mPeopleCache; - - private float findMaxContactScore(Bundle extras) { - if (extras == null) { - return 0f; - } - - final String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE); - if (people == null || people.length == 0) { - return 0f; - } - - float rank = 0f; - for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) { - final String handle = people[personIdx]; - if (TextUtils.isEmpty(handle)) continue; - - LookupResult lookupResult = mPeopleCache.get(handle); - if (lookupResult == null || lookupResult.isExpired()) { - final Uri uri = Uri.parse(handle); - if ("tel".equals(uri.getScheme())) { - if (DBG) Slog.w(TAG, "checking telephone URI: " + handle); - lookupResult = lookupPhoneContact(handle, uri.getSchemeSpecificPart()); - } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) { - if (DBG) Slog.w(TAG, "checking lookup URI: " + handle); - lookupResult = resolveContactsUri(handle, uri); - } else { - if (DBG) Slog.w(TAG, "unsupported URI " + handle); - } - } else { - if (DBG) Slog.w(TAG, "using cached lookupResult: " + lookupResult.mId); - } - if (lookupResult != null) { - rank = Math.max(rank, lookupResult.getRank()); - } - } - return rank; - } - - private LookupResult lookupPhoneContact(final String handle, final String number) { - LookupResult lookupResult = null; - Cursor c = null; - try { - Uri numberUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, - Uri.encode(number)); - c = mContext.getContentResolver().query(numberUri, LOOKUP_PROJECTION, null, null, null); - if (c != null && c.getCount() > 0) { - c.moveToFirst(); - final int idIdx = c.getColumnIndex(Contacts._ID); - final int id = c.getInt(idIdx); - if (DBG) Slog.w(TAG, "is valid: " + id); - lookupResult = new LookupResult(id); - } - } catch(Throwable t) { - Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); - } finally { - if (c != null) { - c.close(); - } - } - if (lookupResult == null) { - lookupResult = new LookupResult(LookupResult.INVALID_ID); - } - mPeopleCache.put(handle, lookupResult); - return lookupResult; - } - - private LookupResult resolveContactsUri(String handle, final Uri personUri) { - LookupResult lookupResult = null; - Cursor c = null; - try { - c = mContext.getContentResolver().query(personUri, LOOKUP_PROJECTION, null, null, null); - if (c != null && c.getCount() > 0) { - c.moveToFirst(); - final int idIdx = c.getColumnIndex(Contacts._ID); - final int id = c.getInt(idIdx); - if (DBG) Slog.w(TAG, "is valid: " + id); - lookupResult = new LookupResult(id); - } - } catch(Throwable t) { - Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); - } finally { - if (c != null) { - c.close(); - } - } - if (lookupResult == null) { - lookupResult = new LookupResult(LookupResult.INVALID_ID); - } - mPeopleCache.put(handle, lookupResult); - return lookupResult; - } - - private final static int clamp(int x, int low, int high) { - return (x < low) ? low : ((x > high) ? high : x); - } - - // TODO: rework this function before shipping - private static int priorityBumpMap(int incomingScore) { - //assumption is that scale runs from [-2*pm, 2*pm] - int pm = NOTIFICATION_PRIORITY_MULTIPLIER; - int theScore = incomingScore; - // enforce input in range - theScore = clamp(theScore, -2 * pm, 2 * pm); - if (theScore != incomingScore) return incomingScore; - // map -20 -> -20 and -10 -> 5 (when pm = 10) - if (theScore <= -pm) { - theScore += 1.5 * (theScore + 2 * pm); - } else { - // map 0 -> 10, 10 -> 15, 20 -> 20; - theScore += 0.5 * (2 * pm - theScore); - } - if (DBG) Slog.v(TAG, "priorityBumpMap: score before: " + incomingScore - + ", score after " + theScore + "."); - return theScore; - } - - @Override - public void initialize(Context context) { - if (DBG) Slog.v(TAG, "Initializing " + getClass().getSimpleName() + "."); - mContext = context; - mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE); - mEnabled = ENABLE_PEOPLE_SCORER && 1 == Settings.Global.getInt( - mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_SCORER, 0); - } - - @Override - public int getScore(Notification notification, int score) { - if (notification == null || !mEnabled) { - if (DBG) Slog.w(TAG, "empty notification? scorer disabled?"); - return score; - } - float contactScore = findMaxContactScore(notification.extras); - if (contactScore > 0f) { - if (DBG) Slog.v(TAG, "Notification references a real contact. Promoted!"); - score = priorityBumpMap(score); - } else { - if (DBG) Slog.v(TAG, "Notification lacks any valid contact reference. Not promoted!"); - } - return score; - } - - private static class LookupResult { - private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr - public static final int INVALID_ID = -1; - - private final long mExpireMillis; - private int mId; - - public LookupResult(int id) { - mId = id; - mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS; - } - - public boolean isExpired() { - return mExpireMillis < System.currentTimeMillis(); - } - - public boolean isInvalid() { - return mId == INVALID_ID || isExpired(); - } - - public float getRank() { - if (isInvalid()) { - return 0f; - } else { - return 1f; // TODO: finer grained score - } - } - - public LookupResult setId(int id) { - mId = id; - return this; - } - } -} - diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 1aff190..7bd5b12 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -188,8 +188,7 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mShuttingDown; - HashMap<String, SparseBooleanArray>[] mActiveEvents - = (HashMap<String, SparseBooleanArray>[]) new HashMap[HistoryItem.EVENT_COUNT]; + final HistoryEventTracker mActiveEvents = new HistoryEventTracker(); long mHistoryBaseTime; boolean mHaveBatteryLevel = false; @@ -2297,44 +2296,8 @@ public final class BatteryStatsImpl extends BatteryStats { public void noteEventLocked(int code, String name, int uid) { uid = mapUid(uid); - if ((code&HistoryItem.EVENT_FLAG_START) != 0) { - int idx = code&~HistoryItem.EVENT_FLAG_START; - HashMap<String, SparseBooleanArray> active = mActiveEvents[idx]; - if (active == null) { - active = new HashMap<String, SparseBooleanArray>(); - mActiveEvents[idx] = active; - } - SparseBooleanArray uids = active.get(name); - if (uids == null) { - uids = new SparseBooleanArray(); - active.put(name, uids); - } - if (uids.get(uid)) { - // Already set, nothing to do! - return; - } - uids.put(uid, true); - } else if ((code&HistoryItem.EVENT_FLAG_FINISH) != 0) { - int idx = code&~HistoryItem.EVENT_FLAG_FINISH; - HashMap<String, SparseBooleanArray> active = mActiveEvents[idx]; - if (active == null) { - // not currently active, nothing to do. - return; - } - SparseBooleanArray uids = active.get(name); - if (uids == null) { - // not currently active, nothing to do. - return; - } - idx = uids.indexOfKey(uid); - if (idx < 0 || !uids.valueAt(idx)) { - // not currently active, nothing to do. - return; - } - uids.removeAt(idx); - if (uids.size() <= 0) { - active.remove(name); - } + if (!mActiveEvents.updateState(code, name, uid, 0)) { + return; } final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); @@ -2348,6 +2311,9 @@ public final class BatteryStatsImpl extends BatteryStats { } } + private String mInitialAcquireWakeName; + private int mInitialAcquireWakeUid = -1; + public void noteStartWakeLocked(int uid, int pid, String name, String historyName, int type, boolean unimportantForLogging, long elapsedRealtime, long uptime) { uid = mapUid(uid); @@ -2360,8 +2326,9 @@ public final class BatteryStatsImpl extends BatteryStats { if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: " + Integer.toHexString(mHistoryCur.states)); mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = historyName != null ? historyName : name; - mHistoryCur.wakelockTag.uid = uid; + mHistoryCur.wakelockTag.string = mInitialAcquireWakeName + = historyName != null ? historyName : name; + mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid; mWakeLockImportant = !unimportantForLogging; addHistoryRecordLocked(elapsedRealtime, uptime); } else if (!mWakeLockImportant && !unimportantForLogging) { @@ -2369,8 +2336,9 @@ public final class BatteryStatsImpl extends BatteryStats { // We'll try to update the last tag. mHistoryLastWritten.wakelockTag = null; mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = historyName != null ? historyName : name; - mHistoryCur.wakelockTag.uid = uid; + mHistoryCur.wakelockTag.string = mInitialAcquireWakeName + = historyName != null ? historyName : name; + mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid; addHistoryRecordLocked(elapsedRealtime, uptime); } mWakeLockImportant = true; @@ -2395,6 +2363,14 @@ public final class BatteryStatsImpl extends BatteryStats { mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: " + Integer.toHexString(mHistoryCur.states)); + if ((name != null && !name.equals(mInitialAcquireWakeName)) + || uid != mInitialAcquireWakeUid) { + mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; + mHistoryCur.wakelockTag.string = name; + mHistoryCur.wakelockTag.uid = uid; + } + mInitialAcquireWakeName = null; + mInitialAcquireWakeUid = -1; addHistoryRecordLocked(elapsedRealtime, uptime); } } @@ -5699,7 +5675,8 @@ public final class BatteryStatsImpl extends BatteryStats { final long lastRealtime = out.time; final long lastWalltime = out.currentTime; readHistoryDelta(mHistoryBuffer, out); - if (out.cmd != HistoryItem.CMD_CURRENT_TIME && lastWalltime != 0) { + if (out.cmd != HistoryItem.CMD_CURRENT_TIME + && out.cmd != HistoryItem.CMD_RESET && lastWalltime != 0) { out.currentTime = lastWalltime + (out.time - lastRealtime); } return true; @@ -5845,17 +5822,15 @@ public final class BatteryStatsImpl extends BatteryStats { private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) { for (int i=0; i<HistoryItem.EVENT_COUNT; i++) { - HashMap<String, SparseBooleanArray> active = mActiveEvents[i]; + HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(i); if (active == null) { continue; } - for (HashMap.Entry<String, SparseBooleanArray> ent : active.entrySet()) { - SparseBooleanArray uids = ent.getValue(); + for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) { + SparseIntArray uids = ent.getValue(); for (int j=0; j<uids.size(); j++) { - if (uids.valueAt(j)) { - addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(), - uids.keyAt(j)); - } + addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(), + uids.keyAt(j)); } } } @@ -5971,7 +5946,8 @@ public final class BatteryStatsImpl extends BatteryStats { boolean reset) { mRecordingHistory = true; mHistoryCur.currentTime = System.currentTimeMillis(); - addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME, + addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, + reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME, mHistoryCur); mHistoryCur.currentTime = 0; if (reset) { diff --git a/core/java/com/android/internal/os/PkgUsageStats.java b/core/java/com/android/internal/os/PkgUsageStats.java deleted file mode 100644 index 8c2c405..0000000 --- a/core/java/com/android/internal/os/PkgUsageStats.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.os; - -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.HashMap; -import java.util.Map; - -/** - * implementation of PkgUsageStats associated with an - * application package. - * @hide - */ -public class PkgUsageStats implements Parcelable { - public String packageName; - public int launchCount; - public long usageTime; - public Map<String, Long> componentResumeTimes; - - public static final Parcelable.Creator<PkgUsageStats> CREATOR - = new Parcelable.Creator<PkgUsageStats>() { - public PkgUsageStats createFromParcel(Parcel in) { - return new PkgUsageStats(in); - } - - public PkgUsageStats[] newArray(int size) { - return new PkgUsageStats[size]; - } - }; - - public String toString() { - return "PkgUsageStats{" - + Integer.toHexString(System.identityHashCode(this)) - + " " + packageName + "}"; - } - - public PkgUsageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) { - packageName = pkgName; - launchCount = count; - usageTime = time; - componentResumeTimes = new HashMap<String, Long>(lastResumeTimes); - } - - public PkgUsageStats(Parcel source) { - packageName = source.readString(); - launchCount = source.readInt(); - usageTime = source.readLong(); - final int N = source.readInt(); - componentResumeTimes = new HashMap<String, Long>(N); - for (int i = 0; i < N; i++) { - String component = source.readString(); - long lastResumeTime = source.readLong(); - componentResumeTimes.put(component, lastResumeTime); - } - } - - public PkgUsageStats(PkgUsageStats pStats) { - packageName = pStats.packageName; - launchCount = pStats.launchCount; - usageTime = pStats.usageTime; - componentResumeTimes = new HashMap<String, Long>(pStats.componentResumeTimes); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel dest, int parcelableFlags) { - dest.writeString(packageName); - dest.writeInt(launchCount); - dest.writeLong(usageTime); - dest.writeInt(componentResumeTimes.size()); - for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) { - dest.writeString(ent.getKey()); - dest.writeLong(ent.getValue()); - } - } -} diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java index 52281d9..34f62ba 100644 --- a/core/java/com/android/internal/util/AsyncChannel.java +++ b/core/java/com/android/internal/util/AsyncChannel.java @@ -450,6 +450,7 @@ public class AsyncChannel { public void disconnect() { if ((mConnection != null) && (mSrcContext != null)) { mSrcContext.unbindService(mConnection); + mConnection = null; } try { // Send the DISCONNECTED, although it may not be received @@ -463,10 +464,12 @@ public class AsyncChannel { // Tell source we're disconnected. if (mSrcHandler != null) { replyDisconnected(STATUS_SUCCESSFUL); + mSrcHandler = null; } // Unlink only when bindService isn't used if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) { mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0); + mDeathMonitor = null; } } diff --git a/core/java/com/android/internal/util/VirtualRefBasePtr.java b/core/java/com/android/internal/util/VirtualRefBasePtr.java new file mode 100644 index 0000000..0bd4d3a --- /dev/null +++ b/core/java/com/android/internal/util/VirtualRefBasePtr.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +/** + * Helper class that contains a strong reference to a VirtualRefBase native + * object. This will incStrong in the ctor, and decStrong in the finalizer + */ +public final class VirtualRefBasePtr { + private long mNativePtr; + + public VirtualRefBasePtr(long ptr) { + mNativePtr = ptr; + nIncStrong(mNativePtr); + } + + public long get() { + return mNativePtr; + } + + @Override + protected void finalize() throws Throwable { + try { + nDecStrong(mNativePtr); + mNativePtr = 0; + } finally { + super.finalize(); + } + } + + private static native void nIncStrong(long ptr); + private static native void nDecStrong(long ptr); +} diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl index 90210ce..367b713 100644 --- a/core/java/com/android/internal/view/IInputMethodSession.aidl +++ b/core/java/com/android/internal/view/IInputMethodSession.aidl @@ -21,6 +21,7 @@ import android.os.Bundle; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.ExtractedText; /** @@ -47,4 +48,6 @@ oneway interface IInputMethodSession { void toggleSoftInput(int showFlags, int hideFlags); void finishSession(); + + void updateCursorAnchorInfo(in CursorAnchorInfo cursorAnchorInfo); } |
