summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/animation/TypeEvaluator.java2
-rw-r--r--core/java/android/app/ActionBar.java60
-rw-r--r--core/java/android/app/Activity.java47
-rw-r--r--core/java/android/app/ActivityManager.java190
-rw-r--r--core/java/android/app/ActivityManagerNative.java64
-rw-r--r--core/java/android/app/ActivityOptions.java38
-rw-r--r--core/java/android/app/ActivityThread.java34
-rw-r--r--core/java/android/app/ActivityTransitionCoordinator.java233
-rw-r--r--core/java/android/app/ActivityView.java24
-rw-r--r--core/java/android/app/ApplicationPackageManager.java25
-rw-r--r--core/java/android/app/ApplicationThreadNative.java18
-rw-r--r--core/java/android/app/ContextImpl.java8
-rw-r--r--core/java/android/app/Dialog.java5
-rw-r--r--core/java/android/app/EnterTransitionCoordinator.java13
-rw-r--r--core/java/android/app/IActivityManager.java14
-rw-r--r--core/java/android/app/IApplicationThread.java3
-rw-r--r--core/java/android/app/INotificationManager.aidl4
-rw-r--r--core/java/android/app/IProcessObserver.aidl2
-rw-r--r--core/java/android/app/IThumbnailReceiver.aidl30
-rw-r--r--core/java/android/app/WallpaperManager.java4
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java66
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl3
-rw-r--r--core/java/android/app/task/ITaskCallback.aidl53
-rw-r--r--core/java/android/app/task/ITaskService.aidl35
-rw-r--r--core/java/android/app/task/TaskParams.aidl19
-rw-r--r--core/java/android/app/task/TaskParams.java86
-rw-r--r--core/java/android/app/task/TaskService.java260
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java3
-rw-r--r--core/java/android/appwidget/AppWidgetProviderInfo.java5
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java43
-rw-r--r--core/java/android/bluetooth/BluetoothGatt.java9
-rw-r--r--core/java/android/bluetooth/BluetoothGattServer.java6
-rw-r--r--core/java/android/bluetooth/BluetoothManager.java22
-rw-r--r--core/java/android/bluetooth/BluetoothSocket.java1
-rw-r--r--core/java/android/bluetooth/BluetoothTetheringDataTracker.java8
-rw-r--r--core/java/android/bluetooth/BluetoothUuid.java63
-rw-r--r--core/java/android/bluetooth/IBluetoothGatt.aidl4
-rw-r--r--core/java/android/content/Context.java10
-rw-r--r--core/java/android/content/Task.java400
-rw-r--r--core/java/android/content/TaskManager.java66
-rw-r--r--core/java/android/content/pm/ILauncherApps.aidl2
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl7
-rw-r--r--core/java/android/content/pm/LauncherApps.java33
-rw-r--r--core/java/android/content/pm/PackageManager.java22
-rw-r--r--core/java/android/content/pm/PackageParser.java17
-rw-r--r--core/java/android/content/pm/Signature.java40
-rw-r--r--core/java/android/content/pm/UserInfo.java13
-rw-r--r--core/java/android/content/res/Resources.java4
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java22
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java8
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java14
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java6
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java315
-rw-r--r--core/java/android/hardware/camera2/ColorSpaceTransform.java239
-rw-r--r--core/java/android/hardware/camera2/LensShadingMap.java243
-rw-r--r--core/java/android/hardware/camera2/MeteringRectangle.java224
-rw-r--r--core/java/android/hardware/camera2/ReprocessFormatsMap.java258
-rw-r--r--core/java/android/hardware/camera2/RggbChannelVector.java199
-rw-r--r--core/java/android/hardware/camera2/Size.java57
-rw-r--r--core/java/android/hardware/camera2/StreamConfiguration.java166
-rw-r--r--core/java/android/hardware/camera2/StreamConfigurationDuration.java148
-rw-r--r--core/java/android/hardware/camera2/StreamConfigurationMap.java508
-rw-r--r--core/java/android/hardware/camera2/TonemapCurve.java290
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDevice.java2
-rw-r--r--core/java/android/hardware/camera2/impl/HashCodeHelpers.java143
-rw-r--r--core/java/android/hardware/hdmi/HdmiCec.java10
-rw-r--r--core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java168
-rw-r--r--core/java/android/hardware/hdmi/HdmiCecMessage.java4
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java3
-rw-r--r--core/java/android/inputmethodservice/SoftInputWindow.java78
-rw-r--r--core/java/android/net/INetworkScoreCache.aidl43
-rw-r--r--core/java/android/net/INetworkScoreService.aidl18
-rw-r--r--core/java/android/net/MobileDataStateTracker.java2
-rw-r--r--core/java/android/net/NetworkScoreManager.java47
-rw-r--r--core/java/android/net/Proxy.java42
-rw-r--r--core/java/android/net/RssiCurve.java21
-rw-r--r--core/java/android/os/BatteryStats.java118
-rw-r--r--core/java/android/os/Build.java28
-rw-r--r--core/java/android/os/IUserManager.aidl1
-rw-r--r--core/java/android/os/Process.java10
-rw-r--r--core/java/android/os/UserManager.java110
-rw-r--r--core/java/android/provider/Settings.java13
-rw-r--r--core/java/android/provider/TvContract.java151
-rw-r--r--core/java/android/service/dreams/DreamService.java4
-rw-r--r--core/java/android/service/fingerprint/FingerprintManager.java200
-rw-r--r--core/java/android/service/fingerprint/FingerprintManagerReceiver.java59
-rw-r--r--core/java/android/service/fingerprint/FingerprintService.java219
-rw-r--r--core/java/android/service/fingerprint/FingerprintUtils.java85
-rw-r--r--core/java/android/service/fingerprint/IFingerprintService.aidl38
-rw-r--r--core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl30
-rw-r--r--core/java/android/service/notification/Condition.java53
-rw-r--r--core/java/android/service/notification/ConditionProviderService.java58
-rw-r--r--core/java/android/service/notification/StatusBarNotification.java2
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java86
-rw-r--r--core/java/android/service/voice/IVoiceInteractionSession.aidl11
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java19
-rw-r--r--core/java/android/service/voice/VoiceInteractionSession.java357
-rw-r--r--core/java/android/service/voice/VoiceInteractionSessionService.java13
-rw-r--r--core/java/android/speech/tts/BlockingAudioTrack.java13
-rw-r--r--core/java/android/speech/tts/FileSynthesisCallback.java3
-rw-r--r--core/java/android/text/format/Formatter.java65
-rw-r--r--core/java/android/transition/ChangeClipBounds.java96
-rw-r--r--core/java/android/transition/ChangeTransform.java163
-rw-r--r--core/java/android/transition/CircularPropagation.java10
-rw-r--r--core/java/android/transition/MoveImage.java44
-rw-r--r--core/java/android/transition/SidePropagation.java10
-rw-r--r--core/java/android/transition/Transition.java64
-rw-r--r--core/java/android/transition/TransitionInflater.java34
-rw-r--r--core/java/android/tv/TvInputService.java9
-rw-r--r--core/java/android/util/Range.java144
-rw-r--r--core/java/android/util/Size.java98
-rw-r--r--core/java/android/util/SizeF.java108
-rw-r--r--core/java/android/view/Choreographer.java8
-rw-r--r--core/java/android/view/GLES20Canvas.java11
-rw-r--r--core/java/android/view/GLES20RecordingCanvas.java9
-rw-r--r--core/java/android/view/HardwareCanvas.java4
-rw-r--r--core/java/android/view/KeyEvent.java330
-rw-r--r--core/java/android/view/LayoutInflater.java15
-rw-r--r--core/java/android/view/MotionEvent.java22
-rw-r--r--core/java/android/view/RenderNode.java53
-rw-r--r--core/java/android/view/RenderNodeAnimator.java155
-rw-r--r--core/java/android/view/SurfaceControl.java25
-rw-r--r--core/java/android/view/TextureView.java30
-rw-r--r--core/java/android/view/ThreadedRenderer.java48
-rw-r--r--core/java/android/view/View.java133
-rw-r--r--core/java/android/view/ViewConfiguration.java25
-rw-r--r--core/java/android/view/ViewGroup.java3
-rw-r--r--core/java/android/view/ViewParent.java15
-rw-r--r--core/java/android/view/ViewPropertyAnimator.java26
-rw-r--r--core/java/android/view/ViewRootImpl.java114
-rw-r--r--core/java/android/view/Window.java16
-rw-r--r--core/java/android/view/WindowInsets.java54
-rw-r--r--core/java/android/view/WindowManagerPolicy.java19
-rw-r--r--core/java/android/view/inputmethod/InputMethodInfo.java15
-rw-r--r--core/java/android/webkit/ClientCertRequest.java2
-rw-r--r--core/java/android/webkit/PermissionRequest.java13
-rw-r--r--core/java/android/webkit/WebChromeClient.java4
-rw-r--r--core/java/android/webkit/WebSettings.java57
-rw-r--r--core/java/android/webkit/WebView.java40
-rw-r--r--core/java/android/webkit/WebViewClient.java37
-rw-r--r--core/java/android/webkit/WebViewFactoryProvider.java7
-rw-r--r--core/java/android/webkit/WebViewProvider.java2
-rw-r--r--core/java/android/widget/AbsListView.java171
-rw-r--r--core/java/android/widget/ActionMenuPresenter.java3
-rw-r--r--core/java/android/widget/ListPopupWindow.java6
-rw-r--r--core/java/android/widget/ListView.java2
-rw-r--r--core/java/android/widget/PopupWindow.java116
-rw-r--r--core/java/android/widget/ScrollView.java41
148 files changed, 8423 insertions, 1130 deletions
diff --git a/core/java/android/animation/TypeEvaluator.java b/core/java/android/animation/TypeEvaluator.java
index 2640457..429c435 100644
--- a/core/java/android/animation/TypeEvaluator.java
+++ b/core/java/android/animation/TypeEvaluator.java
@@ -29,7 +29,7 @@ public interface TypeEvaluator<T> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
- * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
+ * calculation is a simple parametric calculation: <code>result = x0 + t * (x1 - x0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 04f62e3..3c3df01 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -932,6 +932,66 @@ public abstract class ActionBar {
*/
public void setHomeActionContentDescription(int resId) { }
+ /**
+ * Enable hiding the action bar on content scroll.
+ *
+ * <p>If enabled, the action bar will scroll out of sight along with a
+ * {@link View#setNestedScrollingEnabled(boolean) nested scrolling child} view's content.
+ * The action bar must be in {@link Window#FEATURE_ACTION_BAR_OVERLAY overlay mode}
+ * to enable hiding on content scroll.</p>
+ *
+ * <p>When partially scrolled off screen the action bar is considered
+ * {@link #hide() hidden}. A call to {@link #show() show} will cause it to return to full view.
+ * </p>
+ * @param hideOnContentScroll true to enable hiding on content scroll.
+ */
+ public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) {
+ if (hideOnContentScroll) {
+ throw new UnsupportedOperationException("Hide on content scroll is not supported in " +
+ "this action bar configuration.");
+ }
+ }
+
+ /**
+ * Return whether the action bar is configured to scroll out of sight along with
+ * a {@link View#setNestedScrollingEnabled(boolean) nested scrolling child}.
+ *
+ * @return true if hide-on-content-scroll is enabled
+ * @see #setHideOnContentScrollEnabled(boolean)
+ */
+ public boolean isHideOnContentScrollEnabled() {
+ return false;
+ }
+
+ /**
+ * Return the current vertical offset of the action bar.
+ *
+ * <p>The action bar's current hide offset is the distance that the action bar is currently
+ * scrolled offscreen in pixels. The valid range is 0 (fully visible) to the action bar's
+ * current measured {@link #getHeight() height} (fully invisible).</p>
+ *
+ * @return The action bar's offset toward its fully hidden state in pixels
+ */
+ public int getHideOffset() {
+ return 0;
+ }
+
+ /**
+ * Set the current hide offset of the action bar.
+ *
+ * <p>The action bar's current hide offset is the distance that the action bar is currently
+ * scrolled offscreen in pixels. The valid range is 0 (fully visible) to the action bar's
+ * current measured {@link #getHeight() height} (fully invisible).</p>
+ *
+ * @param offset The action bar's offset toward its fully hidden state in pixels.
+ */
+ public void setHideOffset(int offset) {
+ if (offset != 0) {
+ throw new UnsupportedOperationException("Setting an explicit action bar hide offset " +
+ "is not supported in this action bar configuration.");
+ }
+ }
+
/** @hide */
public void setDefaultDisplayHomeAsUpEnabled(boolean enabled) {
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 8981c88..af3a92c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -655,7 +655,8 @@ import java.util.HashMap;
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
- OnCreateContextMenuListener, ComponentCallbacks2 {
+ OnCreateContextMenuListener, ComponentCallbacks2,
+ Window.OnWindowDismissedCallback {
private static final String TAG = "Activity";
private static final boolean DEBUG_LIFECYCLE = false;
@@ -2519,7 +2520,9 @@ public class Activity extends ContextThemeWrapper
/**
* Called when the main window associated with the activity has been dismissed.
+ * @hide
*/
+ @Override
public void onWindowDismissed() {
finish();
}
@@ -3493,6 +3496,16 @@ public class Activity extends ContextThemeWrapper
}
theme.applyStyle(resid, false);
}
+
+ // 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);
+ }
}
/**
@@ -4779,31 +4792,26 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Set a label and icon to be used in the Recents task display. When {@link
- * ActivityManager#getRecentTasks} is called, the activities of each task are
- * traversed in order from the topmost activity to the bottommost. As soon as one activity is
- * found with either a non-null label or a non-null icon set by this call the traversal is
- * ended. For each task those values will be returned in {@link
- * ActivityManager.RecentTaskInfo#activityLabel} and {@link
- * ActivityManager.RecentTaskInfo#activityIcon}.
+ * Sets information describing this Activity for presentation inside the Recents System UI. When
+ * {@link ActivityManager#getRecentTasks} is called, the activities of each task are
+ * traversed in order from the topmost activity to the bottommost. The traversal continues for
+ * each property until a suitable value is found. For each task those values will be returned in
+ * {@link android.app.ActivityManager.RecentsActivityValues}.
*
* @see ActivityManager#getRecentTasks
- * @see ActivityManager.RecentTaskInfo
+ * @see android.app.ActivityManager.RecentsActivityValues
*
- * @param activityLabel The label to use in the RecentTaskInfo.
- * @param activityIcon The Bitmap to use in the RecentTaskInfo.
+ * @param values The Recents values that describe this activity.
*/
- public void setActivityLabelAndIcon(CharSequence activityLabel, Bitmap activityIcon) {
- final Bitmap scaledIcon;
- if (activityIcon != null) {
+ public void setRecentsActivityValues(ActivityManager.RecentsActivityValues values) {
+ ActivityManager.RecentsActivityValues activityValues =
+ new ActivityManager.RecentsActivityValues(values);
+ if (values.icon != null) {
final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
- scaledIcon = Bitmap.createScaledBitmap(activityIcon, size, size, true);
- } else {
- scaledIcon = null;
+ activityValues.icon = Bitmap.createScaledBitmap(values.icon, size, size, true);
}
try {
- ActivityManagerNative.getDefault().setActivityLabelAndIcon(mToken, activityLabel,
- scaledIcon);
+ ActivityManagerNative.getDefault().setRecentsActivityValues(mToken, activityValues);
} catch (RemoteException e) {
}
}
@@ -5432,6 +5440,7 @@ public class Activity extends ContextThemeWrapper
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
+ mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 018e949..5d809d8 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -475,6 +475,111 @@ public class ActivityManager {
}
/**
+ * Information you can set and retrieve about the current activity within Recents.
+ */
+ public static class RecentsActivityValues implements Parcelable {
+ public CharSequence label;
+ public Bitmap icon;
+ public int colorPrimary;
+
+ public RecentsActivityValues(RecentsActivityValues values) {
+ copyFrom(values);
+ }
+
+ /**
+ * Creates the RecentsActivityValues to the specified values.
+ *
+ * @param label A label and description of the current state of this activity.
+ * @param icon An icon that represents the current state of this activity.
+ * @param color A color to override the theme's primary color.
+ */
+ public RecentsActivityValues(CharSequence label, Bitmap icon, int color) {
+ this.label = label;
+ this.icon = icon;
+ this.colorPrimary = color;
+ }
+
+ /**
+ * Creates the RecentsActivityValues to the specified values.
+ *
+ * @param label A label and description of the current state of this activity.
+ * @param icon An icon that represents the current state of this activity.
+ */
+ public RecentsActivityValues(CharSequence label, Bitmap icon) {
+ this(label, icon, 0);
+ }
+
+ /**
+ * Creates the RecentsActivityValues to the specified values.
+ *
+ * @param label A label and description of the current state of this activity.
+ */
+ public RecentsActivityValues(CharSequence label) {
+ this(label, null, 0);
+ }
+
+ public RecentsActivityValues() {
+ this(null, null, 0);
+ }
+
+ private RecentsActivityValues(Parcel source) {
+ readFromParcel(source);
+ }
+
+ /**
+ * Do a shallow copy of another set of activity values.
+ * @hide
+ */
+ public void copyFrom(RecentsActivityValues v) {
+ if (v != null) {
+ label = v.label;
+ icon = v.icon;
+ colorPrimary = v.colorPrimary;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ TextUtils.writeToParcel(label, dest,
+ Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ if (icon == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ icon.writeToParcel(dest, 0);
+ }
+ dest.writeInt(colorPrimary);
+ }
+
+ public void readFromParcel(Parcel source) {
+ label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ icon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
+ colorPrimary = source.readInt();
+ }
+
+ public static final Creator<RecentsActivityValues> CREATOR
+ = new Creator<RecentsActivityValues>() {
+ public RecentsActivityValues createFromParcel(Parcel source) {
+ return new RecentsActivityValues(source);
+ }
+ public RecentsActivityValues[] newArray(int size) {
+ return new RecentsActivityValues[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "RecentsActivityValues Label: " + label + " Icon: " + icon +
+ " colorPrimary: " + colorPrimary;
+ }
+ }
+
+ /**
* Information you can retrieve about tasks that the user has most recently
* started or visited.
*/
@@ -523,16 +628,10 @@ public class ActivityManager {
public int userId;
/**
- * The label of the highest activity in the task stack to have set a label using
- * {@link Activity#setActivityLabelAndIcon(CharSequence, android.graphics.Bitmap)}.
- */
- public CharSequence activityLabel;
-
- /**
- * The Bitmap icon of the highest activity in the task stack to set a Bitmap using
- * {@link Activity#setActivityLabelAndIcon(CharSequence, android.graphics.Bitmap)}.
+ * The recent activity values for the highest activity in the stack to have set the values.
+ * {@link Activity#setRecentsActivityValues(android.app.ActivityManager.RecentsActivityValues)}.
*/
- public Bitmap activityIcon;
+ public RecentsActivityValues activityValues;
public RecentTaskInfo() {
}
@@ -555,13 +654,11 @@ public class ActivityManager {
ComponentName.writeToParcel(origActivity, dest);
TextUtils.writeToParcel(description, dest,
Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
- TextUtils.writeToParcel(activityLabel, dest,
- Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
- if (activityIcon == null) {
- dest.writeInt(0);
- } else {
+ if (activityValues != null) {
dest.writeInt(1);
- activityIcon.writeToParcel(dest, 0);
+ activityValues.writeToParcel(dest, 0);
+ } else {
+ dest.writeInt(0);
}
dest.writeInt(stackId);
dest.writeInt(userId);
@@ -573,8 +670,8 @@ public class ActivityManager {
baseIntent = source.readInt() > 0 ? Intent.CREATOR.createFromParcel(source) : null;
origActivity = ComponentName.readFromParcel(source);
description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
- activityLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
- activityIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
+ activityValues = source.readInt() > 0 ?
+ RecentsActivityValues.CREATOR.createFromParcel(source) : null;
stackId = source.readInt();
userId = source.readInt();
}
@@ -791,42 +888,6 @@ public class ActivityManager {
* activity -- the task may have been frozen by the system, so that it
* can be restarted in its previous state when next brought to the
* foreground.
- *
- * @param maxNum The maximum number of entries to return in the list. The
- * actual number returned may be smaller, depending on how many tasks the
- * user has started.
- *
- * @param flags Optional flags
- * @param receiver Optional receiver for delayed thumbnails
- *
- * @return Returns a list of RunningTaskInfo records describing each of
- * the running tasks.
- *
- * Some thumbnails may not be available at the time of this call. The optional
- * receiver may be used to receive those thumbnails.
- *
- * @throws SecurityException Throws SecurityException if the caller does
- * not hold the {@link android.Manifest.permission#GET_TASKS} permission.
- *
- * @hide
- */
- public List<RunningTaskInfo> getRunningTasks(int maxNum, int flags, IThumbnailReceiver receiver)
- throws SecurityException {
- try {
- return ActivityManagerNative.getDefault().getTasks(maxNum, flags, receiver);
- } catch (RemoteException e) {
- // System dead, we will be dead too soon!
- return null;
- }
- }
-
- /**
- * Return a list of the tasks that are currently running, with
- * the most recent being first and older ones after in order. Note that
- * "running" does not mean any of the task's code is currently loaded or
- * activity -- the task may have been frozen by the system, so that it
- * can be restarted in its previous state when next brought to the
- * foreground.
*
* <p><b>Note: this method is only intended for debugging and presenting
* task management user interfaces</b>. This should never be used for
@@ -849,7 +910,12 @@ public class ActivityManager {
*/
public List<RunningTaskInfo> getRunningTasks(int maxNum)
throws SecurityException {
- return getRunningTasks(maxNum, 0, null);
+ try {
+ return ActivityManagerNative.getDefault().getTasks(maxNum, 0);
+ } catch (RemoteException e) {
+ // System dead, we will be dead too soon!
+ return null;
+ }
}
/**
@@ -1627,13 +1693,6 @@ public class ActivityManager {
public int lastTrimLevel;
/**
- * Constant for {@link #importance}: this is a persistent process.
- * Only used when reporting to process observers.
- * @hide
- */
- public static final int IMPORTANCE_PERSISTENT = 50;
-
- /**
* Constant for {@link #importance}: this process is running the
* foreground UI.
*/
@@ -1748,9 +1807,16 @@ public class ActivityManager {
*/
public int importanceReasonImportance;
+ /**
+ * Current process state, as per PROCESS_STATE_* constants.
+ * @hide
+ */
+ public int processState;
+
public RunningAppProcessInfo() {
importance = IMPORTANCE_FOREGROUND;
importanceReasonCode = REASON_UNKNOWN;
+ processState = PROCESS_STATE_IMPORTANT_FOREGROUND;
}
public RunningAppProcessInfo(String pProcessName, int pPid, String pArr[]) {
@@ -1776,6 +1842,7 @@ public class ActivityManager {
dest.writeInt(importanceReasonPid);
ComponentName.writeToParcel(importanceReasonComponent, dest);
dest.writeInt(importanceReasonImportance);
+ dest.writeInt(processState);
}
public void readFromParcel(Parcel source) {
@@ -1791,6 +1858,7 @@ public class ActivityManager {
importanceReasonPid = source.readInt();
importanceReasonComponent = ComponentName.readFromParcel(source);
importanceReasonImportance = source.readInt();
+ processState = source.readInt();
}
public static final Creator<RunningAppProcessInfo> CREATOR =
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index b1c37de..57da21e 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -509,11 +509,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
data.enforceInterface(IActivityManager.descriptor);
int maxNum = data.readInt();
int fl = data.readInt();
- IBinder receiverBinder = data.readStrongBinder();
- IThumbnailReceiver receiver = receiverBinder != null
- ? IThumbnailReceiver.Stub.asInterface(receiverBinder)
- : null;
- List<ActivityManager.RunningTaskInfo> list = getTasks(maxNum, fl, receiver);
+ List<ActivityManager.RunningTaskInfo> list = getTasks(maxNum, fl);
reply.writeNoException();
int N = list != null ? list.size() : -1;
reply.writeInt(N);
@@ -712,17 +708,6 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
- case REPORT_THUMBNAIL_TRANSACTION: {
- data.enforceInterface(IActivityManager.descriptor);
- IBinder token = data.readStrongBinder();
- Bitmap thumbnail = data.readInt() != 0
- ? Bitmap.CREATOR.createFromParcel(data) : null;
- CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data);
- reportThumbnail(token, thumbnail, description);
- reply.writeNoException();
- return true;
- }
-
case GET_CONTENT_PROVIDER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
@@ -2134,13 +2119,12 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
- case SET_ACTIVITY_LABEL_ICON_TRANSACTION: {
+ case SET_RECENTS_ACTIVITY_VALUES_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
- CharSequence activityLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data);
- Bitmap activityIcon = data.readInt() > 0
- ? Bitmap.CREATOR.createFromParcel(data) : null;
- setActivityLabelAndIcon(token, activityLabel, activityIcon);
+ ActivityManager.RecentsActivityValues values =
+ ActivityManager.RecentsActivityValues.CREATOR.createFromParcel(data);
+ setRecentsActivityValues(token, values);
reply.writeNoException();
return true;
}
@@ -2678,14 +2662,12 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
return res;
}
- public List getTasks(int maxNum, int flags,
- IThumbnailReceiver receiver) throws RemoteException {
+ public List getTasks(int maxNum, int flags) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(maxNum);
data.writeInt(flags);
- data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);
mRemote.transact(GET_TASKS_TRANSACTION, data, reply, 0);
reply.readException();
ArrayList list = null;
@@ -2964,25 +2946,6 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
return res;
}
- public void reportThumbnail(IBinder token,
- Bitmap thumbnail, CharSequence description) throws RemoteException
- {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeStrongBinder(token);
- if (thumbnail != null) {
- data.writeInt(1);
- thumbnail.writeToParcel(data, 0);
- } else {
- data.writeInt(0);
- }
- TextUtils.writeToParcel(description, data, 0);
- mRemote.transact(REPORT_THUMBNAIL_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
- reply.readException();
- data.recycle();
- reply.recycle();
- }
public ContentProviderHolder getContentProvider(IApplicationThread caller,
String name, int userId, boolean stable) throws RemoteException {
Parcel data = Parcel.obtain();
@@ -4919,21 +4882,14 @@ class ActivityManagerProxy implements IActivityManager
}
@Override
- public void setActivityLabelAndIcon(IBinder token, CharSequence activityLabel,
- Bitmap activityIcon) throws RemoteException
- {
+ public void setRecentsActivityValues(IBinder token, ActivityManager.RecentsActivityValues values)
+ throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
- TextUtils.writeToParcel(activityLabel, data, 0);
- if (activityIcon != null) {
- data.writeInt(1);
- activityIcon.writeToParcel(data, 0);
- } else {
- data.writeInt(0);
- }
- mRemote.transact(SET_ACTIVITY_LABEL_ICON_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ values.writeToParcel(data, 0);
+ mRemote.transact(SET_RECENTS_ACTIVITY_VALUES_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
reply.readException();
data.recycle();
reply.recycle();
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 85464c47..a49359f 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -649,13 +649,31 @@ public class ActivityOptions {
/**
* Called when the start state for shared elements is captured on enter.
+ *
+ * @param sharedElementNames The names of the shared elements that were accepted into
+ * the View hierarchy.
+ * @param sharedElements The shared elements that are part of the View hierarchy.
+ * @param sharedElementSnapshots The Views containing snap shots of the shared element
+ * from the launching Window. These elements will not
+ * be part of the scene, but will be positioned relative
+ * to the Window decor View.
*/
- public void onCaptureSharedElementStart() {}
+ public void onCaptureSharedElementStart(List<String> sharedElementNames,
+ List<View> sharedElements, List<View> sharedElementSnapshots) {}
/**
* Called when the end state for shared elements is captured on enter.
+ *
+ * @param sharedElementNames The names of the shared elements that were accepted into
+ * the View hierarchy.
+ * @param sharedElements The shared elements that are part of the View hierarchy.
+ * @param sharedElementSnapshots The Views containing snap shots of the shared element
+ * from the launching Window. These elements will not
+ * be part of the scene, but will be positioned relative
+ * to the Window decor View.
*/
- public void onCaptureSharedElementEnd() {}
+ public void onCaptureSharedElementEnd(List<String> sharedElementNames,
+ List<View> sharedElements, List<View> sharedElementSnapshots) {}
/**
* Called when the enter Transition has been started.
@@ -700,6 +718,22 @@ public class ActivityOptions {
* call.
*/
public Pair<View, String>[] getSharedElementsMapping() { return null; }
+
+ /**
+ * Returns <code>true</code> if the ActivityTransitionListener will handle removing
+ * rejected shared elements from the scene. If <code>false</code> is returned, a default
+ * animation will be used to remove the rejected shared elements from the scene.
+ *
+ * @param rejectedSharedElements Views containing visual information of shared elements
+ * that are not part of the entering scene. These Views
+ * are positioned relative to the Window decor View.
+ * @return <code>false</code> if the default animation should be used to remove the
+ * rejected shared elements from the scene or <code>true</code> if the listener provides
+ * custom handling.
+ */
+ public boolean handleRejectedSharedElements(List<View> rejectedSharedElements) {
+ return false;
+ }
}
private static class SharedElementMappingListener extends ActivityTransitionListener {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7dc21b4..3b2ff7f 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -821,10 +821,6 @@ public final class ActivityThread {
sendMessage(H.SUICIDE, null);
}
- public void requestThumbnail(IBinder token) {
- sendMessage(H.REQUEST_THUMBNAIL, token);
- }
-
public void scheduleConfigurationChanged(Configuration config) {
updatePendingConfiguration(config);
sendMessage(H.CONFIGURATION_CHANGED, config);
@@ -1168,7 +1164,7 @@ public final class ActivityThread {
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115;
public static final int STOP_SERVICE = 116;
- public static final int REQUEST_THUMBNAIL = 117;
+
public static final int CONFIGURATION_CHANGED = 118;
public static final int CLEAN_UP_CONTEXT = 119;
public static final int GC_WHEN_IDLE = 120;
@@ -1218,7 +1214,6 @@ public final class ActivityThread {
case CREATE_SERVICE: return "CREATE_SERVICE";
case SERVICE_ARGS: return "SERVICE_ARGS";
case STOP_SERVICE: return "STOP_SERVICE";
- case REQUEST_THUMBNAIL: return "REQUEST_THUMBNAIL";
case CONFIGURATION_CHANGED: return "CONFIGURATION_CHANGED";
case CLEAN_UP_CONTEXT: return "CLEAN_UP_CONTEXT";
case GC_WHEN_IDLE: return "GC_WHEN_IDLE";
@@ -1367,11 +1362,6 @@ public final class ActivityThread {
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
- case REQUEST_THUMBNAIL:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "requestThumbnail");
- handleRequestThumbnail((IBinder)msg.obj);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- break;
case CONFIGURATION_CHANGED:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
@@ -3817,28 +3807,6 @@ public final class ActivityThread {
handleLaunchActivity(r, currentIntent);
}
- private void handleRequestThumbnail(IBinder token) {
- ActivityClientRecord r = mActivities.get(token);
- Bitmap thumbnail = createThumbnailBitmap(r);
- CharSequence description = null;
- try {
- description = r.activity.onCreateDescription();
- } catch (Exception e) {
- if (!mInstrumentation.onException(r.activity, e)) {
- throw new RuntimeException(
- "Unable to create description of activity "
- + r.intent.getComponent().toShortString()
- + ": " + e.toString(), e);
- }
- }
- //System.out.println("Reporting top thumbnail " + thumbnail);
- try {
- ActivityManagerNative.getDefault().reportThumbnail(
- token, thumbnail, description);
- } catch (RemoteException ex) {
- }
- }
-
ArrayList<ComponentCallbacks2> collectComponentCallbacks(
boolean allActivities, Configuration newConfig) {
ArrayList<ComponentCallbacks2> callbacks
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index d8a356f..3eb2fea 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -15,6 +15,13 @@
*/
package android.app;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
@@ -24,10 +31,13 @@ import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.ArrayMap;
import android.util.Pair;
+import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewGroupOverlay;
import android.view.ViewTreeObserver;
import android.view.Window;
+import android.widget.ImageView;
import java.util.ArrayList;
import java.util.Collection;
@@ -129,6 +139,11 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
private static final String KEY_WIDTH = "shared_element:width";
private static final String KEY_HEIGHT = "shared_element:height";
private static final String KEY_NAME = "shared_element:name";
+ private static final String KEY_BITMAP = "shared_element:bitmap";
+ private static final String KEY_SCALE_TYPE = "shared_element:scaleType";
+ private static final String KEY_IMAGE_MATRIX = "shared_element:imageMatrix";
+
+ private static final ImageView.ScaleType[] SCALE_TYPE_VALUES = ImageView.ScaleType.values();
/**
* Sent by the exiting coordinator (either EnterTransitionCoordinator
@@ -197,6 +212,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
private ResultReceiver mRemoteResultReceiver;
private boolean mNotifiedSharedElementTransitionComplete;
private boolean mNotifiedExitTransitionComplete;
+ private boolean mSharedElementTransitionStarted;
private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
@@ -241,7 +257,11 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
onPrepareRestore();
break;
case MSG_EXIT_TRANSITION_COMPLETE:
- onRemoteSceneExitComplete();
+ if (!mSharedElementTransitionStarted) {
+ send(resultCode, resultData);
+ } else {
+ onRemoteSceneExitComplete();
+ }
break;
case MSG_TAKE_SHARED_ELEMENTS:
ArrayList<String> sharedElementNames
@@ -305,16 +325,25 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
setSharedElements();
reconcileSharedElements(sharedElementNames);
mEnteringViews.removeAll(mSharedElements);
- setSharedElementState(state);
+ final ArrayList<View> accepted = new ArrayList<View>();
+ final ArrayList<View> rejected = new ArrayList<View>();
+ createSharedElementImages(accepted, rejected, sharedElementNames, state);
+ ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState =
+ setSharedElementState(state, accepted);
+ handleRejected(rejected);
+
if (getViewsTransition() != null) {
setViewVisibility(mEnteringViews, View.INVISIBLE);
}
setViewVisibility(mSharedElements, View.VISIBLE);
Transition transition = beginTransition(mEnteringViews, true, allowOverlappingTransitions(),
true);
+ setOriginalImageViewState(originalImageViewState);
+
if (allowOverlappingTransitions()) {
onStartEnterTransition(transition, mEnteringViews);
}
+
mRemoteResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
}
@@ -440,9 +469,13 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
mTargetSharedNames.clear();
if (sharedElements == null) {
ArrayMap<String, View> map = new ArrayMap<String, View>();
- setViewVisibility(mEnteringViews, View.VISIBLE);
+ if (getViewsTransition() != null) {
+ setViewVisibility(mEnteringViews, View.VISIBLE);
+ }
getDecor().findSharedElements(map);
- setViewVisibility(mEnteringViews, View.INVISIBLE);
+ if (getViewsTransition() != null) {
+ setViewVisibility(mEnteringViews, View.INVISIBLE);
+ }
for (int i = 0; i < map.size(); i++) {
View view = map.valueAt(i);
String name = map.keyAt(i);
@@ -513,39 +546,94 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
}
private void reconcileSharedElements(ArrayList<String> sharedElementNames) {
- Rect epicenter = null;
- for (int i = mTargetSharedNames.size() - 1; i >= 0; i--) {
- if (!sharedElementNames.contains(mTargetSharedNames.get(i))) {
- mTargetSharedNames.remove(i);
- mSharedElements.remove(i);
+ // keep only those that are in sharedElementNames.
+ int numSharedElements = sharedElementNames.size();
+ int targetIndex = 0;
+ for (int i = 0; i < numSharedElements; i++) {
+ String name = sharedElementNames.get(i);
+ int index = mTargetSharedNames.indexOf(name);
+ if (index >= 0) {
+ // Swap the items at the indexes if necessary.
+ if (index != targetIndex) {
+ View temp = mSharedElements.get(index);
+ mSharedElements.set(index, mSharedElements.get(targetIndex));
+ mSharedElements.set(targetIndex, temp);
+ mTargetSharedNames.set(index, mTargetSharedNames.get(targetIndex));
+ mTargetSharedNames.set(targetIndex, name);
+ }
+ targetIndex++;
}
}
- if (!mSharedElements.isEmpty()) {
+ for (int i = mSharedElements.size() - 1; i >= targetIndex; i--) {
+ mSharedElements.remove(i);
+ mTargetSharedNames.remove(i);
+ }
+ Rect epicenter = null;
+ if (!mTargetSharedNames.isEmpty()
+ && mTargetSharedNames.get(0).equals(sharedElementNames.get(0))) {
epicenter = calcEpicenter(mSharedElements.get(0));
}
mEpicenterCallback.setEpicenter(epicenter);
}
- private void setSharedElementState(Bundle sharedElementState) {
+ private ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
+ Bundle sharedElementState, final ArrayList<View> acceptedOverlayViews) {
+ ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
+ new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
+ final int[] tempLoc = new int[2];
if (sharedElementState != null) {
- int[] tempLoc = new int[2];
for (int i = 0; i < mSharedElements.size(); i++) {
View sharedElement = mSharedElements.get(i);
String name = mTargetSharedNames.get(i);
+ Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
+ name, sharedElementState);
+ if (originalState != null) {
+ originalImageState.put((ImageView) sharedElement, originalState);
+ }
+ View parent = (View) sharedElement.getParent();
+ parent.getLocationOnScreen(tempLoc);
setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
+ sharedElement.requestLayout();
}
}
- mListener.onCaptureSharedElementStart();
+ mListener.onCaptureSharedElementStart(mTargetSharedNames, mSharedElements,
+ acceptedOverlayViews);
+
getDecor().getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
- mListener.onCaptureSharedElementEnd();
+ mListener.onCaptureSharedElementEnd(mTargetSharedNames, mSharedElements,
+ acceptedOverlayViews);
+ mSharedElementTransitionStarted = true;
return true;
}
}
);
+ return originalImageState;
+ }
+
+ private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
+ Bundle transitionArgs) {
+ if (!(view instanceof ImageView)) {
+ return null;
+ }
+ Bundle bundle = transitionArgs.getBundle(name);
+ int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
+ if (scaleTypeInt < 0) {
+ return null;
+ }
+
+ ImageView imageView = (ImageView) view;
+ ImageView.ScaleType originalScaleType = imageView.getScaleType();
+
+ Matrix originalMatrix = null;
+ if (originalScaleType == ImageView.ScaleType.MATRIX) {
+ originalMatrix = new Matrix(imageView.getImageMatrix());
+ }
+
+ return Pair.create(originalScaleType, originalMatrix);
}
/**
@@ -555,15 +643,30 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
* @param name The shared element name given from the source Activity.
* @param transitionArgs A <code>Bundle</code> containing all placementinformation for named
* shared elements in the scene.
- * @param tempLoc A temporary int[2] for capturing the current location of views.
+ * @param parentLoc The x and y coordinates of the parent's screen position.
*/
private static void setSharedElementState(View view, String name, Bundle transitionArgs,
- int[] tempLoc) {
+ int[] parentLoc) {
Bundle sharedElementBundle = transitionArgs.getBundle(name);
if (sharedElementBundle == null) {
return;
}
+ if (view instanceof ImageView) {
+ int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
+ if (scaleTypeInt >= 0) {
+ ImageView imageView = (ImageView) view;
+ ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
+ imageView.setScaleType(scaleType);
+ if (scaleType == ImageView.ScaleType.MATRIX) {
+ float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
+ Matrix matrix = new Matrix();
+ matrix.setValues(matrixValues);
+ imageView.setImageMatrix(matrix);
+ }
+ }
+ }
+
float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
view.setTranslationZ(z);
@@ -576,15 +679,11 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
view.measure(widthSpec, heightSpec);
- ViewGroup parent = (ViewGroup) view.getParent();
- parent.getLocationOnScreen(tempLoc);
- int left = x - tempLoc[0];
- int top = y - tempLoc[1];
+ int left = x - parentLoc[0];
+ int top = y - parentLoc[1];
int right = left + width;
int bottom = top + height;
view.layout(left, top, right, bottom);
-
- view.requestLayout();
}
/**
@@ -615,6 +714,22 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
sharedElementBundle.putString(KEY_NAME, view.getSharedElementName());
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ view.draw(canvas);
+ sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);
+
+ if (view instanceof ImageView) {
+ ImageView imageView = (ImageView) view;
+ int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
+ sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
+ if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
+ float[] matrix = new float[9];
+ imageView.getImageMatrix().getValues(matrix);
+ sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
+ }
+ }
+
transitionArgs.putBundle(name, sharedElementBundle);
}
@@ -723,6 +838,80 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
return transition;
}
+ private void handleRejected(final ArrayList<View> rejected) {
+ int numRejected = rejected.size();
+ if (numRejected == 0) {
+ return;
+ }
+ boolean rejectionHandled = mListener.handleRejectedSharedElements(rejected);
+ if (rejectionHandled) {
+ return;
+ }
+
+ ViewGroupOverlay overlay = getDecor().getOverlay();
+ ObjectAnimator animator = null;
+ for (int i = 0; i < numRejected; i++) {
+ View view = rejected.get(i);
+ overlay.add(view);
+ animator = ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0);
+ animator.start();
+ }
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ViewGroupOverlay overlay = getDecor().getOverlay();
+ for (int i = rejected.size() - 1; i >= 0; i--) {
+ overlay.remove(rejected.get(i));
+ }
+ }
+ });
+ }
+
+ private void createSharedElementImages(ArrayList<View> accepted, ArrayList<View> rejected,
+ ArrayList<String> sharedElementNames, Bundle state) {
+ int numSharedElements = sharedElementNames.size();
+ Context context = getWindow().getContext();
+ int[] parentLoc = new int[2];
+ getDecor().getLocationOnScreen(parentLoc);
+ for (int i = 0; i < numSharedElements; i++) {
+ String name = sharedElementNames.get(i);
+ Bundle sharedElementBundle = state.getBundle(name);
+ if (sharedElementBundle != null) {
+ Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
+ ImageView imageView = new ImageView(context);
+ imageView.setId(com.android.internal.R.id.shared_element);
+ imageView.setScaleType(ImageView.ScaleType.CENTER);
+ imageView.setImageBitmap(bitmap);
+ imageView.setSharedElementName(name);
+ setSharedElementState(imageView, name, state, parentLoc);
+ if (mTargetSharedNames.contains(name)) {
+ accepted.add(imageView);
+ } else {
+ rejected.add(imageView);
+ }
+ }
+ }
+ }
+
+ private static void setOriginalImageViewState(
+ ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
+ for (int i = 0; i < originalState.size(); i++) {
+ ImageView imageView = originalState.keyAt(i);
+ Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
+ imageView.setScaleType(state.first);
+ imageView.setImageMatrix(state.second);
+ }
+ }
+
+ private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
+ for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
+ if (scaleType == SCALE_TYPE_VALUES[i]) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
private Rect mEpicenter;
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index a810134..097c64e 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -33,6 +33,7 @@ import android.view.MotionEvent;
import android.view.Surface;
import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
+import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import dalvik.system.CloseGuard;
@@ -51,6 +52,7 @@ public class ActivityView extends ViewGroup {
private int mWidth;
private int mHeight;
private Surface mSurface;
+ private int mLastVisibility;
// Only one IIntentSender or Intent may be queued at a time. Most recent one wins.
IIntentSender mQueuedPendingIntent;
@@ -95,6 +97,8 @@ public class ActivityView extends ViewGroup {
mMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(mMetrics);
+ mLastVisibility = getVisibility();
+
if (DEBUG) Log.v(TAG, "ctor()");
}
@@ -103,6 +107,26 @@ public class ActivityView extends ViewGroup {
mTextureView.layout(0, 0, r - l, b - t);
}
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+
+ if (mSurface != null) {
+ try {
+ if (visibility == View.GONE) {
+ mActivityContainer.setSurface(null, mWidth, mHeight, mMetrics.densityDpi);
+ } else if (mLastVisibility == View.GONE) {
+ // Don't change surface when going between View.VISIBLE and View.INVISIBLE.
+ mActivityContainer.setSurface(mSurface, mWidth, mHeight, mMetrics.densityDpi);
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException(
+ "ActivityView: Unable to set surface of ActivityContainer. " + e);
+ }
+ }
+ mLastVisibility = visibility;
+ }
+
private boolean injectInputEvent(InputEvent event) {
return mActivityContainer != null && mActivityContainer.injectEvent(event);
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index ab62427..efd3d86 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1440,6 +1440,31 @@ final class ApplicationPackageManager extends PackageManager {
return null;
}
+ /**
+ * @hide
+ */
+ @Override
+ public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int userIdOrig,
+ int userIdDest) {
+ try {
+ mPM.addForwardingIntentFilter(filter, removable, userIdOrig, userIdDest);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void clearForwardingIntentFilters(int userIdOrig) {
+ try {
+ mPM.clearForwardingIntentFilters(userIdOrig);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
private final ContextImpl mContext;
private final IPackageManager mPM;
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index fcc7f8e..7f2fb59 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -312,14 +312,6 @@ public abstract class ApplicationThreadNative extends Binder
return true;
}
- case REQUEST_THUMBNAIL_TRANSACTION:
- {
- data.enforceInterface(IApplicationThread.descriptor);
- IBinder b = data.readStrongBinder();
- requestThumbnail(b);
- return true;
- }
-
case SCHEDULE_CONFIGURATION_CHANGED_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -986,16 +978,6 @@ class ApplicationThreadProxy implements IApplicationThread {
data.recycle();
}
- public final void requestThumbnail(IBinder token)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- data.writeInterfaceToken(IApplicationThread.descriptor);
- data.writeStrongBinder(token);
- mRemote.transact(REQUEST_THUMBNAIL_TRANSACTION, data, null,
- IBinder.FLAG_ONEWAY);
- data.recycle();
- }
-
public final void scheduleConfigurationChanged(Configuration config)
throws RemoteException {
Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fe532bf..c621696 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -106,6 +106,9 @@ import android.os.storage.IMountService;
import android.os.storage.StorageManager;
import android.print.IPrintManager;
import android.print.PrintManager;
+import android.service.fingerprint.FingerprintManager;
+import android.service.fingerprint.FingerprintManagerReceiver;
+import android.service.fingerprint.FingerprintService;
import android.telephony.TelephonyManager;
import android.tv.ITvInputManager;
import android.tv.TvInputManager;
@@ -451,6 +454,11 @@ class ContextImpl extends Context {
return new KeyguardManager();
}});
+ registerService(FINGERPRINT_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ return new FingerprintManager(ctx);
+ }});
+
registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 07583fd..12d4513 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -79,7 +79,7 @@ import java.lang.ref.WeakReference;
* </div>
*/
public class Dialog implements DialogInterface, Window.Callback,
- KeyEvent.Callback, OnCreateContextMenuListener {
+ KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback {
private static final String TAG = "Dialog";
private Activity mOwnerActivity;
@@ -165,6 +165,7 @@ public class Dialog implements DialogInterface, Window.Callback,
Window w = PolicyManager.makeNewWindow(mContext);
mWindow = w;
w.setCallback(this);
+ w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
@@ -708,6 +709,8 @@ public class Dialog implements DialogInterface, Window.Callback,
public void onDetachedFromWindow() {
}
+ /** @hide */
+ @Override
public void onWindowDismissed() {
dismiss();
}
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index aa097e0..cbb8359 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -23,8 +23,6 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.ResultReceiver;
import android.transition.Transition;
-import android.util.ArrayMap;
-import android.util.Pair;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.Window;
@@ -135,11 +133,6 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator
}
@Override
- protected void onRemoteSceneExitComplete() {
- super.onRemoteSceneExitComplete();
- }
-
- @Override
protected void onTakeSharedElements(ArrayList<String> sharedElementNames, Bundle state) {
mEnteringSharedElementNames = new ArrayList<String>();
mEnteringSharedElementNames.addAll(sharedElementNames);
@@ -149,6 +142,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator
@Override
protected void sharedElementTransitionComplete(Bundle bundle) {
notifySharedElementTransitionComplete(bundle);
+ exitAfterSharedElementTransition();
}
@Override
@@ -223,6 +217,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator
@Override
protected void startExitTransition(ArrayList<String> sharedElements) {
+ mMakeOpaque = false;
notifyPrepareRestore();
if (getDecor().getBackground() == null) {
@@ -264,7 +259,6 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator
mExitTransitionComplete = true;
exitAfterSharedElementTransition();
super.onExitTransitionEnd();
- clearConnections();
}
@Override
@@ -281,12 +275,13 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator
}
private void exitAfterSharedElementTransition() {
- if (mSharedElementTransitionComplete && mExitTransitionComplete) {
+ if (mSharedElementTransitionComplete && mExitTransitionComplete && mBackgroundFadedOut) {
mActivity.finish();
if (mSupportsTransition) {
mActivity.overridePendingTransition(0, 0);
}
notifyExitTransitionComplete();
+ clearConnections();
}
}
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 6b94c4e..2e9cdf3b7 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -111,8 +111,7 @@ public interface IActivityManager extends IInterface {
public void activityDestroyed(IBinder token) throws RemoteException;
public String getCallingPackage(IBinder token) throws RemoteException;
public ComponentName getCallingActivity(IBinder token) throws RemoteException;
- public List<RunningTaskInfo> getTasks(int maxNum, int flags,
- IThumbnailReceiver receiver) throws RemoteException;
+ public List<RunningTaskInfo> getTasks(int maxNum, int flags) throws RemoteException;
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
int flags, int userId) throws RemoteException;
public ActivityManager.TaskThumbnails getTaskThumbnails(int taskId) throws RemoteException;
@@ -131,9 +130,6 @@ public interface IActivityManager extends IInterface {
public boolean isInHomeStack(int taskId) throws RemoteException;
public void setFocusedStack(int stackId) throws RemoteException;
public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
- /* oneway */
- public void reportThumbnail(IBinder token,
- Bitmap thumbnail, CharSequence description) throws RemoteException;
public ContentProviderHolder getContentProvider(IApplicationThread caller,
String name, int userId, boolean stable) throws RemoteException;
public ContentProviderHolder getContentProviderExternal(String name, int userId, IBinder token)
@@ -439,8 +435,8 @@ public interface IActivityManager extends IInterface {
public boolean isInLockTaskMode() throws RemoteException;
/** @hide */
- public void setActivityLabelAndIcon(IBinder token, CharSequence activityLabel,
- Bitmap activityBitmap) throws RemoteException;
+ public void setRecentsActivityValues(IBinder token, ActivityManager.RecentsActivityValues values)
+ throws RemoteException;
/*
* Private non-Binder interfaces
@@ -571,7 +567,7 @@ public interface IActivityManager extends IInterface {
int MOVE_TASK_TO_BACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+24;
int MOVE_TASK_BACKWARDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+25;
int GET_TASK_FOR_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+26;
- int REPORT_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27;
+
int GET_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
int PUBLISH_CONTENT_PROVIDERS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
int REF_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
@@ -738,6 +734,6 @@ public interface IActivityManager extends IInterface {
int START_LOCK_TASK_BY_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+214;
int STOP_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+215;
int IS_IN_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+216;
- int SET_ACTIVITY_LABEL_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217;
+ int SET_RECENTS_ACTIVITY_VALUES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217;
int START_VOICE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index f290e94..fefba8a 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -101,7 +101,6 @@ public interface IApplicationThread extends IInterface {
Bundle coreSettings) throws RemoteException;
void scheduleExit() throws RemoteException;
void scheduleSuicide() throws RemoteException;
- void requestThumbnail(IBinder token) throws RemoteException;
void scheduleConfigurationChanged(Configuration config) throws RemoteException;
void updateTimeZone() throws RemoteException;
void clearDnsCache() throws RemoteException;
@@ -159,7 +158,7 @@ public interface IApplicationThread extends IInterface {
int SCHEDULE_STOP_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+11;
int BIND_APPLICATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+12;
int SCHEDULE_EXIT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+13;
- int REQUEST_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+14;
+
int SCHEDULE_CONFIGURATION_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+15;
int SCHEDULE_SERVICE_ARGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+16;
int UPDATE_TIME_ZONE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+17;
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index ad4027d..b917263 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -58,6 +58,8 @@ interface INotificationManager
ZenModeConfig getZenModeConfig();
boolean setZenModeConfig(in ZenModeConfig config);
oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions);
- oneway void requestZenModeConditions(in IConditionListener callback, boolean requested);
+ oneway void requestZenModeConditions(in IConditionListener callback, int relevance);
oneway void setZenModeCondition(in Uri conditionId);
+ oneway void setAutomaticZenModeConditions(in Uri[] conditionIds);
+ Condition[] getAutomaticZenModeConditions();
} \ No newline at end of file
diff --git a/core/java/android/app/IProcessObserver.aidl b/core/java/android/app/IProcessObserver.aidl
index e587912..ecf2c73 100644
--- a/core/java/android/app/IProcessObserver.aidl
+++ b/core/java/android/app/IProcessObserver.aidl
@@ -20,7 +20,7 @@ package android.app;
oneway interface IProcessObserver {
void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities);
- void onImportanceChanged(int pid, int uid, int importance);
+ void onProcessStateChanged(int pid, int uid, int procState);
void onProcessDied(int pid, int uid);
}
diff --git a/core/java/android/app/IThumbnailReceiver.aidl b/core/java/android/app/IThumbnailReceiver.aidl
deleted file mode 100644
index 7943f2c..0000000
--- a/core/java/android/app/IThumbnailReceiver.aidl
+++ /dev/null
@@ -1,30 +0,0 @@
-/* //device/java/android/android/app/IThumbnailReceiver.aidl
-**
-** Copyright 2006, 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.graphics.Bitmap;
-
-/**
- * System private API for receiving updated thumbnails from a checkpoint.
- *
- * {@hide}
- */
-oneway interface IThumbnailReceiver {
- void newThumbnail(int id, in Bitmap thumbnail, CharSequence description);
- void finished();
-}
-
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 5f8ebbe..58d707c 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -651,6 +651,10 @@ public class WallpaperManager {
* not "image/*"
*/
public Intent getCropAndSetWallpaperIntent(Uri imageUri) {
+ if (imageUri == null) {
+ throw new IllegalArgumentException("Image URI must not be null");
+ }
+
if (!ContentResolver.SCHEME_CONTENT.equals(imageUri.getScheme())) {
throw new IllegalArgumentException("Image URI must be of the "
+ ContentResolver.SCHEME_CONTENT + " scheme type");
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 68ab611..929bf65 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -172,6 +172,16 @@ public class DevicePolicyManager {
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_SET_NEW_PASSWORD
= "android.app.action.SET_NEW_PASSWORD";
+ /**
+ * Flag for {@link #addForwardingIntentFilter}: the intents will forwarded to the primary user.
+ */
+ public static int FLAG_TO_PRIMARY_USER = 0x0001;
+
+ /**
+ * Flag for {@link #addForwardingIntentFilter}: the intents will be forwarded to the managed
+ * profile.
+ */
+ public static int FLAG_TO_MANAGED_PROFILE = 0x0002;
/**
* Return true if the given administrator component is currently
@@ -1778,7 +1788,7 @@ public class DevicePolicyManager {
* Sets the enabled state of the profile. A profile should be enabled only once it is ready to
* be used. Only the profile owner can call this.
*
- * @see #isPRofileOwnerApp
+ * @see #isProfileOwnerApp
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
*/
@@ -1834,27 +1844,6 @@ public class DevicePolicyManager {
/**
* @hide
- * @param userId the userId of a managed profile profile.
- *
- * @return whether or not the managed profile is enabled.
- * @throws IllegalArgumentException if the userId is invalid.
- */
- public boolean isProfileEnabled(int userId) throws IllegalArgumentException {
- if (mService != null) {
- try {
- return mService.isProfileEnabled(userId);
- } catch (RemoteException re) {
- Log.w(TAG, "Failed to get status for owner profile.");
- throw new IllegalArgumentException(
- "Failed to get status for owner profile.", re);
- }
- }
- return true;
- }
-
-
- /**
- * @hide
* @return the human readable name of the organisation associated with this DPM or null if
* one is not set.
* @throws IllegalArgumentException if the userId is invalid.
@@ -1953,6 +1942,39 @@ public class DevicePolicyManager {
}
/**
+ * Called by a profile owner to forward intents sent from the managed profile to the owner, or
+ * from the owner to the managed profile.
+ * If an intent matches this intent filter, then activities belonging to the other user can
+ * respond to this intent.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param filter if an intent matches this IntentFilter, then it can be forwarded.
+ */
+ public void addForwardingIntentFilter(ComponentName admin, IntentFilter filter, int flags) {
+ if (mService != null) {
+ try {
+ mService.addForwardingIntentFilter(admin, filter, flags);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Called by a profile owner to remove the forwarding intent filters from the current user
+ * and from the owner.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ */
+ public void clearForwardingIntentFilters(ComponentName admin) {
+ if (mService != null) {
+ try {
+ mService.clearForwardingIntentFilters(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
* Called by a profile or device owner to get the application restrictions for a given target
* application running in the managed profile.
*
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 72b3c20..e3090b6 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -109,7 +109,6 @@ interface IDevicePolicyManager {
String getProfileOwner(int userHandle);
String getProfileOwnerName(int userHandle);
void setProfileEnabled(in ComponentName who);
- boolean isProfileEnabled(int userHandle);
boolean installCaCert(in byte[] certBuffer);
void uninstallCaCert(in byte[] certBuffer);
@@ -121,4 +120,6 @@ interface IDevicePolicyManager {
Bundle getApplicationRestrictions(in ComponentName who, in String packageName);
void setUserRestriction(in ComponentName who, in String key, boolean enable);
+ void addForwardingIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
+ void clearForwardingIntentFilters(in ComponentName admin);
}
diff --git a/core/java/android/app/task/ITaskCallback.aidl b/core/java/android/app/task/ITaskCallback.aidl
new file mode 100644
index 0000000..ffa57d1
--- /dev/null
+++ b/core/java/android/app/task/ITaskCallback.aidl
@@ -0,0 +1,53 @@
+/**
+ * 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.app.task;
+
+import android.app.task.ITaskService;
+import android.app.task.TaskParams;
+
+/**
+ * The server side of the TaskManager IPC protocols. The app-side implementation
+ * invokes on this interface to indicate completion of the (asynchronous) instructions
+ * issued by the server.
+ *
+ * In all cases, the 'who' parameter is the caller's service binder, used to track
+ * which Task Service instance is reporting.
+ *
+ * {@hide}
+ */
+interface ITaskCallback {
+ /**
+ * Immediate callback to the system after sending a start signal, used to quickly detect ANR.
+ *
+ * @param taskId Unique integer used to identify this task.
+ */
+ void acknowledgeStartMessage(int taskId);
+ /**
+ * Immediate callback to the system after sending a stop signal, used to quickly detect ANR.
+ *
+ * @param taskId Unique integer used to identify this task.
+ */
+ void acknowledgeStopMessage(int taskId);
+ /*
+ * Tell the task manager that the client is done with its execution, so that it can go on to
+ * the next one and stop attributing wakelock time to us etc.
+ *
+ * @param taskId Unique integer used to identify this task.
+ * @param reschedule Whether or not to reschedule this task.
+ */
+ void taskFinished(int taskId, boolean reschedule);
+}
diff --git a/core/java/android/app/task/ITaskService.aidl b/core/java/android/app/task/ITaskService.aidl
new file mode 100644
index 0000000..87b0191
--- /dev/null
+++ b/core/java/android/app/task/ITaskService.aidl
@@ -0,0 +1,35 @@
+/**
+ * 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.app.task;
+
+import android.app.task.ITaskCallback;
+import android.app.task.TaskParams;
+
+import android.os.Bundle;
+
+/**
+ * Interface that the framework uses to communicate with application code that implements a
+ * TaskService. End user code does not implement this interface directly; instead, the app's
+ * service implementation will extend android.app.task.TaskService.
+ * {@hide}
+ */
+oneway interface ITaskService {
+ /** Begin execution of application's task. */
+ void startTask(in TaskParams taskParams);
+ /** Stop execution of application's task. */
+ void stopTask(in TaskParams taskParams);
+}
diff --git a/core/java/android/app/task/TaskParams.aidl b/core/java/android/app/task/TaskParams.aidl
new file mode 100644
index 0000000..9b25855
--- /dev/null
+++ b/core/java/android/app/task/TaskParams.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.app.task;
+
+parcelable TaskParams; \ No newline at end of file
diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/task/TaskParams.java
new file mode 100644
index 0000000..e2eafd8
--- /dev/null
+++ b/core/java/android/app/task/TaskParams.java
@@ -0,0 +1,86 @@
+/*
+ * 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.task;
+
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Contains the parameters used to configure/identify your task. You do not create this object
+ * yourself, instead it is handed in to your application by the System.
+ */
+public class TaskParams implements Parcelable {
+
+ private final int taskId;
+ private final Bundle extras;
+ private final IBinder mCallback;
+
+ /**
+ * @return The unique id of this task, specified at creation time.
+ */
+ public int getTaskId() {
+ return taskId;
+ }
+
+ /**
+ * @return The extras you passed in when constructing this task with
+ * {@link android.content.Task.Builder#setExtras(android.os.Bundle)}. This will
+ * never be null. If you did not set any extras this will be an empty bundle.
+ */
+ public Bundle getExtras() {
+ return extras;
+ }
+
+ /**
+ * @hide
+ */
+ public ITaskCallback getCallback() {
+ return ITaskCallback.Stub.asInterface(mCallback);
+ }
+
+ private TaskParams(Parcel in) {
+ taskId = in.readInt();
+ extras = in.readBundle();
+ mCallback = in.readStrongBinder();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(taskId);
+ dest.writeBundle(extras);
+ dest.writeStrongBinder(mCallback);
+ }
+
+ public static final Creator<TaskParams> CREATOR = new Creator<TaskParams>() {
+ @Override
+ public TaskParams createFromParcel(Parcel in) {
+ return new TaskParams(in);
+ }
+
+ @Override
+ public TaskParams[] newArray(int size) {
+ return new TaskParams[size];
+ }
+ };
+}
diff --git a/core/java/android/app/task/TaskService.java b/core/java/android/app/task/TaskService.java
new file mode 100644
index 0000000..81333be
--- /dev/null
+++ b/core/java/android/app/task/TaskService.java
@@ -0,0 +1,260 @@
+/*
+ * 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.task;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * <p>Entry point for the callback from the {@link android.content.TaskManager}.</p>
+ * <p>This is the base class that handles asynchronous requests that were previously scheduled. You
+ * are responsible for overriding {@link TaskService#onStartTask(TaskParams)}, which is where
+ * you will implement your task logic.</p>
+ * <p>This service executes each incoming task on a {@link android.os.Handler} running on your
+ * application's main thread. This means that you <b>must</b> offload your execution logic to
+ * another thread/handler/{@link android.os.AsyncTask} of your choosing. Not doing so will result
+ * in blocking any future callbacks from the TaskManager - specifically
+ * {@link #onStopTask(android.app.task.TaskParams)}, which is meant to inform you that the
+ * scheduling requirements are no longer being met.</p>
+ */
+public abstract class TaskService extends Service {
+ private static final String TAG = "TaskService";
+
+ /**
+ * Task services must be protected with this permission:
+ *
+ * <pre class="prettyprint">
+ * <service android:name="MyTaskService"
+ * android:permission="android.permission.BIND_TASK_SERVICE" >
+ * ...
+ * </service>
+ * </pre>
+ *
+ * <p>If a task service is declared in the manifest but not protected with this
+ * permission, that service will be ignored by the OS.
+ */
+ public static final String PERMISSION_BIND =
+ "android.permission.BIND_TASK_SERVICE";
+
+ /**
+ * Identifier for a message that will result in a call to
+ * {@link #onStartTask(android.app.task.TaskParams)}.
+ */
+ private final int MSG_EXECUTE_TASK = 0;
+ /**
+ * Message that will result in a call to {@link #onStopTask(android.app.task.TaskParams)}.
+ */
+ private final int MSG_STOP_TASK = 1;
+ /**
+ * Message that the client has completed execution of this task.
+ */
+ private final int MSG_TASK_FINISHED = 2;
+
+ /** Lock object for {@link #mHandler}. */
+ private final Object mHandlerLock = new Object();
+
+ /**
+ * Handler we post tasks to. Responsible for calling into the client logic, and handling the
+ * callback to the system.
+ */
+ @GuardedBy("mHandlerLock")
+ TaskHandler mHandler;
+
+ /** Binder for this service. */
+ ITaskService mBinder = new ITaskService.Stub() {
+ @Override
+ public void startTask(TaskParams taskParams) {
+ ensureHandler();
+ Message m = Message.obtain(mHandler, MSG_EXECUTE_TASK, taskParams);
+ m.sendToTarget();
+ }
+ @Override
+ public void stopTask(TaskParams taskParams) {
+ ensureHandler();
+ Message m = Message.obtain(mHandler, MSG_STOP_TASK, taskParams);
+ m.sendToTarget();
+ }
+ };
+
+ /** @hide */
+ void ensureHandler() {
+ synchronized (mHandlerLock) {
+ if (mHandler == null) {
+ mHandler = new TaskHandler(getMainLooper());
+ }
+ }
+ }
+
+ /**
+ * Runs on application's main thread - callbacks are meant to offboard work to some other
+ * (app-specified) mechanism.
+ * @hide
+ */
+ class TaskHandler extends Handler {
+ TaskHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ final TaskParams params = (TaskParams) msg.obj;
+ switch (msg.what) {
+ case MSG_EXECUTE_TASK:
+ try {
+ TaskService.this.onStartTask(params);
+ } catch (Exception e) {
+ Log.e(TAG, "Error while executing task: " + params.getTaskId());
+ throw new RuntimeException(e);
+ } finally {
+ maybeAckMessageReceived(params, MSG_EXECUTE_TASK);
+ }
+ break;
+ case MSG_STOP_TASK:
+ try {
+ TaskService.this.onStopTask(params);
+ } catch (Exception e) {
+ Log.e(TAG, "Application unable to handle onStopTask.", e);
+ throw new RuntimeException(e);
+ } finally {
+ maybeAckMessageReceived(params, MSG_STOP_TASK);
+ }
+ break;
+ case MSG_TASK_FINISHED:
+ final boolean needsReschedule = (msg.arg2 == 1);
+ ITaskCallback callback = params.getCallback();
+ if (callback != null) {
+ try {
+ callback.taskFinished(params.getTaskId(), needsReschedule);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error reporting task finish to system: binder has gone" +
+ "away.");
+ }
+ } else {
+ Log.e(TAG, "finishTask() called for a nonexistent task id.");
+ }
+ break;
+ default:
+ Log.e(TAG, "Unrecognised message received.");
+ break;
+ }
+ }
+
+ /**
+ * Messages come in on the application's main thread, so rather than run the risk of
+ * waiting for an app that may be doing something foolhardy, we ack to the system after
+ * processing a message. This allows us to throw up an ANR dialogue as quickly as possible.
+ * @param params id of the task we're acking.
+ * @param state Information about what message we're acking.
+ */
+ private void maybeAckMessageReceived(TaskParams params, int state) {
+ final ITaskCallback callback = params.getCallback();
+ final int taskId = params.getTaskId();
+ if (callback != null) {
+ try {
+ if (state == MSG_EXECUTE_TASK) {
+ callback.acknowledgeStartMessage(taskId);
+ } else if (state == MSG_STOP_TASK) {
+ callback.acknowledgeStopMessage(taskId);
+ }
+ } catch(RemoteException e) {
+ Log.e(TAG, "System unreachable for starting task.");
+ }
+ } else {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, state + ": Attempting to ack a task that has already been" +
+ "processed.");
+ }
+ }
+ }
+ }
+
+ /** @hide */
+ public final IBinder onBind(Intent intent) {
+ return mBinder.asBinder();
+ }
+
+ /**
+ * Override this method with the callback logic for your task. Any such logic needs to be
+ * performed on a separate thread, as this function is executed on your application's main
+ * thread.
+ *
+ * @param params Parameters specifying info about this task, including the extras bundle you
+ * optionally provided at task-creation time.
+ */
+ public abstract void onStartTask(TaskParams params);
+
+ /**
+ * This method is called if your task should be stopped even before you've called
+ * {@link #taskFinished(TaskParams, boolean)}.
+ *
+ * <p>This will happen if the requirements specified at schedule time are no longer met. For
+ * example you may have requested WiFi with
+ * {@link android.content.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your
+ * task was executing the user toggled WiFi. Another example is if you had specified
+ * {@link android.content.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
+ * idle maintenance window. You are solely responsible for the behaviour of your application
+ * upon receipt of this message; your app will likely start to misbehave if you ignore it. One
+ * repercussion is that the system will cease to hold a wakelock for you.</p>
+ *
+ * <p>After you've done your clean-up you are still expected to call
+ * {@link #taskFinished(TaskParams, boolean)} this will inform the TaskManager that all is well, and
+ * allow you to reschedule your task as it is probably uncompleted. Until you call
+ * taskFinished() you will not receive any newly scheduled tasks with the given task id as the
+ * TaskManager will consider the task to be in an error state.</p>
+ *
+ * @param params Parameters specifying info about this task.
+ * @return True to indicate to the TaskManager whether you'd like to reschedule this task based
+ * on the criteria provided at task creation-time. False to drop the task. Regardless of the
+ * value returned, your task must stop executing.
+ */
+ public abstract boolean onStopTask(TaskParams params);
+
+ /**
+ * Callback to inform the TaskManager you have completed execution. This can be called from any
+ * thread, as it will ultimately be run on your application's main thread. When the system
+ * receives this message it will release the wakelock being held.
+ * <p>
+ * You can specify post-execution behaviour to the scheduler here with <code>needsReschedule
+ * </code>. This will apply a back-off timer to your task based on the default, or what was
+ * set with {@link android.content.Task.Builder#setBackoffCriteria(long, int)}. The
+ * original requirements are always honoured even for a backed-off task.
+ * Note that a task running in idle mode will not be backed-off. Instead what will happen
+ * is the task will be re-added to the queue and re-executed within a future idle
+ * maintenance window.
+ * </p>
+ *
+ * @param params Parameters specifying system-provided info about this task, this was given to
+ * your application in {@link #onStartTask(TaskParams)}.
+ * @param needsReschedule True if this task is complete, false if you want the TaskManager to
+ * reschedule you.
+ */
+ public final void taskFinished(TaskParams params, boolean needsReschedule) {
+ ensureHandler();
+ Message m = Message.obtain(mHandler, MSG_TASK_FINISHED, params);
+ m.arg2 = needsReschedule ? 1 : 0;
+ m.sendToTarget();
+ }
+} \ No newline at end of file
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index dd3a871..d3e9089 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -181,7 +181,8 @@ public class AppWidgetManager {
* A bundle extra that hints to the AppWidgetProvider the category of host that owns this
* this widget. Can have the value {@link
* AppWidgetProviderInfo#WIDGET_CATEGORY_HOME_SCREEN} or {@link
- * AppWidgetProviderInfo#WIDGET_CATEGORY_KEYGUARD}.
+ * AppWidgetProviderInfo#WIDGET_CATEGORY_KEYGUARD} or {@link
+ * AppWidgetProviderInfo#WIDGET_CATEGORY_RECENTS}.
*/
public static final String OPTION_APPWIDGET_HOST_CATEGORY = "appWidgetCategory";
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 4b33799..8b9c7f0 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -54,6 +54,11 @@ public class AppWidgetProviderInfo implements Parcelable {
public static final int WIDGET_CATEGORY_KEYGUARD = 2;
/**
+ * Indicates that the widget can be displayed within recents.
+ */
+ public static final int WIDGET_CATEGORY_RECENTS = 4;
+
+ /**
* Identity of this AppWidget component. This component should be a {@link
* android.content.BroadcastReceiver}, and it will be sent the AppWidget intents
* {@link android.appwidget as described in the AppWidget package documentation}.
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index a396a05..7f8d0ab 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -512,6 +512,25 @@ public final class BluetoothDevice implements Parcelable {
*/
public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
+ /**
+ * No preferrence of physical transport for GATT connections to remote dual-mode devices
+ * @hide
+ */
+ public static final int TRANSPORT_AUTO = 0;
+
+ /**
+ * Prefer BR/EDR transport for GATT connections to remote dual-mode devices
+ * @hide
+ */
+ public static final int TRANSPORT_BREDR = 1;
+
+ /**
+ * Prefer LE transport for GATT connections to remote dual-mode devices
+ * @hide
+ */
+ public static final int TRANSPORT_LE = 2;
+
+
/**
* Lazy initialization. Guaranteed final after first object constructed, or
* getService() called.
@@ -1216,6 +1235,27 @@ public final class BluetoothDevice implements Parcelable {
*/
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback) {
+ return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO));
+ }
+
+ /**
+ * Connect to GATT Server hosted by this device. Caller acts as GATT client.
+ * The callback is used to deliver results to Caller, such as connection status as well
+ * as any further GATT client operations.
+ * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
+ * GATT client operations.
+ * @param callback GATT callback handler that will receive asynchronous callbacks.
+ * @param autoConnect Whether to directly connect to the remote device (false)
+ * or to automatically connect as soon as the remote
+ * device becomes available (true).
+ * @param transport preferred transport for GATT connections to remote dual-mode devices
+ * {@link BluetoothDevice#TRANSPORT_AUTO} or
+ * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE}
+ * @throws IllegalArgumentException if callback is null
+ * @hide
+ */
+ public BluetoothGatt connectGatt(Context context, boolean autoConnect,
+ BluetoothGattCallback callback, int transport) {
// TODO(Bluetooth) check whether platform support BLE
// Do the check here or in GattServer?
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -1226,10 +1266,11 @@ public final class BluetoothDevice implements Parcelable {
// BLE is not supported
return null;
}
- BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this);
+ BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this, transport);
gatt.connect(autoConnect, callback);
return gatt;
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
+
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index ff3af7c..601d9ee 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -51,6 +51,7 @@ public final class BluetoothGatt implements BluetoothProfile {
private int mConnState;
private final Object mStateLock = new Object();
private Boolean mDeviceBusy = false;
+ private int mTransport;
private static final int CONN_STATE_IDLE = 0;
private static final int CONN_STATE_CONNECTING = 1;
@@ -135,7 +136,7 @@ public final class BluetoothGatt implements BluetoothProfile {
}
try {
mService.clientConnect(mClientIf, mDevice.getAddress(),
- !mAutoConnect); // autoConnect is inverse of "isDirect"
+ !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
@@ -600,10 +601,12 @@ public final class BluetoothGatt implements BluetoothProfile {
}
};
- /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) {
+ /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
+ int transport) {
mContext = context;
mService = iGatt;
mDevice = device;
+ mTransport = transport;
mServices = new ArrayList<BluetoothGattService>();
mConnState = CONN_STATE_IDLE;
@@ -759,7 +762,7 @@ public final class BluetoothGatt implements BluetoothProfile {
public boolean connect() {
try {
mService.clientConnect(mClientIf, mDevice.getAddress(),
- false); // autoConnect is inverse of "isDirect"
+ false, mTransport); // autoConnect is inverse of "isDirect"
return true;
} catch (RemoteException e) {
Log.e(TAG,"",e);
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 0c00c06..34e8605 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -50,6 +50,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
private Object mServerIfLock = new Object();
private int mServerIf;
+ private int mTransport;
private List<BluetoothGattService> mServices;
private static final int CALLBACK_REG_TIMEOUT = 10000;
@@ -269,12 +270,13 @@ public final class BluetoothGattServer implements BluetoothProfile {
/**
* Create a BluetoothGattServer proxy object.
*/
- /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt) {
+ /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt, int transport) {
mContext = context;
mService = iGatt;
mAdapter = BluetoothAdapter.getDefaultAdapter();
mCallback = null;
mServerIf = 0;
+ mTransport = transport;
mServices = new ArrayList<BluetoothGattService>();
}
@@ -401,7 +403,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
try {
mService.serverConnect(mServerIf, device.getAddress(),
- autoConnect ? false : true); // autoConnect is inverse of "isDirect"
+ autoConnect ? false : true,mTransport); // autoConnect is inverse of "isDirect"
} catch (RemoteException e) {
Log.e(TAG,"",e);
return false;
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index 172f3bc..b1618cf3 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -194,6 +194,26 @@ public final class BluetoothManager {
*/
public BluetoothGattServer openGattServer(Context context,
BluetoothGattServerCallback callback) {
+
+ return (openGattServer (context, callback, BluetoothDevice.TRANSPORT_AUTO));
+ }
+
+ /**
+ * Open a GATT Server
+ * The callback is used to deliver results to Caller, such as connection status as well
+ * as the results of any other GATT server operations.
+ * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
+ * to conduct GATT server operations.
+ * @param context App context
+ * @param callback GATT server callback handler that will receive asynchronous callbacks.
+ * @param transport preferred transport for GATT connections to remote dual-mode devices
+ * {@link BluetoothDevice#TRANSPORT_AUTO} or
+ * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE}
+ * @return BluetoothGattServer instance
+ * @hide
+ */
+ public BluetoothGattServer openGattServer(Context context,
+ BluetoothGattServerCallback callback,int transport) {
if (context == null || callback == null) {
throw new IllegalArgumentException("null parameter: " + context + " " + callback);
}
@@ -208,7 +228,7 @@ public final class BluetoothManager {
Log.e(TAG, "Fail to get GATT Server connection");
return null;
}
- BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt);
+ BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt,transport);
Boolean regStatus = mGattServer.registerCallback(callback);
return regStatus? mGattServer : null;
} catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index f532f7c..00fd7ce 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -325,6 +325,7 @@ public final class BluetoothSocket implements Closeable {
}
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ throw new IOException("unable to send RPC: " + e.getMessage());
}
}
diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
index 6dd551e..a0b603e 100644
--- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
+++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
@@ -53,6 +53,9 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker {
private static final boolean DBG = true;
private static final boolean VDBG = true;
+ // Event sent to the mBtdtHandler when DHCP fails so we can tear down the network.
+ private static final int EVENT_NETWORK_FAILED = 1;
+
private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
@@ -315,6 +318,7 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker {
}
if (!success) {
Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
+ mBtdtHandler.obtainMessage(EVENT_NETWORK_FAILED).sendToTarget();
return;
}
mLinkProperties = dhcpResults.linkProperties;
@@ -407,6 +411,10 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker {
if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties);
mBtdt.stopReverseTether();
break;
+ case EVENT_NETWORK_FAILED:
+ if (VDBG) Log.d(TAG, "got EVENT_NETWORK_FAILED");
+ mBtdt.teardown();
+ break;
}
}
}
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 4b28516..ab53fb0 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -18,6 +18,8 @@ package android.bluetooth;
import android.os.ParcelUuid;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.HashSet;
import java.util.UUID;
@@ -76,6 +78,12 @@ public final class BluetoothUuid {
public static final ParcelUuid BASE_UUID =
ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
+ /** Length of bytes for 16 bit UUID */
+ public static final int UUID_BYTES_16_BIT = 2;
+ /** Length of bytes for 32 bit UUID */
+ public static final int UUID_BYTES_32_BIT = 4;
+ /** Length of bytes for 128 bit UUID */
+ public static final int UUID_BYTES_128_BIT = 16;
public static final ParcelUuid[] RESERVED_UUIDS = {
AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
@@ -216,15 +224,60 @@ public final class BluetoothUuid {
}
/**
+ * Parse UUID from bytes. The {@code uuidBytes} can represent a 16-bit, 32-bit or 128-bit UUID,
+ * but the returned UUID is always in 128-bit format.
+ * Note UUID is little endian in Bluetooth.
+ *
+ * @param uuidBytes Byte representation of uuid.
+ * @return {@link ParcelUuid} parsed from bytes.
+ * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed.
+ */
+ public static ParcelUuid parseUuidFrom(byte[] uuidBytes) {
+ if (uuidBytes == null) {
+ throw new IllegalArgumentException("uuidBytes cannot be null");
+ }
+ int length = uuidBytes.length;
+ if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT &&
+ length != UUID_BYTES_128_BIT) {
+ throw new IllegalArgumentException("uuidBytes length invalid - " + length);
+ }
+
+ // Construct a 128 bit UUID.
+ if (length == UUID_BYTES_128_BIT) {
+ ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN);
+ long msb = buf.getLong(8);
+ long lsb = buf.getLong(0);
+ return new ParcelUuid(new UUID(msb, lsb));
+ }
+
+ // For 16 bit and 32 bit UUID we need to convert them to 128 bit value.
+ // 128_bit_value = uuid * 2^96 + BASE_UUID
+ long shortUuid;
+ if (length == UUID_BYTES_16_BIT) {
+ shortUuid = uuidBytes[0] & 0xFF;
+ shortUuid += (uuidBytes[1] & 0xFF) << 8;
+ } else {
+ shortUuid = uuidBytes[0] & 0xFF ;
+ shortUuid += (uuidBytes[1] & 0xFF) << 8;
+ shortUuid += (uuidBytes[2] & 0xFF) << 16;
+ shortUuid += (uuidBytes[3] & 0xFF) << 24;
+ }
+ long msb = BASE_UUID.getUuid().getMostSignificantBits() + (shortUuid << 32);
+ long lsb = BASE_UUID.getUuid().getLeastSignificantBits();
+ return new ParcelUuid(new UUID(msb, lsb));
+ }
+
+ /**
* Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid.
+ *
* @param parcelUuid
* @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise.
*/
public static boolean isShortUuid(ParcelUuid parcelUuid) {
- UUID uuid = parcelUuid.getUuid();
- if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
- return false;
- }
- return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L);
+ UUID uuid = parcelUuid.getUuid();
+ if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
+ return false;
+ }
+ return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L);
}
}
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index c6b5c3d..49b156d 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -35,7 +35,7 @@ interface IBluetoothGatt {
void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback);
void unregisterClient(in int clientIf);
- void clientConnect(in int clientIf, in String address, in boolean isDirect);
+ void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport);
void clientDisconnect(in int clientIf, in String address);
void startAdvertising(in int appIf);
void stopAdvertising();
@@ -77,7 +77,7 @@ interface IBluetoothGatt {
void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback);
void unregisterServer(in int serverIf);
- void serverConnect(in int servertIf, in String address, in boolean isDirect);
+ void serverConnect(in int servertIf, in String address, in boolean isDirect, in int transport);
void serverDisconnect(in int serverIf, in String address);
void beginServiceDeclaration(in int serverIf, in int srvcType,
in int srvcInstanceId, in int minHandles,
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index de223a3..7c625bd 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2377,6 +2377,16 @@ public abstract class Context {
/**
* Use with {@link #getSystemService} to retrieve a
+ * {@link android.service.fingerprint.FingerprintManager} for handling management
+ * of fingerprints.
+ *
+ * @see #getSystemService
+ * @see android.app.FingerprintManager
+ */
+ public static final String FINGERPRINT_SERVICE = "fingerprint";
+
+ /**
+ * Use with {@link #getSystemService} to retrieve a
* {@link android.media.MediaRouter} for controlling and managing
* routing of media.
*
diff --git a/core/java/android/content/Task.java b/core/java/android/content/Task.java
new file mode 100644
index 0000000..ed5ed88
--- /dev/null
+++ b/core/java/android/content/Task.java
@@ -0,0 +1,400 @@
+/*
+ * 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.content;
+
+import android.app.task.TaskService;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container of data passed to the {@link android.content.TaskManager} fully encapsulating the
+ * parameters required to schedule work against the calling application. These are constructed
+ * using the {@link Task.Builder}.
+ */
+public class Task implements Parcelable {
+
+ public interface NetworkType {
+ public final int ANY = 0;
+ public final int UNMETERED = 1;
+ }
+
+ /**
+ * Linear: retry_time(failure_time, t) = failure_time + initial_retry_delay * t, t >= 1
+ * Expon: retry_time(failure_time, t) = failure_time + initial_retry_delay ^ t, t >= 1
+ */
+ public interface BackoffPolicy {
+ public final int LINEAR = 0;
+ public final int EXPONENTIAL = 1;
+ }
+
+ /**
+ * Unique task id associated with this class. This is assigned to your task by the scheduler.
+ */
+ public int getTaskId() {
+ return taskId;
+ }
+
+ /**
+ * Bundle of extras which are returned to your application at execution time.
+ */
+ public Bundle getExtras() {
+ return extras;
+ }
+
+ /**
+ * Name of the service endpoint that will be called back into by the TaskManager.
+ */
+ public String getServiceClassName() {
+ return serviceClassName;
+ }
+
+ /**
+ * Whether this task needs the device to be plugged in.
+ */
+ public boolean isRequireCharging() {
+ return requireCharging;
+ }
+
+ /**
+ * Whether this task needs the device to be in an Idle maintenance window.
+ */
+ public boolean isRequireDeviceIdle() {
+ return requireDeviceIdle;
+ }
+
+ /**
+ * See {@link android.content.Task.NetworkType} for a description of this value.
+ */
+ public int getNetworkCapabilities() {
+ return networkCapabilities;
+ }
+
+ /**
+ * Set for a task that does not recur periodically, to specify a delay after which the task
+ * will be eligible for execution. This value is not set if the task recurs periodically.
+ */
+ public long getMinLatencyMillis() {
+ return minLatencyMillis;
+ }
+
+ /**
+ * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the task recurs
+ * periodically.
+ */
+ public long getMaxExecutionDelayMillis() {
+ return maxExecutionDelayMillis;
+ }
+
+ /**
+ * Track whether this task will repeat with a given period.
+ */
+ public boolean isPeriodic() {
+ return isPeriodic;
+ }
+
+ /**
+ * Set to the interval between occurrences of this task. This value is <b>not</b> set if the
+ * task does not recur periodically.
+ */
+ public long getIntervalMillis() {
+ return intervalMillis;
+ }
+
+ /**
+ * The amount of time the TaskManager will wait before rescheduling a failed task. This value
+ * will be increased depending on the backoff policy specified at task creation time. Defaults
+ * to 5 seconds.
+ */
+ public long getInitialBackoffMillis() {
+ return initialBackoffMillis;
+ }
+
+ /**
+ * See {@link android.content.Task.BackoffPolicy} for an explanation of the values this field
+ * can take. This defaults to exponential.
+ */
+ public int getBackoffPolicy() {
+ return backoffPolicy;
+ }
+
+ private final int taskId;
+ // TODO: Change this to use PersistableBundle when that lands in master.
+ private final Bundle extras;
+ private final String serviceClassName;
+ private final boolean requireCharging;
+ private final boolean requireDeviceIdle;
+ private final int networkCapabilities;
+ private final long minLatencyMillis;
+ private final long maxExecutionDelayMillis;
+ private final boolean isPeriodic;
+ private final long intervalMillis;
+ private final long initialBackoffMillis;
+ private final int backoffPolicy;
+
+ private Task(Parcel in) {
+ taskId = in.readInt();
+ extras = in.readBundle();
+ serviceClassName = in.readString();
+ requireCharging = in.readInt() == 1;
+ requireDeviceIdle = in.readInt() == 1;
+ networkCapabilities = in.readInt();
+ minLatencyMillis = in.readLong();
+ maxExecutionDelayMillis = in.readLong();
+ isPeriodic = in.readInt() == 1;
+ intervalMillis = in.readLong();
+ initialBackoffMillis = in.readLong();
+ backoffPolicy = in.readInt();
+ }
+
+ private Task(Task.Builder b) {
+ taskId = b.mTaskId;
+ extras = new Bundle(b.mExtras);
+ serviceClassName = b.mTaskServiceClassName;
+ requireCharging = b.mRequiresCharging;
+ requireDeviceIdle = b.mRequiresDeviceIdle;
+ networkCapabilities = b.mNetworkCapabilities;
+ minLatencyMillis = b.mMinLatencyMillis;
+ maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
+ isPeriodic = b.mIsPeriodic;
+ intervalMillis = b.mIntervalMillis;
+ initialBackoffMillis = b.mInitialBackoffMillis;
+ backoffPolicy = b.mBackoffPolicy;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(taskId);
+ out.writeBundle(extras);
+ out.writeString(serviceClassName);
+ out.writeInt(requireCharging ? 1 : 0);
+ out.writeInt(requireDeviceIdle ? 1 : 0);
+ out.writeInt(networkCapabilities);
+ out.writeLong(minLatencyMillis);
+ out.writeLong(maxExecutionDelayMillis);
+ out.writeInt(isPeriodic ? 1 : 0);
+ out.writeLong(intervalMillis);
+ out.writeLong(initialBackoffMillis);
+ out.writeInt(backoffPolicy);
+ }
+
+ public static final Creator<Task> CREATOR = new Creator<Task>() {
+ @Override
+ public Task createFromParcel(Parcel in) {
+ return new Task(in);
+ }
+
+ @Override
+ public Task[] newArray(int size) {
+ return new Task[size];
+ }
+ };
+
+ /**
+ * Builder class for constructing {@link Task} objects.
+ */
+ public final class Builder {
+ private int mTaskId;
+ private Bundle mExtras;
+ private String mTaskServiceClassName;
+ // Requirements.
+ private boolean mRequiresCharging;
+ private boolean mRequiresDeviceIdle;
+ private int mNetworkCapabilities;
+ // One-off parameters.
+ private long mMinLatencyMillis;
+ private long mMaxExecutionDelayMillis;
+ // Periodic parameters.
+ private boolean mIsPeriodic;
+ private long mIntervalMillis;
+ // Back-off parameters.
+ private long mInitialBackoffMillis = 5000L;
+ private int mBackoffPolicy = BackoffPolicy.EXPONENTIAL;
+ /** Easy way to track whether the client has tried to set a back-off policy. */
+ private boolean mBackoffPolicySet = false;
+
+ /**
+ * @param taskId Application-provided id for this task. Subsequent calls to cancel, or
+ * tasks created with the same taskId, will update the pre-existing task with
+ * the same id.
+ * @param cls The endpoint that you implement that will receive the callback from the
+ * TaskManager.
+ */
+ public Builder(int taskId, Class<TaskService> cls) {
+ mTaskServiceClassName = cls.getClass().getName();
+ mTaskId = taskId;
+ }
+
+ /**
+ * Set optional extras. This is persisted, so we only allow primitive types.
+ * @param extras Bundle containing extras you want the scheduler to hold on to for you.
+ */
+ public Builder setExtras(Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Set some description of the kind of network capabilities you would like to have. This
+ * will be a parameter defined in {@link android.content.Task.NetworkType}.
+ * Not calling this function means the network is not necessary.
+ * Bear in mind that calling this function defines network as a strict requirement for your
+ * task if the network requested is not available your task will never run. See
+ * {@link #setOverrideDeadline(long)} to change this behaviour.
+ */
+ public Builder setRequiredNetworkCapabilities(int networkCapabilities) {
+ mNetworkCapabilities = networkCapabilities;
+ return this;
+ }
+
+ /*
+ * Specify that to run this task, the device needs to be plugged in. This defaults to
+ * false.
+ * @param requireCharging Whether or not the device is plugged in.
+ */
+ public Builder setRequiresCharging(boolean requiresCharging) {
+ mRequiresCharging = requiresCharging;
+ return this;
+ }
+
+ /**
+ * Specify that to run, the task needs the device to be in idle mode. This defaults to
+ * false.
+ * <p>Idle mode is a loose definition provided by the system, which means that the device
+ * is not in use, and has not been in use for some time. As such, it is a good time to
+ * perform resource heavy tasks. Bear in mind that battery usage will still be attributed
+ * to your application, and surfaced to the user in battery stats.</p>
+ * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance
+ * window.
+ */
+ public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
+ mRequiresDeviceIdle = requiresDeviceIdle;
+ return this;
+ }
+
+ /**
+ * Specify that this task should recur with the provided interval, not more than once per
+ * period. You have no control over when within this interval this task will be executed,
+ * only the guarantee that it will be executed at most once within this interval.
+ * A periodic task will be repeated until the phone is turned off, however it will only be
+ * persisted if the client app has declared the
+ * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission. You can schedule
+ * periodic tasks without this permission, they simply will cease to exist after the phone
+ * restarts.
+ * Setting this function on the builder with {@link #setMinimumLatency(long)} or
+ * {@link #setOverrideDeadline(long)} will result in an error.
+ * @param intervalMillis Millisecond interval for which this task will repeat.
+ */
+ public Builder setPeriodic(long intervalMillis) {
+ mIsPeriodic = true;
+ mIntervalMillis = intervalMillis;
+ return this;
+ }
+
+ /**
+ * Specify that this task should be delayed by the provided amount of time.
+ * Because it doesn't make sense setting this property on a periodic task, doing so will
+ * throw an {@link java.lang.IllegalArgumentException} when
+ * {@link android.content.Task.Builder#build()} is called.
+ * @param minLatencyMillis Milliseconds before which this task will not be considered for
+ * execution.
+ */
+ public Builder setMinimumLatency(long minLatencyMillis) {
+ mMinLatencyMillis = minLatencyMillis;
+ return this;
+ }
+
+ /**
+ * Set deadline which is the maximum scheduling latency. The task will be run by this
+ * deadline even if other requirements are not met. Because it doesn't make sense setting
+ * this property on a periodic task, doing so will throw an
+ * {@link java.lang.IllegalArgumentException} when
+ * {@link android.content.Task.Builder#build()} is called.
+ */
+ public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
+ mMaxExecutionDelayMillis = maxExecutionDelayMillis;
+ return this;
+ }
+
+ /**
+ * Set up the back-off/retry policy.
+ * This defaults to some respectable values: {5 seconds, Exponential}. We cap back-off at
+ * 1hr.
+ * Note that trying to set a backoff criteria for a task with
+ * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build().
+ * This is because back-off typically does not make sense for these types of tasks. See
+ * {@link android.app.task.TaskService#taskFinished(android.app.task.TaskParams, boolean)}
+ * for more description of the return value for the case of a task executing while in idle
+ * mode.
+ * @param initialBackoffMillis Millisecond time interval to wait initially when task has
+ * failed.
+ * @param backoffPolicy is one of {@link BackoffPolicy}
+ */
+ public Builder setBackoffCriteria(long initialBackoffMillis, int backoffPolicy) {
+ mBackoffPolicySet = true;
+ mInitialBackoffMillis = initialBackoffMillis;
+ mBackoffPolicy = backoffPolicy;
+ return this;
+ }
+
+ /**
+ * @return The task object to hand to the TaskManager. This object is immutable.
+ */
+ public Task build() {
+ // Check that extras bundle only contains primitive types.
+ try {
+ for (String key : extras.keySet()) {
+ Object value = extras.get(key);
+ if (value == null) continue;
+ if (value instanceof Long) continue;
+ if (value instanceof Integer) continue;
+ if (value instanceof Boolean) continue;
+ if (value instanceof Float) continue;
+ if (value instanceof Double) continue;
+ if (value instanceof String) continue;
+ throw new IllegalArgumentException("Unexpected value type: "
+ + value.getClass().getName());
+ }
+ } catch (IllegalArgumentException e) {
+ throw e;
+ } catch (RuntimeException exc) {
+ throw new IllegalArgumentException("error unparcelling Bundle", exc);
+ }
+ // Check that a deadline was not set on a periodic task.
+ if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) {
+ throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
+ "periodic task.");
+ }
+ if (mIsPeriodic && (mMinLatencyMillis != 0L)) {
+ throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
+ "periodic task");
+ }
+ if (mBackoffPolicySet && mRequiresDeviceIdle) {
+ throw new IllegalArgumentException("An idle mode task will not respect any" +
+ " back-off policy, so calling setBackoffCriteria with" +
+ " setRequiresDeviceIdle is an error.");
+ }
+ return new Task(this);
+ }
+ }
+
+}
diff --git a/core/java/android/content/TaskManager.java b/core/java/android/content/TaskManager.java
new file mode 100644
index 0000000..d28d78a
--- /dev/null
+++ b/core/java/android/content/TaskManager.java
@@ -0,0 +1,66 @@
+/*
+ * 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.content;
+
+import java.util.List;
+
+/**
+ * Class for scheduling various types of tasks with the scheduling framework on the device.
+ *
+ * Get an instance of this class through {@link Context#getSystemService(String)}.
+ */
+public abstract class TaskManager {
+ /*
+ * Returned from {@link #schedule(Task)} when an invalid parameter was supplied. This can occur
+ * if the run-time for your task is too short, or perhaps the system can't resolve the
+ * requisite {@link TaskService} in your package.
+ */
+ static final int RESULT_INVALID_PARAMETERS = -1;
+ /**
+ * Returned from {@link #schedule(Task)} if this application has made too many requests for
+ * work over too short a time.
+ */
+ // TODO: Determine if this is necessary.
+ static final int RESULT_OVER_QUOTA = -2;
+
+ /*
+ * @param task The task you wish scheduled. See {@link Task#TaskBuilder} for more detail on
+ * the sorts of tasks you can schedule.
+ * @return If >0, this int corresponds to the taskId of the successfully scheduled task.
+ * Otherwise you have to compare the return value to the error codes defined in this class.
+ */
+ public abstract int schedule(Task task);
+
+ /**
+ * Cancel a task that is pending in the TaskManager.
+ * @param taskId unique identifier for this task. Obtain this value from the tasks returned by
+ * {@link #getAllPendingTasks()}.
+ * @return
+ */
+ public abstract void cancel(int taskId);
+
+ /**
+ * Cancel all tasks that have been registered with the TaskManager by this package.
+ */
+ public abstract void cancelAll();
+
+ /**
+ * @return a list of all the tasks registered by this package that have not yet been executed.
+ */
+ public abstract List<Task> getAllPendingTasks();
+
+}
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 796b113..0acf043 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -35,4 +35,6 @@ interface ILauncherApps {
ResolveInfo resolveActivity(in Intent intent, in UserHandle user);
void startActivityAsUser(in ComponentName component, in Rect sourceBounds,
in Bundle opts, in UserHandle user);
+ boolean isPackageEnabled(String packageName, in UserHandle user);
+ boolean isActivityEnabled(in ComponentName component, in UserHandle user);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 488e25f..03eb50f 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -111,6 +111,8 @@ interface IPackageManager {
ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags, int userId);
+ boolean canForwardTo(in Intent intent, String resolvedType, int userIdFrom, int userIdDest);
+
List<ResolveInfo> queryIntentActivities(in Intent intent,
String resolvedType, int flags, int userId);
@@ -245,6 +247,11 @@ interface IPackageManager {
void clearPackagePersistentPreferredActivities(String packageName, int userId);
+ void addForwardingIntentFilter(in IntentFilter filter, boolean removable, int userIdOrig,
+ int userIdDest);
+
+ void clearForwardingIntentFilters(int userIdOrig);
+
/**
* Report the set of 'Home' activity candidates, plus (if any) which of them
* is the current "always use this one" setting.
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 5187181..8025b60 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -187,6 +187,39 @@ public class LauncherApps {
}
/**
+ * Checks if the package is installed and enabled for a profile.
+ *
+ * @param packageName The package to check.
+ * @param user The UserHandle of the profile.
+ *
+ * @return true if the package exists and is enabled.
+ */
+ public boolean isPackageEnabledForProfile(String packageName, UserHandle user) {
+ try {
+ return mService.isPackageEnabled(packageName, user);
+ } catch (RemoteException re) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the activity exists and it enabled for a profile.
+ *
+ * @param component The activity to check.
+ * @param user The UserHandle of the profile.
+ *
+ * @return true if the activity exists and is enabled.
+ */
+ public boolean isActivityEnabledForProfile(ComponentName component, UserHandle user) {
+ try {
+ return mService.isActivityEnabled(component, user);
+ } catch (RemoteException re) {
+ return false;
+ }
+ }
+
+
+ /**
* Adds a listener for changes to packages in current and managed profiles.
*
* @param listener The listener to add.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 484a2a1..1a003ff 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3507,4 +3507,26 @@ public abstract class PackageManager {
return Environment.getDataDirectory().toString() + "/user/" + userId
+ "/" + packageName;
}
+
+ /**
+ * Adds a forwarding intent filter. After calling this method all intents sent from the user
+ * with id userIdOrig can also be be resolved by activities in the user with id userIdDest if
+ * they match the specified intent filter.
+ * @param filter the {@link IntentFilter} the intent has to match to be forwarded
+ * @param removable if set to false, {@link clearForwardingIntents} will not remove this intent
+ * filter
+ * @param userIdOrig user from which the intent can be forwarded
+ * @param userIdDest user to which the intent can be forwarded
+ * @hide
+ */
+ public abstract void addForwardingIntentFilter(IntentFilter filter, boolean removable,
+ int userIdOrig, int userIdDest);
+
+ /**
+ * Clearing all removable {@link ForwardingIntentFilter}s that are set with the given user as
+ * the origin.
+ * @param userIdOrig user from which the intent can be forwarded
+ * @hide
+ */
+ public abstract void clearForwardingIntentFilters(int userIdOrig);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 8f19f01..080b37b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -459,7 +459,7 @@ public class PackageParser {
return pi;
}
- private Certificate[] loadCertificates(StrictJarFile jarFile, ZipEntry je,
+ private Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry je,
byte[] readBuffer) {
try {
// We must read the stream for the JarEntry to retrieve
@@ -469,7 +469,7 @@ public class PackageParser {
// not using
}
is.close();
- return je != null ? jarFile.getCertificates(je) : null;
+ return je != null ? jarFile.getCertificateChains(je) : null;
} catch (IOException e) {
Slog.w(TAG, "Exception reading " + je.getName() + " in " + jarFile, e);
} catch (RuntimeException e) {
@@ -632,7 +632,7 @@ public class PackageParser {
try {
StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath);
- Certificate[] certs = null;
+ Certificate[][] certs = null;
if ((flags&PARSE_IS_SYSTEM) != 0) {
// If this package comes from the system image, then we
@@ -656,8 +656,8 @@ public class PackageParser {
final int N = certs.length;
for (int i=0; i<N; i++) {
Slog.i(TAG, " Public key: "
- + certs[i].getPublicKey().getEncoded()
- + " " + certs[i].getPublicKey());
+ + certs[i][0].getPublicKey().getEncoded()
+ + " " + certs[i][0].getPublicKey());
}
}
}
@@ -677,7 +677,7 @@ public class PackageParser {
ManifestDigest.fromInputStream(jarFile.getInputStream(je));
}
- final Certificate[] localCerts = loadCertificates(jarFile, je, readBuffer);
+ final Certificate[][] localCerts = loadCertificates(jarFile, je, readBuffer);
if (DEBUG_JAR) {
Slog.i(TAG, "File " + mArchiveSourcePath + " entry " + je.getName()
+ ": certs=" + certs + " ("
@@ -726,8 +726,7 @@ public class PackageParser {
final int N = certs.length;
pkg.mSignatures = new Signature[certs.length];
for (int i=0; i<N; i++) {
- pkg.mSignatures[i] = new Signature(
- certs[i].getEncoded());
+ pkg.mSignatures[i] = new Signature(certs[i]);
}
} else {
Slog.e(TAG, "Package " + pkg.packageName
@@ -739,7 +738,7 @@ public class PackageParser {
// Add the signing KeySet to the system
pkg.mSigningKeys = new HashSet<PublicKey>();
for (int i=0; i < certs.length; i++) {
- pkg.mSigningKeys.add(certs[i].getPublicKey());
+ pkg.mSigningKeys.add(certs[i][0].getPublicKey());
}
} catch (CertificateEncodingException e) {
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index 752bf8b..f4e7dc3 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -25,6 +25,7 @@ import java.io.ByteArrayInputStream;
import java.lang.ref.SoftReference;
import java.security.PublicKey;
import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
@@ -38,12 +39,28 @@ public class Signature implements Parcelable {
private int mHashCode;
private boolean mHaveHashCode;
private SoftReference<String> mStringRef;
+ private Certificate[] mCertificateChain;
/**
* Create Signature from an existing raw byte array.
*/
public Signature(byte[] signature) {
mSignature = signature.clone();
+ mCertificateChain = null;
+ }
+
+ /**
+ * Create signature from a certificate chain. Used for backward
+ * compatibility.
+ *
+ * @throws CertificateEncodingException
+ * @hide
+ */
+ public Signature(Certificate[] certificateChain) throws CertificateEncodingException {
+ mSignature = certificateChain[0].getEncoded();
+ if (certificateChain.length > 1) {
+ mCertificateChain = Arrays.copyOfRange(certificateChain, 1, certificateChain.length);
+ }
}
private static final int parseHexDigit(int nibble) {
@@ -156,6 +173,29 @@ public class Signature implements Parcelable {
return cert.getPublicKey();
}
+ /**
+ * Used for compatibility code that needs to check the certificate chain
+ * during upgrades.
+ *
+ * @throws CertificateEncodingException
+ * @hide
+ */
+ public Signature[] getChainSignatures() throws CertificateEncodingException {
+ if (mCertificateChain == null) {
+ return new Signature[] { this };
+ }
+
+ Signature[] chain = new Signature[1 + mCertificateChain.length];
+ chain[0] = this;
+
+ int i = 1;
+ for (Certificate c : mCertificateChain) {
+ chain[i++] = new Signature(c.getEncoded());
+ }
+
+ return chain;
+ }
+
@Override
public boolean equals(Object obj) {
try {
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index f53aa4c..c0383a3 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -27,8 +27,8 @@ import android.os.UserHandle;
*/
public class UserInfo implements Parcelable {
- /** 6 bits for user type */
- public static final int FLAG_MASK_USER_TYPE = 0x0000003F;
+ /** 8 bits for user type */
+ public static final int FLAG_MASK_USER_TYPE = 0x000000FF;
/**
* *************************** NOTE ***************************
@@ -70,6 +70,11 @@ public class UserInfo implements Parcelable {
*/
public static final int FLAG_MANAGED_PROFILE = 0x00000020;
+ /**
+ * Indicates that this user is disabled.
+ */
+ public static final int FLAG_DISABLED = 0x00000040;
+
public static final int NO_PROFILE_GROUP_ID = -1;
@@ -117,6 +122,10 @@ public class UserInfo implements Parcelable {
return (flags & FLAG_MANAGED_PROFILE) == FLAG_MANAGED_PROFILE;
}
+ public boolean isEnabled() {
+ return (flags & FLAG_DISABLED) != FLAG_DISABLED;
+ }
+
/**
* @return true if this user can be switched to.
**/
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 4879c23..499de17 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -2308,8 +2308,8 @@ public class Resources {
*/
private Drawable loadDrawableForCookie(TypedValue value, int id, Theme theme) {
if (value.string == null) {
- throw new NotFoundException(
- "Resource is not a Drawable (color or path): " + value);
+ throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+ + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
}
final String file = value.string.toString();
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 528e119..28309d7 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -372,7 +372,6 @@ public final class CameraCharacteristics extends CameraMetadata {
/**
* <p>Optional. Hyperfocal distance for this lens.</p>
- * <p>If the lens is fixed focus, the camera device will report 0.</p>
* <p>If the lens is not fixed focus, the camera device will report this
* field when {@link CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION android.lens.info.focusDistanceCalibration} is APPROXIMATE or CALIBRATED.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
@@ -455,22 +454,25 @@ public final class CameraCharacteristics extends CameraMetadata {
* <p>The maximum numbers of different types of output streams
* that can be configured and used simultaneously by a camera device.</p>
* <p>This is a 3 element tuple that contains the max number of output simultaneous
- * streams for raw sensor, processed (and uncompressed), and JPEG formats respectively.
- * For example, if max raw sensor format output stream number is 1, max YUV streams
+ * streams for raw sensor, processed (but not stalling), and processed (and stalling)
+ * formats respectively. For example, assuming that JPEG is typically a processed and
+ * stalling stream, if max raw sensor format output stream number is 1, max YUV streams
* number is 3, and max JPEG stream number is 2, then this tuple should be <code>(1, 3, 2)</code>.</p>
* <p>This lists the upper bound of the number of output streams supported by
* the camera device. Using more streams simultaneously may require more hardware and
* CPU resources that will consume more power. The image format for a output stream can
- * be any supported format provided by {@link CameraCharacteristics#SCALER_AVAILABLE_FORMATS android.scaler.availableFormats}. The formats
- * defined in {@link CameraCharacteristics#SCALER_AVAILABLE_FORMATS android.scaler.availableFormats} can be catergorized into the 3 stream types
- * as below:</p>
+ * be any supported format provided by {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS android.scaler.availableStreamConfigurations}.
+ * The formats defined in {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS android.scaler.availableStreamConfigurations} can be catergorized
+ * into the 3 stream types as below:</p>
* <ul>
- * <li>JPEG-compressed format: BLOB.</li>
- * <li>Raw formats: RAW_SENSOR and RAW_OPAQUE.</li>
- * <li>processed, uncompressed formats: YCbCr_420_888, YCrCb_420_SP, YV12.</li>
+ * <li>Processed (but stalling): any non-RAW format with a stallDurations &gt; 0.
+ * Typically JPEG format (ImageFormat#JPEG).</li>
+ * <li>Raw formats: ImageFormat#RAW_SENSOR and ImageFormat#RAW_OPAQUE.</li>
+ * <li>Processed (but not-stalling): any non-RAW format without a stall duration.
+ * Typically ImageFormat#YUV_420_888, ImageFormat#NV21, ImageFormat#YV12.</li>
* </ul>
*
- * @see CameraCharacteristics#SCALER_AVAILABLE_FORMATS
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS
*/
public static final Key<int[]> REQUEST_MAX_NUM_OUTPUT_STREAMS =
new Key<int[]>("android.request.maxNumOutputStreams", int[].class);
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index bb290af..9d0e0e1 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -192,8 +192,9 @@ public interface CameraDevice extends AutoCloseable {
*
* <p>The camera device will query each Surface's size and formats upon this
* call, so they must be set to a valid setting at this time (in particular:
- * if the format is user-visible, it must be one of android.scaler.availableFormats;
- * and the size must be one of android.scaler.available[Processed|Jpeg]Sizes).</p>
+ * if the format is user-visible, it must be one of
+ * {@link StreamConfigurationMap#getOutputFormats}; and the size must be one of
+ * {@link StreamConfigurationMap#getOutputSizes(int)}).</p>
*
* <p>When this method is called with valid Surfaces, the device will transition to the {@link
* StateListener#onBusy busy state}. Once configuration is complete, the device will transition
@@ -239,6 +240,9 @@ public interface CameraDevice extends AutoCloseable {
* @see StateListener#onUnconfigured
* @see #stopRepeating
* @see #flush
+ * @see StreamConfigurationMap#getOutputFormats()
+ * @see StreamConfigurationMap#getOutputSizes(int)
+ * @see StreamConfigurationMap#getOutputSizes(Class)
*/
public void configureOutputs(List<Surface> outputs) throws CameraAccessException;
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index ba8db3a..6e38a22 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1295,6 +1295,20 @@ public abstract class CameraMetadata {
public static final int CONTROL_SCENE_MODE_BARCODE = 16;
//
+ // Enumeration values for CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
+ //
+
+ /**
+ * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
+ */
+ public static final int CONTROL_VIDEO_STABILIZATION_MODE_OFF = 0;
+
+ /**
+ * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
+ */
+ public static final int CONTROL_VIDEO_STABILIZATION_MODE_ON = 1;
+
+ //
// Enumeration values for CaptureRequest#EDGE_MODE
//
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index c4e342c..f161f3a 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -831,9 +831,11 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable {
* stabilized</p>
*
* @see CaptureRequest#SCALER_CROP_REGION
+ * @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF
+ * @see #CONTROL_VIDEO_STABILIZATION_MODE_ON
*/
- public static final Key<Boolean> CONTROL_VIDEO_STABILIZATION_MODE =
- new Key<Boolean>("android.control.videoStabilizationMode", boolean.class);
+ public static final Key<Integer> CONTROL_VIDEO_STABILIZATION_MODE =
+ new Key<Integer>("android.control.videoStabilizationMode", int.class);
/**
* <p>Operation mode for edge
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index d8981c8..1d2d0e9 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -124,6 +124,58 @@ public final class CaptureResult extends CameraMetadata {
/**
+ * <p>The mode control selects how the image data is converted from the
+ * sensor's native color into linear sRGB color.</p>
+ * <p>When auto-white balance is enabled with {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, this
+ * control is overridden by the AWB routine. When AWB is disabled, the
+ * application controls how the color mapping is performed.</p>
+ * <p>We define the expected processing pipeline below. For consistency
+ * across devices, this is always the case with TRANSFORM_MATRIX.</p>
+ * <p>When either FULL or HIGH_QUALITY is used, the camera device may
+ * do additional processing but {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and
+ * {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} will still be provided by the
+ * camera device (in the results) and be roughly correct.</p>
+ * <p>Switching to TRANSFORM_MATRIX and using the data provided from
+ * FAST or HIGH_QUALITY will yield a picture with the same white point
+ * as what was produced by the camera device in the earlier frame.</p>
+ * <p>The expected processing pipeline is as follows:</p>
+ * <p><img alt="White balance processing pipeline" src="../../../../images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png" /></p>
+ * <p>The white balance is encoded by two values, a 4-channel white-balance
+ * gain vector (applied in the Bayer domain), and a 3x3 color transform
+ * matrix (applied after demosaic).</p>
+ * <p>The 4-channel white-balance gains are defined as:</p>
+ * <pre><code>{@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} = [ R G_even G_odd B ]
+ * </code></pre>
+ * <p>where <code>G_even</code> is the gain for green pixels on even rows of the
+ * output, and <code>G_odd</code> is the gain for green pixels on the odd rows.
+ * These may be identical for a given camera device implementation; if
+ * the camera device does not support a separate gain for even/odd green
+ * channels, it will use the <code>G_even</code> value, and write <code>G_odd</code> equal to
+ * <code>G_even</code> in the output result metadata.</p>
+ * <p>The matrices for color transforms are defined as a 9-entry vector:</p>
+ * <pre><code>{@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} = [ I0 I1 I2 I3 I4 I5 I6 I7 I8 ]
+ * </code></pre>
+ * <p>which define a transform from input sensor colors, <code>P_in = [ r g b ]</code>,
+ * to output linear sRGB, <code>P_out = [ r' g' b' ]</code>,</p>
+ * <p>with colors as follows:</p>
+ * <pre><code>r' = I0r + I1g + I2b
+ * g' = I3r + I4g + I5b
+ * b' = I6r + I7g + I8b
+ * </code></pre>
+ * <p>Both the input and output value ranges must match. Overflow/underflow
+ * values are clipped to fit within the range.</p>
+ *
+ * @see CaptureRequest#COLOR_CORRECTION_GAINS
+ * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
+ * @see CaptureRequest#CONTROL_AWB_MODE
+ * @see #COLOR_CORRECTION_MODE_TRANSFORM_MATRIX
+ * @see #COLOR_CORRECTION_MODE_FAST
+ * @see #COLOR_CORRECTION_MODE_HIGH_QUALITY
+ */
+ public static final Key<Integer> COLOR_CORRECTION_MODE =
+ new Key<Integer>("android.colorCorrection.mode", int.class);
+
+ /**
* <p>A color transform matrix to use to transform
* from sensor RGB color space to output linear sRGB color space</p>
* <p>This matrix is either set by the camera device when the request
@@ -176,6 +228,82 @@ public final class CaptureResult extends CameraMetadata {
new Key<Integer>("android.control.aePrecaptureId", int.class);
/**
+ * <p>The desired setting for the camera device's auto-exposure
+ * algorithm's antibanding compensation.</p>
+ * <p>Some kinds of lighting fixtures, such as some fluorescent
+ * lights, flicker at the rate of the power supply frequency
+ * (60Hz or 50Hz, depending on country). While this is
+ * typically not noticeable to a person, it can be visible to
+ * a camera device. If a camera sets its exposure time to the
+ * wrong value, the flicker may become visible in the
+ * viewfinder as flicker or in a final captured image, as a
+ * set of variable-brightness bands across the image.</p>
+ * <p>Therefore, the auto-exposure routines of camera devices
+ * include antibanding routines that ensure that the chosen
+ * exposure value will not cause such banding. The choice of
+ * exposure time depends on the rate of flicker, which the
+ * camera device can detect automatically, or the expected
+ * rate can be selected by the application using this
+ * control.</p>
+ * <p>A given camera device may not support all of the possible
+ * options for the antibanding mode. The
+ * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_ANTIBANDING_MODES android.control.aeAvailableAntibandingModes} key contains
+ * the available modes for a given camera device.</p>
+ * <p>The default mode is AUTO, which must be supported by all
+ * camera devices.</p>
+ * <p>If manual exposure control is enabled (by setting
+ * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} to OFF),
+ * then this setting has no effect, and the application must
+ * ensure it selects exposure times that do not cause banding
+ * issues. The {@link CaptureResult#STATISTICS_SCENE_FLICKER android.statistics.sceneFlicker} key can assist
+ * the application in this.</p>
+ *
+ * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_ANTIBANDING_MODES
+ * @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureRequest#CONTROL_MODE
+ * @see CaptureResult#STATISTICS_SCENE_FLICKER
+ * @see #CONTROL_AE_ANTIBANDING_MODE_OFF
+ * @see #CONTROL_AE_ANTIBANDING_MODE_50HZ
+ * @see #CONTROL_AE_ANTIBANDING_MODE_60HZ
+ * @see #CONTROL_AE_ANTIBANDING_MODE_AUTO
+ */
+ public static final Key<Integer> CONTROL_AE_ANTIBANDING_MODE =
+ new Key<Integer>("android.control.aeAntibandingMode", int.class);
+
+ /**
+ * <p>Adjustment to AE target image
+ * 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>
+ */
+ public static final Key<Integer> CONTROL_AE_EXPOSURE_COMPENSATION =
+ new Key<Integer>("android.control.aeExposureCompensation", int.class);
+
+ /**
+ * <p>Whether AE is currently locked to its latest
+ * calculated values.</p>
+ * <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>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})
+ * parameters. The flash may be fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}
+ * is ON_AUTO_FLASH/ON_AUTO_FLASH_REDEYE and the scene is too dark. If the
+ * {@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_MODE
+ * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
+ * @see CaptureResult#CONTROL_AE_STATE
+ * @see CaptureRequest#SENSOR_EXPOSURE_TIME
+ * @see CaptureRequest#SENSOR_SENSITIVITY
+ */
+ public static final Key<Boolean> CONTROL_AE_LOCK =
+ new Key<Boolean>("android.control.aeLock", boolean.class);
+
+ /**
* <p>The desired mode for the camera device's
* auto-exposure routine.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} is
@@ -237,6 +365,35 @@ public final class CaptureResult extends CameraMetadata {
new Key<int[]>("android.control.aeRegions", int[].class);
/**
+ * <p>Range over which fps can be adjusted to
+ * maintain exposure</p>
+ * <p>Only constrains AE algorithm, not manual control
+ * of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}</p>
+ *
+ * @see CaptureRequest#SENSOR_EXPOSURE_TIME
+ */
+ public static final Key<int[]> CONTROL_AE_TARGET_FPS_RANGE =
+ new Key<int[]>("android.control.aeTargetFpsRange", int[].class);
+
+ /**
+ * <p>Whether the camera device will trigger a precapture
+ * metering sequence when it processes this request.</p>
+ * <p>This entry is normally set to IDLE, or is not
+ * included at all in the request settings. When included and
+ * set to START, the camera device will trigger the autoexposure
+ * precapture metering sequence.</p>
+ * <p>The effect of AE precapture trigger depends on the current
+ * AE mode and state; see {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture
+ * state transition details.</p>
+ *
+ * @see CaptureResult#CONTROL_AE_STATE
+ * @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE
+ * @see #CONTROL_AE_PRECAPTURE_TRIGGER_START
+ */
+ public static final Key<Integer> CONTROL_AE_PRECAPTURE_TRIGGER =
+ new Key<Integer>("android.control.aePrecaptureTrigger", int.class);
+
+ /**
* <p>Current state of AE algorithm</p>
* <p>Switching between or enabling AE modes ({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}) always
* resets the AE state to INACTIVE. Similarly, switching between {@link CaptureRequest#CONTROL_MODE android.control.mode},
@@ -481,6 +638,24 @@ public final class CaptureResult extends CameraMetadata {
new Key<int[]>("android.control.afRegions", int[].class);
/**
+ * <p>Whether the camera device will trigger autofocus for this request.</p>
+ * <p>This entry is normally set to IDLE, or is not
+ * included at all in the request settings.</p>
+ * <p>When included and set to START, the camera device will trigger the
+ * autofocus algorithm. If autofocus is disabled, this trigger has no effect.</p>
+ * <p>When set to CANCEL, the camera device will cancel any active trigger,
+ * and return to its initial AF state.</p>
+ * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what that means for each AF mode.</p>
+ *
+ * @see CaptureResult#CONTROL_AF_STATE
+ * @see #CONTROL_AF_TRIGGER_IDLE
+ * @see #CONTROL_AF_TRIGGER_START
+ * @see #CONTROL_AF_TRIGGER_CANCEL
+ */
+ public static final Key<Integer> CONTROL_AF_TRIGGER =
+ new Key<Integer>("android.control.afTrigger", int.class);
+
+ /**
* <p>Current state of AF algorithm.</p>
* <p>Switching between or enabling AF modes ({@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}) always
* resets the AF state to INACTIVE. Similarly, switching between {@link CaptureRequest#CONTROL_MODE android.control.mode},
@@ -889,6 +1064,16 @@ public final class CaptureResult extends CameraMetadata {
new Key<Integer>("android.control.afTriggerId", int.class);
/**
+ * <p>Whether AWB is currently locked to its
+ * latest calculated values.</p>
+ * <p>Note that AWB lock is only meaningful for AUTO
+ * mode; in other modes, AWB is already fixed to a specific
+ * setting.</p>
+ */
+ public static final Key<Boolean> CONTROL_AWB_LOCK =
+ new Key<Boolean>("android.control.awbLock", boolean.class);
+
+ /**
* <p>Whether AWB is currently setting the color
* transform fields, and what its illumination target
* is.</p>
@@ -948,6 +1133,30 @@ public final class CaptureResult extends CameraMetadata {
new Key<int[]>("android.control.awbRegions", int[].class);
/**
+ * <p>Information to the camera device 3A (auto-exposure,
+ * auto-focus, auto-white balance) routines about the purpose
+ * of this capture, to help the camera device to decide optimal 3A
+ * strategy.</p>
+ * <p>This control (except for MANUAL) is only effective if
+ * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p>
+ * <p>ZERO_SHUTTER_LAG must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
+ * contains ZSL. MANUAL must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
+ * contains MANUAL_SENSOR.</p>
+ *
+ * @see CaptureRequest#CONTROL_MODE
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ * @see #CONTROL_CAPTURE_INTENT_CUSTOM
+ * @see #CONTROL_CAPTURE_INTENT_PREVIEW
+ * @see #CONTROL_CAPTURE_INTENT_STILL_CAPTURE
+ * @see #CONTROL_CAPTURE_INTENT_VIDEO_RECORD
+ * @see #CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT
+ * @see #CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG
+ * @see #CONTROL_CAPTURE_INTENT_MANUAL
+ */
+ public static final Key<Integer> CONTROL_CAPTURE_INTENT =
+ new Key<Integer>("android.control.captureIntent", int.class);
+
+ /**
* <p>Current state of AWB algorithm</p>
* <p>Switching between or enabling AWB modes ({@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}) always
* resets the AWB state to INACTIVE. Similarly, switching between {@link CaptureRequest#CONTROL_MODE android.control.mode},
@@ -1078,6 +1287,31 @@ public final class CaptureResult extends CameraMetadata {
new Key<Integer>("android.control.awbState", int.class);
/**
+ * <p>A special color effect to apply.</p>
+ * <p>When this mode is set, a color effect will be applied
+ * to images produced by the camera device. The interpretation
+ * and implementation of these color effects is left to the
+ * implementor of the camera device, and should not be
+ * depended on to be consistent (or present) across all
+ * devices.</p>
+ * <p>A color effect will only be applied if
+ * {@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF.</p>
+ *
+ * @see CaptureRequest#CONTROL_MODE
+ * @see #CONTROL_EFFECT_MODE_OFF
+ * @see #CONTROL_EFFECT_MODE_MONO
+ * @see #CONTROL_EFFECT_MODE_NEGATIVE
+ * @see #CONTROL_EFFECT_MODE_SOLARIZE
+ * @see #CONTROL_EFFECT_MODE_SEPIA
+ * @see #CONTROL_EFFECT_MODE_POSTERIZE
+ * @see #CONTROL_EFFECT_MODE_WHITEBOARD
+ * @see #CONTROL_EFFECT_MODE_BLACKBOARD
+ * @see #CONTROL_EFFECT_MODE_AQUA
+ */
+ public static final Key<Integer> CONTROL_EFFECT_MODE =
+ new Key<Integer>("android.control.effectMode", int.class);
+
+ /**
* <p>Overall mode of 3A control
* routines.</p>
* <p>High-level 3A control. When set to OFF, all 3A control
@@ -1106,6 +1340,57 @@ public final class CaptureResult extends CameraMetadata {
new Key<Integer>("android.control.mode", int.class);
/**
+ * <p>A camera mode optimized for conditions typical in a particular
+ * capture setting.</p>
+ * <p>This is the mode that that is active when
+ * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code>. Aside from FACE_PRIORITY,
+ * these modes will disable {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode},
+ * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, and {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} while in use.</p>
+ * <p>The interpretation and implementation of these scene modes is left
+ * to the implementor of the camera device. Their behavior will not be
+ * consistent across all devices, and any given device may only implement
+ * a subset of these modes.</p>
+ *
+ * @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureRequest#CONTROL_AF_MODE
+ * @see CaptureRequest#CONTROL_AWB_MODE
+ * @see CaptureRequest#CONTROL_MODE
+ * @see #CONTROL_SCENE_MODE_DISABLED
+ * @see #CONTROL_SCENE_MODE_FACE_PRIORITY
+ * @see #CONTROL_SCENE_MODE_ACTION
+ * @see #CONTROL_SCENE_MODE_PORTRAIT
+ * @see #CONTROL_SCENE_MODE_LANDSCAPE
+ * @see #CONTROL_SCENE_MODE_NIGHT
+ * @see #CONTROL_SCENE_MODE_NIGHT_PORTRAIT
+ * @see #CONTROL_SCENE_MODE_THEATRE
+ * @see #CONTROL_SCENE_MODE_BEACH
+ * @see #CONTROL_SCENE_MODE_SNOW
+ * @see #CONTROL_SCENE_MODE_SUNSET
+ * @see #CONTROL_SCENE_MODE_STEADYPHOTO
+ * @see #CONTROL_SCENE_MODE_FIREWORKS
+ * @see #CONTROL_SCENE_MODE_SPORTS
+ * @see #CONTROL_SCENE_MODE_PARTY
+ * @see #CONTROL_SCENE_MODE_CANDLELIGHT
+ * @see #CONTROL_SCENE_MODE_BARCODE
+ */
+ public static final Key<Integer> CONTROL_SCENE_MODE =
+ new Key<Integer>("android.control.sceneMode", int.class);
+
+ /**
+ * <p>Whether video stabilization is
+ * active</p>
+ * <p>If enabled, video stabilization can modify the
+ * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream
+ * stabilized</p>
+ *
+ * @see CaptureRequest#SCALER_CROP_REGION
+ * @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF
+ * @see #CONTROL_VIDEO_STABILIZATION_MODE_ON
+ */
+ public static final Key<Integer> CONTROL_VIDEO_STABILIZATION_MODE =
+ new Key<Integer>("android.control.videoStabilizationMode", int.class);
+
+ /**
* <p>Operation mode for edge
* enhancement.</p>
* <p>Edge/sharpness/detail enhancement. OFF means no
@@ -1688,6 +1973,22 @@ public final class CaptureResult extends CameraMetadata {
new Key<Float>("android.sensor.greenSplit", float.class);
/**
+ * <p>A pixel <code>[R, G_even, G_odd, B]</code> that supplies the test pattern
+ * when {@link CaptureRequest#SENSOR_TEST_PATTERN_MODE android.sensor.testPatternMode} is SOLID_COLOR.</p>
+ * <p>Each color channel is treated as an unsigned 32-bit integer.
+ * The camera device then uses the most significant X bits
+ * that correspond to how many bits are in its Bayer raw sensor
+ * output.</p>
+ * <p>For example, a sensor with RAW10 Bayer output would use the
+ * 10 most significant bits from each color channel.</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SENSOR_TEST_PATTERN_MODE
+ */
+ public static final Key<int[]> SENSOR_TEST_PATTERN_DATA =
+ new Key<int[]>("android.sensor.testPatternData", int[].class);
+
+ /**
* <p>When enabled, the sensor sends a test pattern instead of
* doing a real exposure from the camera.</p>
* <p>When a test pattern is enabled, all manual sensor controls specified
@@ -1936,6 +2237,20 @@ public final class CaptureResult extends CameraMetadata {
new Key<int[]>("android.statistics.hotPixelMap", int[].class);
/**
+ * <p>Whether the camera device will output the lens
+ * shading map in output result metadata.</p>
+ * <p>When set to ON,
+ * {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap} must be provided in
+ * the output result metadata.</p>
+ *
+ * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
+ * @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF
+ * @see #STATISTICS_LENS_SHADING_MAP_MODE_ON
+ */
+ public static final Key<Integer> STATISTICS_LENS_SHADING_MAP_MODE =
+ new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
+
+ /**
* <p>Tonemapping / contrast / gamma curve for the blue
* channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
* CONTRAST_CURVE.</p>
diff --git a/core/java/android/hardware/camera2/ColorSpaceTransform.java b/core/java/android/hardware/camera2/ColorSpaceTransform.java
new file mode 100644
index 0000000..9912e4b
--- /dev/null
+++ b/core/java/android/hardware/camera2/ColorSpaceTransform.java
@@ -0,0 +1,239 @@
+/*
+ * 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.hardware.camera2;
+
+import static com.android.internal.util.Preconditions.*;
+import android.hardware.camera2.impl.HashCodeHelpers;
+
+import java.util.Arrays;
+
+/**
+ * Immutable class for describing a 3x3 matrix of {@link Rational} values in row-major order.
+ *
+ * <p>This matrix maps a transform from one color space to another. For the particular color space
+ * source and target, see the appropriate camera metadata documentation for the key that provides
+ * this value.</p>
+ *
+ * @see CameraMetadata
+ */
+public final class ColorSpaceTransform {
+
+ /** The number of rows in this matrix. */
+ private static final int ROWS = 3;
+
+ /** The number of columns in this matrix. */
+ private static final int COLUMNS = 3;
+
+ /** The number of total Rational elements in this matrix. */
+ private static final int COUNT = ROWS * COLUMNS;
+
+ /** Number of int elements in a rational. */
+ private static final int RATIONAL_SIZE = 2;
+
+ /** Numerator offset inside a rational (pair). */
+ private static final int OFFSET_NUMERATOR = 0;
+
+ /** Denominator offset inside a rational (pair). */
+ private static final int OFFSET_DENOMINATOR = 1;
+
+ /** Number of int elements in this matrix. */
+ private static final int COUNT_INT = ROWS * COLUMNS * RATIONAL_SIZE;
+
+ /**
+ * Create a new immutable {@link ColorSpaceTransform} instance from a {@link Rational} array.
+ *
+ * <p>The elements must be stored in a row-major order.</p>
+ *
+ * @param elements An array of {@code 9} elements
+ *
+ * @throws IllegalArgumentException
+ * if the count of {@code elements} is not {@code 9}
+ * @throws NullPointerException
+ * if {@code elements} or any sub-element is {@code null}
+ */
+ public ColorSpaceTransform(Rational[] elements) {
+
+ checkNotNull(elements, "elements must not be null");
+ if (elements.length != COUNT) {
+ throw new IllegalArgumentException("elements must be " + COUNT + " length");
+ }
+
+ mElements = new int[COUNT_INT];
+
+ for (int i = 0; i < elements.length; ++i) {
+ checkNotNull(elements, "element[" + i + "] must not be null");
+ mElements[i * RATIONAL_SIZE + OFFSET_NUMERATOR] = elements[i].getNumerator();
+ mElements[i * RATIONAL_SIZE + OFFSET_DENOMINATOR] = elements[i].getDenominator();
+ }
+ }
+
+ /**
+ * Create a new immutable {@link ColorSpaceTransform} instance from an {@code int} array.
+ *
+ * <p>The elements must be stored in a row-major order. Each rational is stored
+ * contiguously as a {@code (numerator, denominator)} pair.</p>
+ *
+ * <p>In particular:<pre>{@code
+ * int[] elements = new int[
+ * N11, D11, N12, D12, N13, D13,
+ * N21, D21, N22, D22, N23, D23,
+ * N31, D31, N32, D32, N33, D33
+ * ];
+ *
+ * new ColorSpaceTransform(elements)}</pre>
+ *
+ * where {@code Nij} and {@code Dij} is the numerator and denominator for row {@code i} and
+ * column {@code j}.</p>
+ *
+ * @param elements An array of {@code 18} elements
+ *
+ * @throws IllegalArgumentException
+ * if the count of {@code elements} is not {@code 18}
+ * @throws NullPointerException
+ * if {@code elements} is {@code null}
+ */
+ public ColorSpaceTransform(int[] elements) {
+ checkNotNull(elements, "elements must not be null");
+ if (elements.length != COUNT_INT) {
+ throw new IllegalArgumentException("elements must be " + COUNT_INT + " length");
+ }
+
+ for (int i = 0; i < elements.length; ++i) {
+ checkNotNull(elements, "element " + i + " must not be null");
+ }
+
+ mElements = Arrays.copyOf(elements, elements.length);
+ }
+
+ /**
+ * Get an element of this matrix by its row and column.
+ *
+ * <p>The rows must be within the range [0, 3),
+ * and the column must be within the range [0, 3).</p>
+ *
+ * @return element (non-{@code null})
+ *
+ * @throws IllegalArgumentException if column or row was out of range
+ */
+ public Rational getElement(int column, int row) {
+ if (column < 0 || column >= COLUMNS) {
+ throw new IllegalArgumentException("column out of range");
+ } else if (row < 0 || row >= ROWS) {
+ throw new IllegalArgumentException("row out of range");
+ }
+
+ int numerator = mElements[row * ROWS * RATIONAL_SIZE + column + OFFSET_NUMERATOR];
+ int denominator = mElements[row * ROWS * RATIONAL_SIZE + column + OFFSET_DENOMINATOR];
+
+ return new Rational(numerator, denominator);
+ }
+
+ /**
+ * Copy the {@link Rational} elements in row-major order from this matrix into the destination.
+ *
+ * @param destination
+ * an array big enough to hold at least {@code 9} elements after the
+ * {@code offset}
+ * @param offset
+ * a non-negative offset into the array
+ * @throws NullPointerException
+ * If {@code destination} was {@code null}
+ * @throws ArrayIndexOutOfBoundsException
+ * If there's not enough room to write the elements at the specified destination and
+ * offset.
+ */
+ public void copyElements(Rational[] destination, int offset) {
+ checkArgumentNonnegative(offset, "offset must not be negative");
+ checkNotNull(destination, "destination must not be null");
+ if (destination.length + offset < COUNT) {
+ throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
+ }
+
+ for (int i = 0, j = 0; i < COUNT; ++i, j += RATIONAL_SIZE) {
+ int numerator = mElements[j + OFFSET_NUMERATOR];
+ int denominator = mElements[j + OFFSET_DENOMINATOR];
+
+ destination[i + offset] = new Rational(numerator, denominator);
+ }
+ }
+
+ /**
+ * Copy the {@link Rational} elements in row-major order from this matrix into the destination.
+ *
+ * <p>Each element is stored as a contiguous rational packed as a
+ * {@code (numerator, denominator)} pair of ints, identical to the
+ * {@link ColorSpaceTransform#ColorSpaceTransform(int[]) constructor}.</p>
+ *
+ * @param destination
+ * an array big enough to hold at least {@code 18} elements after the
+ * {@code offset}
+ * @param offset
+ * a non-negative offset into the array
+ * @throws NullPointerException
+ * If {@code destination} was {@code null}
+ * @throws ArrayIndexOutOfBoundsException
+ * If there's not enough room to write the elements at the specified destination and
+ * offset.
+ *
+ * @see ColorSpaceTransform#ColorSpaceTransform(int[])
+ */
+ public void copyElements(int[] destination, int offset) {
+ checkArgumentNonnegative(offset, "offset must not be negative");
+ checkNotNull(destination, "destination must not be null");
+ if (destination.length + offset < COUNT_INT) {
+ throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
+ }
+
+ // Manual copy faster than System#arraycopy for very small loops
+ for (int i = 0; i < COUNT_INT; ++i) {
+ destination[i + offset] = mElements[i];
+ }
+ }
+
+ /**
+ * Check if this {@link ColorSpaceTransform} is equal to another {@link ColorSpaceTransform}.
+ *
+ * <p>Two color space transforms are equal if and only if all of their elements are
+ * {@link Object#equals equal}.</p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ColorSpaceTransform) {
+ final ColorSpaceTransform other = (ColorSpaceTransform) obj;
+ return Arrays.equals(mElements, other.mElements);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCodeHelpers.hashCode(mElements);
+ }
+
+ private final int[] mElements;
+};
diff --git a/core/java/android/hardware/camera2/LensShadingMap.java b/core/java/android/hardware/camera2/LensShadingMap.java
new file mode 100644
index 0000000..2c224f6
--- /dev/null
+++ b/core/java/android/hardware/camera2/LensShadingMap.java
@@ -0,0 +1,243 @@
+/*
+ * 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.hardware.camera2;
+
+import static com.android.internal.util.Preconditions.*;
+import static android.hardware.camera2.RggbChannelVector.*;
+
+import android.hardware.camera2.impl.HashCodeHelpers;
+
+import java.util.Arrays;
+
+/**
+ * Immutable class for describing a {@code 4 x N x M} lens shading map of floats.
+ *
+ * @see CameraCharacteristics#LENS_SHADING_MAP
+ */
+public final class LensShadingMap {
+
+ /**
+ * The smallest gain factor in this map.
+ *
+ * <p>All values in this map will be at least this large.</p>
+ */
+ public static final float MINIMUM_GAIN_FACTOR = 1.0f;
+
+ /**
+ * Create a new immutable LensShadingMap instance.
+ *
+ * <p>The elements must be stored in a row-major order (fully packed).</p>
+ *
+ * <p>This constructor takes over the array; do not write to the array afterwards.</p>
+ *
+ * @param elements
+ * An array of elements whose length is
+ * {@code RggbChannelVector.COUNT * rows * columns}
+ *
+ * @throws IllegalArgumentException
+ * if the {@code elements} array length is invalid,
+ * if any of the subelems are not finite or less than {@value #MINIMUM_GAIN_FACTOR},
+ * or if rows or columns is not positive
+ * @throws NullPointerException
+ * if {@code elements} is {@code null}
+ *
+ * @hide
+ */
+ public LensShadingMap(final float[] elements, final int rows, final int columns) {
+
+ mRows = checkArgumentPositive(rows, "rows must be positive");
+ mColumns = checkArgumentPositive(rows, "columns must be positive");
+ mElements = checkNotNull(elements, "elements must not be null");
+
+ if (elements.length != getGainFactorCount()) {
+ throw new IllegalArgumentException("elements must be " + getGainFactorCount() +
+ " length");
+ }
+
+ // Every element must be finite and >= 1.0f
+ checkArrayElementsInRange(elements, MINIMUM_GAIN_FACTOR, Float.MAX_VALUE, "elements");
+ }
+
+ /**
+ * Get the number of rows in this map.
+ */
+ public int getRowCount() {
+ return mRows;
+ }
+
+ /**
+ * Get the number of columns in this map.
+ */
+ public int getColumnCount() {
+ return mColumns;
+ }
+
+ /**
+ * Get the total number of gain factors in this map.
+ *
+ * <p>A single gain factor contains exactly one color channel.
+ * Use with {@link #copyGainFactors} to allocate a large-enough array.</p>
+ */
+ public int getGainFactorCount() {
+ return mRows * mColumns * COUNT;
+ }
+
+ /**
+ * Get a single color channel gain factor from this lens shading map by its row and column.
+ *
+ * <p>The rows must be within the range [0, {@link #getRowCount}),
+ * the column must be within the range [0, {@link #getColumnCount}),
+ * and the color channel must be within the range [0, {@value RggbChannelVector#COUNT}).</p>
+ *
+ * <p>The channel order is {@code [R, Geven, Godd, B]}, where
+ * {@code Geven} is the green channel for the even rows of a Bayer pattern, and
+ * {@code Godd} is the odd rows.
+ * </p>
+ *
+ * @param colorChannel color channel from {@code [R, Geven, Godd, B]}
+ * @param column within the range [0, {@link #getColumnCount})
+ * @param row within the range [0, {@link #getRowCount})
+ *
+ * @return a gain factor >= {@value #MINIMUM_GAIN_FACTOR}
+ *
+ * @throws IllegalArgumentException if any of the parameters was out of range
+ *
+ * @see #RED
+ * @see #GREEN_EVEN
+ * @see #GREEN_ODD
+ * @see #BLUE
+ * @see #getRowCount
+ * @see #getColumnCount
+ */
+ public float getGainFactor(final int colorChannel, final int column, final int row) {
+ if (colorChannel < 0 || colorChannel > COUNT) {
+ throw new IllegalArgumentException("colorChannel out of range");
+ } else if (column < 0 || column >= mColumns) {
+ throw new IllegalArgumentException("column out of range");
+ } else if (row < 0 || row >= mRows) {
+ throw new IllegalArgumentException("row out of range");
+ }
+
+ return mElements[colorChannel + (row * mColumns + column) * COUNT ];
+ }
+
+ /**
+ * Get a gain factor vector from this lens shading map by its row and column.
+ *
+ * <p>The rows must be within the range [0, {@link #getRowCount}),
+ * the column must be within the range [0, {@link #getColumnCount}).</p>
+ *
+ * @param column within the range [0, {@link #getColumnCount})
+ * @param row within the range [0, {@link #getRowCount})
+ *
+ * @return an {@link RggbChannelVector} where each gain factor >= {@value #MINIMUM_GAIN_FACTOR}
+ *
+ * @throws IllegalArgumentException if any of the parameters was out of range
+ *
+ * @see #getRowCount
+ * @see #getColumnCount
+ */
+ public RggbChannelVector getGainFactorVector(final int column, final int row) {
+ if (column < 0 || column >= mColumns) {
+ throw new IllegalArgumentException("column out of range");
+ } else if (row < 0 || row >= mRows) {
+ throw new IllegalArgumentException("row out of range");
+ }
+
+ final int offset = (row * mColumns + column) * COUNT;
+
+ final float red =
+ mElements[RED + offset];
+ final float greenEven =
+ mElements[GREEN_EVEN + offset];
+ final float greenOdd =
+ mElements[GREEN_ODD + offset];
+ final float blue =
+ mElements[BLUE + offset];
+
+ return new RggbChannelVector(red, greenEven, greenOdd, blue);
+ }
+
+ /**
+ * Copy all gain factors in row-major order from this lens shading map into the destination.
+ *
+ * <p>Each gain factor will be >= {@link #MINIMUM_GAIN_FACTOR}.</p>
+ *
+ * @param destination
+ * an array big enough to hold at least {@link RggbChannelVector#COUNT}
+ * elements after the {@code offset}
+ * @param offset
+ * a non-negative offset into the array
+ * @throws NullPointerException
+ * If {@code destination} was {@code null}
+ * @throws IllegalArgumentException
+ * If offset was negative
+ * @throws ArrayIndexOutOfBoundsException
+ * If there's not enough room to write the elements at the specified destination and
+ * offset.
+ *
+ * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
+ */
+ public void copyGainFactors(final float[] destination, final int offset) {
+ checkArgumentNonnegative(offset, "offset must not be negative");
+ checkNotNull(destination, "destination must not be null");
+ if (destination.length + offset < getGainFactorCount()) {
+ throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
+ }
+
+ System.arraycopy(mElements, /*srcPos*/0, destination, offset, getGainFactorCount());
+ }
+
+ /**
+ * Check if this LensShadingMap is equal to another LensShadingMap.
+ *
+ * <p>Two lens shading maps are equal if and only if they have the same rows/columns,
+ * and all of their elements are {@link Object#equals equal}.</p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof LensShadingMap) {
+ final LensShadingMap other = (LensShadingMap) obj;
+ return mRows == other.mRows
+ && mColumns == other.mColumns
+ && Arrays.equals(mElements, other.mElements);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ int elemsHash = HashCodeHelpers.hashCode(mElements);
+ return HashCodeHelpers.hashCode(mRows, mColumns, elemsHash);
+ }
+
+
+ private final int mRows;
+ private final int mColumns;
+ private final float[] mElements;
+};
diff --git a/core/java/android/hardware/camera2/MeteringRectangle.java b/core/java/android/hardware/camera2/MeteringRectangle.java
new file mode 100644
index 0000000..ff7a745
--- /dev/null
+++ b/core/java/android/hardware/camera2/MeteringRectangle.java
@@ -0,0 +1,224 @@
+/*
+ * 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.hardware.camera2;
+
+import android.util.Size;
+import static com.android.internal.util.Preconditions.*;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.camera2.impl.HashCodeHelpers;
+
+/**
+ * An immutable class to represent a rectangle {@code (x,y, width, height)} with an
+ * additional weight component.
+ *
+ * </p>The rectangle is defined to be inclusive of the specified coordinates.</p>
+ *
+ * <p>When used with a {@link CaptureRequest}, the coordinate system is based on the active pixel
+ * array, with {@code (0,0)} being the top-left pixel in the
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE active pixel array}, and
+ * {@code (android.sensor.info.activeArraySize.width - 1,
+ * android.sensor.info.activeArraySize.height - 1)}
+ * being the bottom-right pixel in the active pixel array.
+ * </p>
+ *
+ * <p>The metering weight is nonnegative.</p>
+ */
+public final class MeteringRectangle {
+
+ private final int mX;
+ private final int mY;
+ private final int mWidth;
+ private final int mHeight;
+ private final int mWeight;
+
+ /**
+ * Create a new metering rectangle.
+ *
+ * @param x coordinate >= 0
+ * @param y coordinate >= 0
+ * @param width width >= 0
+ * @param height height >= 0
+ * @param meteringWeight weight >= 0
+ *
+ * @throws IllegalArgumentException if any of the parameters were non-negative
+ */
+ public MeteringRectangle(int x, int y, int width, int height, int meteringWeight) {
+ mX = checkArgumentNonnegative(x, "x must be nonnegative");
+ mY = checkArgumentNonnegative(y, "y must be nonnegative");
+ mWidth = checkArgumentNonnegative(width, "width must be nonnegative");
+ mHeight = checkArgumentNonnegative(height, "height must be nonnegative");
+ mWeight = checkArgumentNonnegative(meteringWeight, "meteringWeight must be nonnegative");
+ }
+
+ /**
+ * Create a new metering rectangle.
+ *
+ * @param xy a non-{@code null} {@link Point} with both x,y >= 0
+ * @param dimensions a non-{@code null} {@link android.util.Size Size} with width, height >= 0
+ * @param meteringWeight weight >= 0
+ *
+ * @throws IllegalArgumentException if any of the parameters were non-negative
+ * @throws NullPointerException if any of the arguments were null
+ */
+ public MeteringRectangle(Point xy, Size dimensions, int meteringWeight) {
+ checkNotNull(xy, "xy must not be null");
+ checkNotNull(dimensions, "dimensions must not be null");
+
+ mX = checkArgumentNonnegative(xy.x, "x must be nonnegative");
+ mY = checkArgumentNonnegative(xy.y, "y must be nonnegative");
+ mWidth = checkArgumentNonnegative(dimensions.getWidth(), "width must be nonnegative");
+ mHeight = checkArgumentNonnegative(dimensions.getHeight(), "height must be nonnegative");
+ mWeight = checkArgumentNonnegative(meteringWeight, "meteringWeight must be nonnegative");
+ }
+
+ /**
+ * Create a new metering rectangle.
+ *
+ * @param rect a non-{@code null} rectangle with all x,y,w,h dimensions >= 0
+ * @param meteringWeight weight >= 0
+ *
+ * @throws IllegalArgumentException if any of the parameters were non-negative
+ * @throws NullPointerException if any of the arguments were null
+ */
+ public MeteringRectangle(Rect rect, int meteringWeight) {
+ checkNotNull(rect, "rect must not be null");
+
+ mX = checkArgumentNonnegative(rect.left, "rect.left must be nonnegative");
+ mY = checkArgumentNonnegative(rect.top, "rect.top must be nonnegative");
+ mWidth = checkArgumentNonnegative(rect.width(), "rect.width must be nonnegative");
+ mHeight = checkArgumentNonnegative(rect.height(), "rect.height must be nonnegative");
+ mWeight = checkArgumentNonnegative(meteringWeight, "meteringWeight must be nonnegative");
+ }
+
+ /**
+ * Return the X coordinate of the left side of the rectangle.
+ *
+ * @return x coordinate >= 0
+ */
+ public int getX() {
+ return mX;
+ }
+
+ /**
+ * Return the Y coordinate of the upper side of the rectangle.
+ *
+ * @return y coordinate >= 0
+ */
+ public int getY() {
+ return mY;
+ }
+
+ /**
+ * Return the width of the rectangle.
+ *
+ * @return width >= 0
+ */
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * Return the height of the rectangle.
+ *
+ * @return height >= 0
+ */
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * Return the metering weight of the rectangle.
+ *
+ * @return weight >= 0
+ */
+ public int getMeteringWeight() {
+ return mWeight;
+ }
+
+ /**
+ * Convenience method to create the upper-left (X,Y) coordinate as a {@link Point}.
+ *
+ * @return {@code (x,y)} point with both x,y >= 0
+ */
+ public Point getUpperLeftPoint() {
+ return new Point(mX, mY);
+ }
+
+ /**
+ * Convenience method to create the size from this metering rectangle.
+ *
+ * <p>This strips away the X,Y,weight from the rectangle.</p>
+ *
+ * @return a Size with non-negative width and height
+ */
+ public Size getSize() {
+ return new Size(mWidth, mHeight);
+ }
+
+ /**
+ * Convenience method to create a {@link Rect} from this metering rectangle.
+ *
+ * <p>This strips away the weight from the rectangle.</p>
+ *
+ * @return a {@link Rect} with non-negative x1, y1, x2, y2
+ */
+ public Rect getRect() {
+ return new Rect(mX, mY, mX + mWidth, mY + mHeight);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(final Object other) {
+ if (other instanceof MeteringRectangle) {
+ return equals(other);
+ }
+ return false;
+ }
+
+ /**
+ * Compare two metering rectangles to see if they are equal.
+ *
+ * Two weighted rectangles are only considered equal if each of their components
+ * (x, y, width, height, weight) is respectively equal.
+ *
+ * @param other Another MeteringRectangle
+ *
+ * @return {@code true} if the metering rectangles are equal, {@code false} otherwise
+ */
+ public boolean equals(final MeteringRectangle other) {
+ if (other == null) {
+ return false;
+ }
+
+ return (mX == other.mX
+ && mY == other.mY
+ && mWidth == other.mWidth
+ && mHeight == other.mHeight
+ && mWidth == other.mWidth);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCodeHelpers.hashCode(mX, mY, mWidth, mHeight, mWeight);
+ }
+}
diff --git a/core/java/android/hardware/camera2/ReprocessFormatsMap.java b/core/java/android/hardware/camera2/ReprocessFormatsMap.java
new file mode 100644
index 0000000..c6c59d4
--- /dev/null
+++ b/core/java/android/hardware/camera2/ReprocessFormatsMap.java
@@ -0,0 +1,258 @@
+/*
+ * 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.hardware.camera2;
+
+import static com.android.internal.util.Preconditions.*;
+
+import android.hardware.camera2.impl.HashCodeHelpers;
+
+import java.util.Arrays;
+
+/**
+ * Immutable class to store the input to output formats
+ * {@link CameraCharacteristics#SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP map} to be used for with
+ * camera image reprocessing.
+ *
+ * <p>
+ * The mapping of image formats that are supported by this camera device for input streams,
+ * to their corresponding output formats.</p>
+ *
+ * <p>
+ * Attempting to configure an input stream with output streams not listed as available in this map
+ * is not valid.
+ * </p>
+ *
+ * @see CameraCharacteristics#SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS
+ *
+ * <!-- hide this until we expose input streams through public API -->
+ * @hide
+ */
+public final class ReprocessFormatsMap {
+ /**
+ * Create a new {@link ReprocessFormatsMap}
+ *
+ * <p>This value is encoded as a variable-size array-of-arrays.
+ * The inner array always contains {@code [format, length, ...]} where ... has length elements.
+ * An inner array is followed by another inner array if the total metadata entry size hasn't
+ * yet been exceeded.</p>
+ *
+ * <p>Entry must not be {@code null}. Empty array is acceptable.</p>
+ *
+ * <p>The entry array ownership is passed to this instance after construction; do not
+ * write to it afterwards.</p>
+ *
+ * @param entry Array of ints, not yet deserialized (not-null)
+ *
+ * @throws IllegalArgumentException
+ * if the data was poorly formatted
+ * (missing output format length or too few output formats)
+ * @throws NullPointerException
+ * if entry was null
+ *
+ * @hide
+ */
+ public ReprocessFormatsMap(final int[] entry) {
+ checkNotNull(entry, "entry must not be null");
+
+ int numInputs = 0;
+ int left = entry.length;
+ for (int i = 0; i < entry.length; ) {
+ final int format = entry[i];
+
+ left--;
+ i++;
+
+ if (left < 1) {
+ throw new IllegalArgumentException(
+ String.format("Input %x had no output format length listed", format));
+ }
+
+ final int length = entry[i];
+ left--;
+ i++;
+
+ if (length > 0) {
+ if (left < length) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Input %x had too few output formats listed (actual: %d, " +
+ "expected: %d)", format, left, length));
+ }
+
+ i += length;
+ left -= length;
+ }
+
+ numInputs++;
+ }
+
+ mEntry = entry;
+ mInputCount = numInputs;
+ }
+
+ /**
+ * Get a list of all input image formats that can be used to reprocess an input
+ * stream into an output stream.
+ *
+ * <p>Use this input format to look up the available output formats with {@link #getOutputs}.
+ * </p>
+ *
+ * @return an array of inputs (possibly empty, but never {@code null})
+ *
+ * @see ImageFormat
+ * @see #getOutputs
+ */
+ public int[] getInputs() {
+ final int[] inputs = new int[mInputCount];
+
+ int left = mEntry.length;
+ for (int i = 0, j = 0; i < mEntry.length; j++) {
+ final int format = mEntry[i];
+
+ left--;
+ i++;
+
+ if (left < 1) {
+ throw new AssertionError(
+ String.format("Input %x had no output format length listed", format));
+ }
+ // TODO: check format is a valid input format
+
+ final int length = mEntry[i];
+ left--;
+ i++;
+
+ if (length > 0) {
+ if (left < length) {
+ throw new AssertionError(
+ String.format(
+ "Input %x had too few output formats listed (actual: %d, " +
+ "expected: %d)", format, left, length));
+ }
+
+ i += length;
+ left -= length;
+ }
+
+ // TODO: check output format is a valid output format
+
+ inputs[j] = format;
+ }
+
+ return inputs;
+ }
+
+ /**
+ * Get the list of output formats that can be reprocessed into from the input {@code format}.
+ *
+ * <p>The input {@code format} must be one of the formats returned by {@link #getInputs}.</p>
+ *
+ * @param format an input format
+ *
+ * @return list of output image formats
+ *
+ * @see ImageFormat
+ * @see #getInputs
+ */
+ public int[] getOutputs(final int format) {
+
+ int left = mEntry.length;
+ for (int i = 0; i < mEntry.length; ) {
+ final int inputFormat = mEntry[i];
+
+ left--;
+ i++;
+
+ if (left < 1) {
+ throw new AssertionError(
+ String.format("Input %x had no output format length listed", format));
+ }
+
+ final int length = mEntry[i];
+ left--;
+ i++;
+
+ if (length > 0) {
+ if (left < length) {
+ throw new AssertionError(
+ String.format(
+ "Input %x had too few output formats listed (actual: %d, " +
+ "expected: %d)", format, left, length));
+ }
+ }
+
+ if (inputFormat == format) {
+ int[] outputs = new int[length];
+
+ // Copying manually faster than System.arraycopy for small arrays
+ for (int k = 0; k < length; ++k) {
+ outputs[k] = mEntry[i + k];
+ }
+
+ return outputs;
+ }
+
+ i += length;
+ left -= length;
+
+ }
+
+ throw new IllegalArgumentException(
+ String.format("Input format %x was not one in #getInputs", format));
+ }
+
+ /**
+ * Check if this {@link ReprocessFormatsMap} is equal to another
+ * {@link ReprocessFormatsMap}.
+ *
+ * <p>These two objects are only equal if and only if each of the respective elements is equal.
+ * </p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ReprocessFormatsMap) {
+ final ReprocessFormatsMap other = (ReprocessFormatsMap) obj;
+ // Do not compare anything besides mEntry, since the rest of the values are derived
+ return Arrays.equals(mEntry, other.mEntry);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ // Do not hash anything besides mEntry since the rest of the values are derived
+ return HashCodeHelpers.hashCode(mEntry);
+ }
+
+ private final int[] mEntry;
+ /*
+ * Dependent fields: values are derived from mEntry
+ */
+ private final int mInputCount;
+}
diff --git a/core/java/android/hardware/camera2/RggbChannelVector.java b/core/java/android/hardware/camera2/RggbChannelVector.java
new file mode 100644
index 0000000..80167c6
--- /dev/null
+++ b/core/java/android/hardware/camera2/RggbChannelVector.java
@@ -0,0 +1,199 @@
+/*
+ * 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.hardware.camera2;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Immutable class to store a 4-element vector of floats indexable by a bayer RAW 2x2 pixel block.
+ */
+public final class RggbChannelVector {
+ /**
+ * The number of color channels in this vector.
+ */
+ public static final int COUNT = 4;
+
+ /** Red color channel in a bayer Raw pattern. */
+ public static final int RED = 0;
+
+ /** Green color channel in a bayer Raw pattern used by the even rows. */
+ public static final int GREEN_EVEN = 1;
+
+ /** Green color channel in a bayer Raw pattern used by the odd rows. */
+ public static final int GREEN_ODD = 2;
+
+ /** Blue color channel in a bayer Raw pattern. */
+ public static final int BLUE = 3;
+
+ /**
+ * Create a new {@link RggbChannelVector} from an RGGB 2x2 pixel.
+ *
+ * <p>All pixel values are considered normalized within {@code [0.0f, 1.0f]}
+ * (i.e. {@code 1.0f} could be linearized to {@code 255} if converting to a
+ * non-floating point pixel representation).</p>
+ *
+ * <p>All arguments must be finite; NaN and infinity is not allowed.</p>
+ *
+ * @param red red pixel
+ * @param greenEven green pixel (even row)
+ * @param greenOdd green pixel (odd row)
+ * @param blue blue pixel
+ *
+ * @throws IllegalArgumentException if any of the arguments were not finite
+ */
+ public RggbChannelVector(final float red, final float greenEven, final float greenOdd,
+ final float blue) {
+ mRed = checkArgumentFinite(red, "red");
+ mGreenEven = checkArgumentFinite(greenEven, "greenEven");
+ mGreenOdd = checkArgumentFinite(greenOdd, "greenOdd");
+ mBlue = checkArgumentFinite(blue, "blue");
+ }
+
+ /**
+ * Get the red component.
+ *
+ * @return a floating point value (guaranteed to be finite)
+ */
+ public final float getRed() {
+ return mRed;
+ }
+
+ /**
+ * Get the green (even rows) component.
+ *
+ * @return a floating point value (guaranteed to be finite)
+ */
+ public float getGreenEven() {
+ return mGreenEven;
+ }
+
+ /**
+ * Get the green (odd rows) component.
+ *
+ * @return a floating point value (guaranteed to be finite)
+ */
+ public float getGreenOdd() {
+ return mGreenOdd;
+ }
+
+ /**
+ * Get the blue component.
+ *
+ * @return a floating point value (guaranteed to be finite)
+ */
+ public float getBlue() {
+ return mBlue;
+ }
+
+ /**
+ * Get the component by the color channel index.
+ *
+ * <p>{@code colorChannel} must be one of {@link #RED}, {@link #GREEN_EVEN}, {@link #GREEN_ODD},
+ * {@link #BLUE}.</p>
+ *
+ * @param colorChannel greater or equal to {@code 0} and less than {@link #COUNT}
+ * @return a floating point value (guaranteed to be finite)
+ *
+ * @throws IllegalArgumentException if {@code colorChannel} was out of range
+ */
+ public float getComponent(final int colorChannel) {
+ if (colorChannel < 0 || colorChannel >= COUNT) {
+ throw new IllegalArgumentException("Color channel out of range");
+ }
+
+ switch (colorChannel) {
+ case RED:
+ return mRed;
+ case GREEN_EVEN:
+ return mGreenEven;
+ case GREEN_ODD:
+ return mGreenOdd;
+ case BLUE:
+ return mBlue;
+ default:
+ throw new AssertionError("Unhandled case " + colorChannel);
+ }
+ }
+
+ /**
+ * Copy the vector into the destination in the order {@code [R, Geven, Godd, B]}.
+ *
+ * @param destination
+ * an array big enough to hold at least {@value #COUNT} elements after the
+ * {@code offset}
+ * @param offset
+ * a non-negative offset into the array
+ *
+ * @throws NullPointerException
+ * If {@code destination} was {@code null}
+ * @throws ArrayIndexOutOfBoundsException
+ * If there's not enough room to write the elements at the specified destination and
+ * offset.
+ */
+ public void copyTo(final float[] destination, final int offset) {
+ checkNotNull(destination, "destination must not be null");
+ if (destination.length + offset < COUNT) {
+ throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
+ }
+
+ destination[offset + RED] = mRed;
+ destination[offset + GREEN_EVEN] = mGreenEven;
+ destination[offset + GREEN_ODD] = mGreenOdd;
+ destination[offset + BLUE] = mBlue;
+ }
+
+ /**
+ * Check if this {@link RggbChannelVector} is equal to another {@link RggbChannelVector}.
+ *
+ * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof RggbChannelVector) {
+ final RggbChannelVector other = (RggbChannelVector) obj;
+ return mRed == other.mRed &&
+ mGreenEven == other.mGreenEven &&
+ mGreenOdd == other.mGreenOdd &&
+ mBlue == other.mBlue;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return Float.floatToIntBits(mRed) ^
+ Float.floatToIntBits(mGreenEven) ^
+ Float.floatToIntBits(mGreenOdd) ^
+ Float.floatToIntBits(mBlue);
+ }
+
+ private final float mRed;
+ private final float mGreenEven;
+ private final float mGreenOdd;
+ private final float mBlue;
+}
diff --git a/core/java/android/hardware/camera2/Size.java b/core/java/android/hardware/camera2/Size.java
index 45aaeae..9328a003 100644
--- a/core/java/android/hardware/camera2/Size.java
+++ b/core/java/android/hardware/camera2/Size.java
@@ -16,32 +16,55 @@
package android.hardware.camera2;
+// TODO: Delete this class, since it was moved to android.util as public API
+
/**
- * A simple immutable class for describing the dimensions of camera image
- * buffers.
+ * Immutable class for describing width and height dimensions in pixels.
+ *
+ * @hide
*/
public final class Size {
/**
- * Create a new immutable Size instance
+ * Create a new immutable Size instance.
*
- * @param width The width to store in the Size instance
- * @param height The height to store in the Size instance
+ * @param width The width of the size, in pixels
+ * @param height The height of the size, in pixels
*/
- public Size(int width, int height) {
+ public Size(final int width, final int height) {
mWidth = width;
mHeight = height;
}
+ /**
+ * Get the width of the size (in pixels).
+ * @return width
+ */
public final int getWidth() {
return mWidth;
}
+ /**
+ * Get the height of the size (in pixels).
+ * @return height
+ */
public final int getHeight() {
return mHeight;
}
+ /**
+ * Check if this size is equal to another size.
+ * <p>
+ * Two sizes are equal if and only if both their widths and heights are
+ * equal.
+ * </p>
+ * <p>
+ * A size object is never equal to any other type of object.
+ * </p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
@Override
- public boolean equals(Object obj) {
+ public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
@@ -49,27 +72,29 @@ public final class Size {
return true;
}
if (obj instanceof Size) {
- Size other = (Size) obj;
+ final Size other = (Size) obj;
return mWidth == other.mWidth && mHeight == other.mHeight;
}
return false;
}
+ /**
+ * Return the size represented as a string with the format {@code "WxH"}
+ *
+ * @return string representation of the size
+ */
@Override
public String toString() {
return mWidth + "x" + mHeight;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public int hashCode() {
- final long INT_MASK = 0xffffffffL;
-
- long asLong = INT_MASK & mWidth;
- asLong <<= 32;
-
- asLong |= (INT_MASK & mHeight);
-
- return ((Long)asLong).hashCode();
+ // assuming most sizes are <2^16, doing a rotate will give us perfect hashing
+ return mHeight ^ ((mWidth << (Integer.SIZE / 2)) | (mWidth >>> (Integer.SIZE / 2)));
}
private final int mWidth;
diff --git a/core/java/android/hardware/camera2/StreamConfiguration.java b/core/java/android/hardware/camera2/StreamConfiguration.java
new file mode 100644
index 0000000..c53dd7c
--- /dev/null
+++ b/core/java/android/hardware/camera2/StreamConfiguration.java
@@ -0,0 +1,166 @@
+/*
+ * 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.hardware.camera2;
+
+import static com.android.internal.util.Preconditions.*;
+import static android.hardware.camera2.StreamConfigurationMap.checkArgumentFormatInternal;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.impl.HashCodeHelpers;
+import android.util.Size;
+
+/**
+ * Immutable class to store the available stream
+ * {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS configurations} to be used
+ * when configuring streams with {@link CameraDevice#configureOutputs}.
+ * <!-- TODO: link to input stream configuration -->
+ *
+ * <p>This is the authoritative list for all input/output formats (and sizes respectively
+ * for that format) that are supported by a camera device.</p>
+ *
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS
+ *
+ * @hide
+ */
+public final class StreamConfiguration {
+
+ /**
+ * Create a new {@link StreamConfiguration}.
+ *
+ * @param format image format
+ * @param width image width, in pixels (positive)
+ * @param height image height, in pixels (positive)
+ * @param input true if this is an input configuration, false for output configurations
+ *
+ * @throws IllegalArgumentException
+ * if width/height were not positive
+ * or if the format was not user-defined in ImageFormat/PixelFormat
+ * (IMPL_DEFINED is ok)
+ *
+ * @hide
+ */
+ public StreamConfiguration(
+ final int format, final int width, final int height, final boolean input) {
+ mFormat = checkArgumentFormatInternal(format);
+ mWidth = checkArgumentPositive(width, "width must be positive");
+ mHeight = checkArgumentPositive(width, "height must be positive");
+ mInput = input;
+ }
+
+ /**
+ * Get the image {@code format} in this stream configuration.
+ *
+ * @return an integer format
+ *
+ * @see ImageFormat
+ */
+ public final int getFormat() {
+ return mFormat;
+ }
+
+
+ /**
+ * Return the width of the stream configuration.
+ *
+ * @return width > 0
+ */
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * Return the height of the stream configuration.
+ *
+ * @return height > 0
+ */
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * Convenience method to return the size of this stream configuration.
+ *
+ * @return a Size with positive width and height
+ */
+ public Size getSize() {
+ return new Size(mWidth, mHeight);
+ }
+
+ /**
+ * Determines if this configuration is usable for input streams.
+ *
+ * <p>Input and output stream configurations are not interchangeable;
+ * input stream configurations must be used when configuring inputs.</p>
+ *
+ * @return {@code true} if input configuration, {@code false} otherwise
+ */
+ public boolean isInput() {
+ return mInput;
+ }
+
+ /**
+ * Determines if this configuration is usable for output streams.
+ *
+ * <p>Input and output stream configurations are not interchangeable;
+ * out stream configurations must be used when configuring outputs.</p>
+ *
+ * @return {@code true} if output configuration, {@code false} otherwise
+ *
+ * @see CameraDevice#configureOutputs
+ */
+ public boolean isOutput() {
+ return !mInput;
+ }
+
+ /**
+ * Check if this {@link StreamConfiguration} is equal to another {@link StreamConfiguration}.
+ *
+ * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof StreamConfiguration) {
+ final StreamConfiguration other = (StreamConfiguration) obj;
+ return mFormat == other.mFormat &&
+ mWidth == other.mWidth &&
+ mHeight == other.mHeight &&
+ mInput == other.mInput;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCodeHelpers.hashCode(mFormat, mWidth, mHeight, mInput ? 1 : 0);
+ }
+
+ private final int mFormat;
+ private final int mWidth;
+ private final int mHeight;
+ private final boolean mInput;
+}
diff --git a/core/java/android/hardware/camera2/StreamConfigurationDuration.java b/core/java/android/hardware/camera2/StreamConfigurationDuration.java
new file mode 100644
index 0000000..189ae62
--- /dev/null
+++ b/core/java/android/hardware/camera2/StreamConfigurationDuration.java
@@ -0,0 +1,148 @@
+/*
+ * 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.hardware.camera2;
+
+import static com.android.internal.util.Preconditions.*;
+import static android.hardware.camera2.StreamConfigurationMap.checkArgumentFormatInternal;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.impl.HashCodeHelpers;
+import android.util.Size;
+
+/**
+ * Immutable class to store a time duration for any given format/size combination.
+ *
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS
+ * @see CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS
+ *
+ * @hide
+ */
+public final class StreamConfigurationDuration {
+
+ /**
+ * Create a new {@link StreamConfigurationDuration}.
+ *
+ * @param format image format
+ * @param width image width, in pixels (positive)
+ * @param height image height, in pixels (positive)
+ * @param durationNs duration in nanoseconds (non-negative)
+ *
+ * @throws IllegalArgumentException
+ * if width/height were not positive, or durationNs was negative
+ * or if the format was not user-defined in ImageFormat/PixelFormat
+ * (IMPL_DEFINED is OK)
+ *
+ *
+ * @hide
+ */
+ public StreamConfigurationDuration(
+ final int format, final int width, final int height, final long durationNs) {
+ mFormat = checkArgumentFormatInternal(format);
+ mWidth = checkArgumentPositive(width, "width must be positive");
+ mHeight = checkArgumentPositive(width, "height must be positive");
+ mDurationNs = checkArgumentNonnegative(durationNs, "durationNs must be non-negative");
+ }
+
+ /**
+ * Get the image {@code format} in this stream configuration duration
+ *
+ * @return an integer format
+ *
+ * @see ImageFormat
+ */
+ public final int getFormat() {
+ return mFormat;
+ }
+
+
+ /**
+ * Return the width of the stream configuration duration.
+ *
+ * @return width > 0
+ */
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * Return the height of the stream configuration duration
+ *
+ * @return height > 0
+ */
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * Convenience method to return the size of this stream configuration duration.
+ *
+ * @return a Size with positive width and height
+ */
+ public Size getSize() {
+ return new Size(mWidth, mHeight);
+ }
+
+ /**
+ * Get the time duration (in nanoseconds).
+ *
+ * @return long >= 0
+ */
+ public long getDuration() {
+ return mDurationNs;
+ }
+
+ /**
+ * Check if this {@link StreamConfigurationDuration} is equal to another
+ * {@link StreamConfigurationDuration}.
+ *
+ * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof StreamConfigurationDuration) {
+ final StreamConfigurationDuration other = (StreamConfigurationDuration) obj;
+ return mFormat == other.mFormat &&
+ mWidth == other.mWidth &&
+ mHeight == other.mHeight &&
+ mDurationNs == other.mDurationNs;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCodeHelpers.hashCode(mFormat, mWidth, mHeight,
+ (int) mDurationNs, (int)(mDurationNs >>> Integer.SIZE));
+ }
+
+ private final int mFormat;
+ private final int mWidth;
+ private final int mHeight;
+ private final long mDurationNs;
+}
diff --git a/core/java/android/hardware/camera2/StreamConfigurationMap.java b/core/java/android/hardware/camera2/StreamConfigurationMap.java
new file mode 100644
index 0000000..e24fd1b
--- /dev/null
+++ b/core/java/android/hardware/camera2/StreamConfigurationMap.java
@@ -0,0 +1,508 @@
+/*
+ * 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.hardware.camera2;
+
+import android.graphics.ImageFormat;
+import android.graphics.PixelFormat;
+import android.hardware.camera2.impl.HashCodeHelpers;
+import android.view.Surface;
+import android.util.Size;
+
+import java.util.Arrays;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Immutable class to store the available stream
+ * {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS configurations} to be used
+ * when configuring streams with {@link CameraDevice#configureOutputs}.
+ * <!-- TODO: link to input stream configuration -->
+ *
+ * <p>This is the authoritative list for all <!-- input/ -->output formats (and sizes respectively
+ * for that format) that are supported by a camera device.</p>
+ *
+ * <p>This also contains the minimum frame durations and stall durations for each format/size
+ * combination that can be used to calculate effective frame rate when submitting multiple captures.
+ * </p>
+ *
+ * <p>An instance of this object is available from {@link CameraCharacteristics} using
+ * the {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS} key and the
+ * {@link CameraCharacteristics#get} method.</p.
+ *
+ * <pre>{@code
+ * CameraCharacteristics characteristics = ...;
+ * StreamConfigurationMap configs = characteristics.get(
+ * CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+ * }</pre>
+ *
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS
+ * @see CameraDevice#configureOutputs
+ */
+public final class StreamConfigurationMap {
+
+ /**
+ * Create a new {@link StreamConfigurationMap}.
+ *
+ * <p>The array parameters ownership is passed to this object after creation; do not
+ * write to them after this constructor is invoked.</p>
+ *
+ * @param configurations a non-{@code null} array of {@link StreamConfiguration}
+ * @param durations a non-{@code null} array of {@link StreamConfigurationDuration}
+ *
+ * @throws NullPointerException if any of the arguments or subelements were {@code null}
+ *
+ * @hide
+ */
+ public StreamConfigurationMap(
+ StreamConfiguration[] configurations,
+ StreamConfigurationDuration[] durations) {
+ // TODO: format check against ImageFormat/PixelFormat ?
+
+ mConfigurations = checkArrayElementsNotNull(configurations, "configurations");
+ mDurations = checkArrayElementsNotNull(durations, "durations");
+
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * Get the image {@code format} output formats in this stream configuration.
+ *
+ * <p>All image formats returned by this function will be defined in either {@link ImageFormat}
+ * or in {@link PixelFormat} (and there is no possibility of collision).</p>
+ *
+ * <p>Formats listed in this array are guaranteed to return true if queried with
+ * {@link #isOutputSupportedFor(int).</p>
+ *
+ * @return an array of integer format
+ *
+ * @see ImageFormat
+ * @see PixelFormat
+ */
+ public final int[] getOutputFormats() {
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * Get the image {@code format} input formats in this stream configuration.
+ *
+ * <p>All image formats returned by this function will be defined in either {@link ImageFormat}
+ * or in {@link PixelFormat} (and there is no possibility of collision).</p>
+ *
+ * @return an array of integer format
+ *
+ * @see ImageFormat
+ * @see PixelFormat
+ *
+ * @hide
+ */
+ public final int[] getInputFormats() {
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * Get the supported input sizes for this input format.
+ *
+ * <p>The format must have come from {@link #getInputFormats}; otherwise
+ * {@code null} is returned.</p>
+ *
+ * @param format a format from {@link #getInputFormats}
+ * @return a non-empty array of sizes, or {@code null} if the format was not available.
+ *
+ * @hide
+ */
+ public Size[] getInputSizes(final int format) {
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * Determine whether or not output streams can be
+ * {@link CameraDevice#configureOutputs configured} with a particular user-defined format.
+ *
+ * <p>This method determines that the output {@code format} is supported by the camera device;
+ * each output {@code surface} target may or may not itself support that {@code format}.
+ * Refer to the class which provides the surface for additional documentation.</p>
+ *
+ * <p>Formats for which this returns {@code true} are guaranteed to exist in the result
+ * returned by {@link #getOutputSizes}.</p>
+ *
+ * @param format an image format from either {@link ImageFormat} or {@link PixelFormat}
+ * @return
+ * {@code true} iff using a {@code surface} with this {@code format} will be
+ * supported with {@link CameraDevice#configureOutputs}
+ *
+ * @throws IllegalArgumentException
+ * if the image format was not a defined named constant
+ * from either {@link ImageFormat} or {@link PixelFormat}
+ *
+ * @see ImageFormat
+ * @see PixelFormat
+ * @see CameraDevice#configureOutputs
+ */
+ public boolean isOutputSupportedFor(int format) {
+ checkArgumentFormat(format);
+
+ final int[] formats = getOutputFormats();
+ for (int i = 0; i < formats.length; ++i) {
+ if (format == formats[i]) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Determine whether or not output streams can be configured with a particular class
+ * as a consumer.
+ *
+ * <p>The following list is generally usable for outputs:
+ * <ul>
+ * <li>{@link android.media.ImageReader} -
+ * Recommended for image processing or streaming to external resources (such as a file or
+ * network)
+ * <li>{@link android.media.MediaRecorder} -
+ * Recommended for recording video (simple to use)
+ * <li>{@link android.media.MediaCodec} -
+ * Recommended for recording video (more complicated to use, with more flexibility)
+ * <li>{@link android.renderscript.Allocation} -
+ * Recommended for image processing with {@link android.renderscript RenderScript}
+ * <li>{@link android.view.SurfaceHolder} -
+ * Recommended for low-power camera preview with {@link android.view.SurfaceView}
+ * <li>{@link android.graphics.SurfaceTexture} -
+ * Recommended for OpenGL-accelerated preview processing or compositing with
+ * {@link android.view.TextureView}
+ * </ul>
+ * </p>
+ *
+ * <p>Generally speaking this means that creating a {@link Surface} from that class <i>may</i>
+ * provide a producer endpoint that is suitable to be used with
+ * {@link CameraDevice#configureOutputs}.</p>
+ *
+ * <p>Since not all of the above classes support output of all format and size combinations,
+ * the particular combination should be queried with {@link #isOutputSupportedFor(Surface)}.</p>
+ *
+ * @param klass a non-{@code null} {@link Class} object reference
+ * @return {@code true} if this class is supported as an output, {@code false} otherwise
+ *
+ * @throws NullPointerException if {@code klass} was {@code null}
+ *
+ * @see CameraDevice#configureOutputs
+ * @see #isOutputSupportedFor(Surface)
+ */
+ public static <T> boolean isOutputSupportedFor(final Class<T> klass) {
+ checkNotNull(klass, "klass must not be null");
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * Determine whether or not the {@code surface} in its current state is suitable to be
+ * {@link CameraDevice#configureOutputs configured} as an output.
+ *
+ * <p>Not all surfaces are usable with the {@link CameraDevice}, and not all configurations
+ * of that {@code surface} are compatible. Some classes that provide the {@code surface} are
+ * compatible with the {@link CameraDevice} in general
+ * (see {@link #isOutputSupportedFor(Class)}, but it is the caller's responsibility to put the
+ * {@code surface} into a state that will be compatible with the {@link CameraDevice}.</p>
+ *
+ * <p>Reasons for a {@code surface} being specifically incompatible might be:
+ * <ul>
+ * <li>Using a format that's not listed by {@link #getOutputFormats}
+ * <li>Using a format/size combination that's not listed by {@link #getOutputSizes}
+ * <li>The {@code surface} itself is not in a state where it can service a new producer.</p>
+ * </li>
+ * </ul>
+ *
+ * This is not an exhaustive list; see the particular class's documentation for further
+ * possible reasons of incompatibility.</p>
+ *
+ * @param surface a non-{@code null} {@link Surface} object reference
+ * @return {@code true} if this is supported, {@code false} otherwise
+ *
+ * @throws NullPointerException if {@code surface} was {@code null}
+ *
+ * @see CameraDevice#configureOutputs
+ * @see #isOutputSupportedFor(Class)
+ */
+ public boolean isOutputSupportedFor(final Surface surface) {
+ checkNotNull(surface, "surface must not be null");
+
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * Get a list of sizes compatible with {@code klass} to use as an output.
+ *
+ * <p>Since some of the supported classes may support additional formats beyond
+ * an opaque/implementation-defined (under-the-hood) format; this function only returns
+ * sizes for the implementation-defined format.</p>
+ *
+ * <p>Some classes such as {@link android.media.ImageReader} may only support user-defined
+ * formats; in particular {@link #isOutputSupportedFor(Class)} will return {@code true} for
+ * that class and this method will return an empty array (but not {@code null}).</p>
+ *
+ * <p>If a well-defined format such as {@code NV21} is required, use
+ * {@link #getOutputSizes(int)} instead.</p>
+ *
+ * <p>The {@code klass} should be a supported output, that querying
+ * {@code #isOutputSupportedFor(Class)} should return {@code true}.</p>
+ *
+ * @param klass
+ * a non-{@code null} {@link Class} object reference
+ * @return
+ * an array of supported sizes for implementation-defined formats,
+ * or {@code null} iff the {@code klass} is not a supported output
+ *
+ * @throws NullPointerException if {@code klass} was {@code null}
+ *
+ * @see #isOutputSupportedFor(Class)
+ */
+ public <T> Size[] getOutputSizes(final Class<T> klass) {
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * Get a list of sizes compatible with the requested image {@code format}.
+ *
+ * <p>The {@code format} should be a supported format (one of the formats returned by
+ * {@link #getOutputFormats}).</p>
+ *
+ * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
+ * @return
+ * an array of supported sizes,
+ * or {@code null} if the {@code format} is not a supported output
+ *
+ * @see ImageFormat
+ * @see PixelFormat
+ * @see #getOutputFormats
+ */
+ public Size[] getOutputSizes(final int format) {
+ try {
+ checkArgumentFormatSupported(format, /*output*/true);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration}
+ * for the format/size combination (in nanoseconds).
+ *
+ * <p>{@code format} should be one of the ones returned by {@link #getOutputFormats()}.</p>
+ * <p>{@code size} should be one of the ones returned by
+ * {@link #getOutputSizes(int)}.</p>
+ *
+ * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
+ * @param size an output-compatible size
+ * @return a minimum frame duration {@code >=} 0 in nanoseconds
+ *
+ * @throws IllegalArgumentException if {@code format} or {@code size} was not supported
+ * @throws NullPointerException if {@code size} was {@code null}
+ *
+ * @see CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS
+ * @see CaptureRequest#SENSOR_FRAME_DURATION
+ * @see ImageFormat
+ * @see PixelFormat
+ */
+ public long getOutputMinFrameDuration(final int format, final Size size) {
+ checkArgumentFormatSupported(format, /*output*/true);
+
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration}
+ * for the class/size combination (in nanoseconds).
+ *
+ * <p>This assumes a the {@code klass} is set up to use an implementation-defined format.
+ * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p>
+ *
+ * <p>{@code klass} should be one of the ones which is supported by
+ * {@link #isOutputSupportedFor(Class)}.</p>
+ *
+ * <p>{@code size} should be one of the ones returned by
+ * {@link #getOutputSizes(int)}.</p>
+ *
+ * @param klass
+ * a class which is supported by {@link #isOutputSupportedFor(Class)} and has a
+ * non-empty array returned by {@link #getOutputSizes(Class)}
+ * @param size an output-compatible size
+ * @return a minimum frame duration {@code >=} 0 in nanoseconds
+ *
+ * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
+ * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
+ *
+ * @see CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS
+ * @see CaptureRequest#SENSOR_FRAME_DURATION
+ * @see ImageFormat
+ * @see PixelFormat
+ */
+ public <T> long getOutputMinFrameDuration(final Class<T> klass, final Size size) {
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * Get the {@link CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS stall duration}
+ * for the format/size combination (in nanoseconds).
+ *
+ * <p>{@code format} should be one of the ones returned by {@link #getOutputFormats()}.</p>
+ * <p>{@code size} should be one of the ones returned by
+ * {@link #getOutputSizes(int)}.</p>
+ *
+ * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
+ * @param size an output-compatible size
+ * @return a stall duration {@code >=} 0 in nanoseconds
+ *
+ * @throws IllegalArgumentException if {@code format} or {@code size} was not supported
+ * @throws NullPointerException if {@code size} was {@code null}
+ *
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS
+ * @see ImageFormat
+ * @see PixelFormat
+ */
+ public long getOutputStallDuration(final int format, final Size size) {
+ checkArgumentFormatSupported(format, /*output*/true);
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * Get the {@link CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS stall duration}
+ * for the class/size combination (in nanoseconds).
+ *
+ * <p>This assumes a the {@code klass} is set up to use an implementation-defined format.
+ * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p>
+ *
+ * <p>{@code klass} should be one of the ones with a non-empty array returned by
+ * {@link #getOutputSizes(Class)}.</p>
+ *
+ * <p>{@code size} should be one of the ones returned by
+ * {@link #getOutputSizes(Class)}.</p>
+ *
+ * @param klass
+ * a class which is supported by {@link #isOutputSupportedFor(Class)} and has a
+ * non-empty array returned by {@link #getOutputSizes(Class)}
+ * @param size an output-compatible size
+ * @return a minimum frame duration {@code >=} 0 in nanoseconds
+ *
+ * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
+ * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
+ *
+ * @see CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS
+ * @see CaptureRequest#SENSOR_FRAME_DURATION
+ * @see ImageFormat
+ * @see PixelFormat
+ */
+ public <T> long getOutputStallDuration(final Class<T> klass, final Size size) {
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ /**
+ * Check if this {@link StreamConfigurationMap} is equal to another
+ * {@link StreamConfigurationMap}.
+ *
+ * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof StreamConfigurationMap) {
+ final StreamConfigurationMap other = (StreamConfigurationMap) obj;
+ // TODO: do we care about order?
+ return Arrays.equals(mConfigurations, other.mConfigurations) &&
+ Arrays.equals(mDurations, other.mDurations);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ // TODO: do we care about order?
+ return HashCodeHelpers.hashCode(mConfigurations) ^ HashCodeHelpers.hashCode(mDurations);
+ }
+
+ // Check that the argument is supported by #getOutputFormats or #getInputFormats
+ private int checkArgumentFormatSupported(int format, boolean output) {
+ checkArgumentFormat(format);
+
+ int[] formats = output ? getOutputFormats() : getInputFormats();
+ for (int i = 0; i < formats.length; ++i) {
+ if (format == formats[i]) {
+ return format;
+ }
+ }
+
+ throw new IllegalArgumentException(String.format(
+ "format %x is not supported by this stream configuration map", format));
+ }
+
+ /**
+ * Ensures that the format is either user-defined or implementation defined.
+ *
+ * <p>Any invalid/undefined formats will raise an exception.</p>
+ *
+ * @param format image format
+ * @return the format
+ *
+ * @throws IllegalArgumentException if the format was invalid
+ */
+ static int checkArgumentFormatInternal(int format) {
+ if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
+ return format;
+ }
+
+ return checkArgumentFormat(format);
+ }
+
+ /**
+ * Ensures that the format is user-defined in either ImageFormat or PixelFormat.
+ *
+ * <p>Any invalid/undefined formats will raise an exception, including implementation-defined.
+ * </p>
+ *
+ * <p>Note that {@code @hide} and deprecated formats will not pass this check.</p>
+ *
+ * @param format image format
+ * @return the format
+ *
+ * @throws IllegalArgumentException if the format was not user-defined
+ */
+ static int checkArgumentFormat(int format) {
+ if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
+ throw new IllegalArgumentException(String.format(
+ "format %x was not defined in either ImageFormat or PixelFormat", format));
+ }
+
+ return format;
+ }
+
+ private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
+
+ private final StreamConfiguration[] mConfigurations;
+ private final StreamConfigurationDuration[] mDurations;
+
+}
diff --git a/core/java/android/hardware/camera2/TonemapCurve.java b/core/java/android/hardware/camera2/TonemapCurve.java
new file mode 100644
index 0000000..ee20d68
--- /dev/null
+++ b/core/java/android/hardware/camera2/TonemapCurve.java
@@ -0,0 +1,290 @@
+/*
+ * 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.hardware.camera2;
+
+import static com.android.internal.util.Preconditions.*;
+
+import android.graphics.PointF;
+import android.hardware.camera2.impl.HashCodeHelpers;
+
+import java.util.Arrays;
+
+/**
+ * Immutable class for describing a {@code 2 x M x 3} tonemap curve of floats.
+ *
+ * <p>This defines red, green, and blue curves that the {@link CameraDevice} will
+ * use as the tonemapping/contrast/gamma curve when {@link CaptureRequest#TONEMAP_MODE} is
+ * set to {@link CameraMetadata#TONEMAP_MODE_CONTRAST_CURVE}.</p>
+ *
+ * <p>The total number of points {@code (Pin, Pout)} for each color channel can be no more than
+ * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS}.</p>
+ *
+ * <p>The coordinate system for each point is within the inclusive range
+ * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p>
+ *
+ * @see CaptureRequest#TONEMAP_CURVE_BLUE
+ * @see CaptureRequest#TONEMAP_CURVE_GREEN
+ * @see CaptureRequest#TONEMAP_CURVE_RED
+ * @see CameraMetadata#TONEMAP_MODE_CONTRAST_CURVE
+ * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
+ */
+public final class TonemapCurve {
+ /**
+ * Lower bound tonemap value corresponding to pure black for a single color channel.
+ */
+ public static final float LEVEL_BLACK = 0.0f;
+
+ /**
+ * Upper bound tonemap value corresponding to a pure white for a single color channel.
+ */
+ public static final float LEVEL_WHITE = 1.0f;
+
+ /**
+ * Number of elements in a {@code (Pin, Pout)} point;
+ */
+ public static final int POINT_SIZE = 2;
+
+ /**
+ * Index of the red color channel curve.
+ */
+ public static final int CHANNEL_RED = 0;
+ /**
+ * Index of the green color channel curve.
+ */
+ public static final int CHANNEL_GREEN = 1;
+ /**
+ * Index of the blue color channel curve.
+ */
+ public static final int CHANNEL_BLUE = 2;
+
+ /**
+ * Create a new immutable TonemapCurve instance.
+ *
+ * <p>Values are stored as a contiguous {@code (Pin, Pout}) point.</p>
+ *
+ * <p>All parameters may have independent length but should have at most
+ * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS} * {@value #POINT_SIZE} elements.</p>
+ *
+ * <p>All sub-elements must be in the inclusive range of
+ * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p>
+ *
+ * <p>This constructor copies the array contents and does not retain ownership of the array.</p>
+ *
+ * @param elements An array of elements whose length is {@code CHANNEL_COUNT * rows * columns}
+ *
+ * @throws IllegalArgumentException
+ * if the {@code elements} array length is invalid,
+ * if any of the subelems are not finite
+ * @throws NullPointerException
+ * if any of the parameters is {@code null}
+ *
+ * @hide
+ */
+ public TonemapCurve(float[] red, float[] green, float[] blue) {
+ // TODO: maxCurvePoints check?
+
+ checkNotNull(red, "red must not be null");
+ checkNotNull(green, "green must not be null");
+ checkNotNull(blue, "blue must not be null");
+
+ checkArgumentArrayLengthDivisibleBy(red, POINT_SIZE, "red");
+ checkArgumentArrayLengthDivisibleBy(green, POINT_SIZE, "green");
+ checkArgumentArrayLengthDivisibleBy(blue, POINT_SIZE, "blue");
+
+ checkArrayElementsInRange(red, LEVEL_BLACK, LEVEL_WHITE, "red");
+ checkArrayElementsInRange(green, LEVEL_BLACK, LEVEL_WHITE, "green");
+ checkArrayElementsInRange(blue, LEVEL_BLACK, LEVEL_WHITE, "blue");
+
+ mRed = Arrays.copyOf(red, red.length);
+ mGreen = Arrays.copyOf(green, green.length);
+ mBlue = Arrays.copyOf(blue, blue.length);
+ }
+
+ private static void checkArgumentArrayLengthDivisibleBy(float[] array,
+ int divisible, String arrayName) {
+ if (array.length % divisible != 0) {
+ throw new IllegalArgumentException(arrayName + " size must be divisible by "
+ + divisible);
+ }
+ }
+
+ private static int checkArgumentColorChannel(int colorChannel) {
+ switch (colorChannel) {
+ case CHANNEL_RED:
+ case CHANNEL_GREEN:
+ case CHANNEL_BLUE:
+ break;
+ default:
+ throw new IllegalArgumentException("colorChannel out of range");
+ }
+
+ return colorChannel;
+ }
+
+ /**
+ * Get the number of points stored in this tonemap curve for the specified color channel.
+ *
+ * @param colorChannel one of {@link #CHANNEL_RED}, {@link #CHANNEL_GREEN}, {@link #CHANNEL_BLUE}
+ * @return number of points stored in this tonemap for that color's curve (>= 0)
+ *
+ * @throws IllegalArgumentException if {@code colorChannel} was out of range
+ */
+ public int getPointCount(int colorChannel) {
+ checkArgumentColorChannel(colorChannel);
+
+ return getCurve(colorChannel).length / POINT_SIZE;
+ }
+
+ /**
+ * Get the point for a color channel at a specified index.
+ *
+ * <p>The index must be at least 0 but no greater than {@link #getPointCount(int)} for
+ * that {@code colorChannel}.</p>
+ *
+ * <p>All returned coordinates in the point are between the range of
+ * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p>
+ *
+ * @param colorChannel {@link #CHANNEL_RED}, {@link #CHANNEL_GREEN}, or {@link #CHANNEL_BLUE}
+ * @param index at least 0 but no greater than {@code getPointCount(colorChannel)}
+ * @return the {@code (Pin, Pout)} pair mapping the tone for that index
+ *
+ * @throws IllegalArgumentException if {@code colorChannel} or {@code index} was out of range
+ *
+ * @see #LEVEL_BLACK
+ * @see #LEVEL_WHITE
+ */
+ public PointF getPoint(int colorChannel, int index) {
+ checkArgumentColorChannel(colorChannel);
+ if (index < 0 || index >= getPointCount(colorChannel)) {
+ throw new IllegalArgumentException("index out of range");
+ }
+
+ final float[] curve = getCurve(colorChannel);
+
+ final float pIn = curve[index * POINT_SIZE + OFFSET_POINT_IN];
+ final float pOut = curve[index * POINT_SIZE + OFFSET_POINT_OUT];
+
+ return new PointF(pIn, pOut);
+ }
+
+ /**
+ * Copy the color curve for a single color channel from this tonemap curve into the destination.
+ *
+ * <p>
+ * <!--The output is encoded the same as in the constructor -->
+ * Values are stored as packed {@code (Pin, Pout}) points, and there are a total of
+ * {@link #getPointCount} points for that respective channel.</p>
+ *
+ * <p>All returned coordinates are between the range of
+ * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p>
+ *
+ * @param destination
+ * an array big enough to hold at least {@link #getPointCount} {@code *}
+ * {@link #POINT_SIZE} elements after the {@code offset}
+ * @param offset
+ * a non-negative offset into the array
+ * @throws NullPointerException
+ * If {@code destination} was {@code null}
+ * @throws IllegalArgumentException
+ * If offset was negative
+ * @throws ArrayIndexOutOfBoundsException
+ * If there's not enough room to write the elements at the specified destination and
+ * offset.
+ *
+ * @see CaptureRequest#TONEMAP_CURVE_BLUE
+ * @see CaptureRequest#TONEMAP_CURVE_RED
+ * @see CaptureRequest#TONEMAP_CURVE_GREEN
+ * @see #LEVEL_BLACK
+ * @see #LEVEL_WHITE
+ */
+ public void copyColorCurve(int colorChannel, float[] destination,
+ int offset) {
+ checkArgumentNonnegative(offset, "offset must not be negative");
+ checkNotNull(destination, "destination must not be null");
+
+ if (destination.length + offset < getPointCount(colorChannel) * POINT_SIZE) {
+ throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
+ }
+
+ float[] curve = getCurve(colorChannel);
+ System.arraycopy(curve, /*srcPos*/0, destination, offset, curve.length);
+ }
+
+ /**
+ * Check if this TonemapCurve is equal to another TonemapCurve.
+ *
+ * <p>Two matrices are equal if and only if all of their elements are
+ * {@link Object#equals equal}.</p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof TonemapCurve) {
+ final TonemapCurve other = (TonemapCurve) obj;
+ return Arrays.equals(mRed, other.mRed) &&
+ Arrays.equals(mGreen, other.mGreen) &&
+ Arrays.equals(mBlue, other.mBlue);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ if (mHashCalculated) {
+ // Avoid re-calculating hash. Data is immutable so this is both legal and faster.
+ return mHashCode;
+ }
+
+ mHashCode = HashCodeHelpers.hashCode(mRed, mGreen, mBlue);
+ mHashCalculated = true;
+
+ return mHashCode;
+ }
+
+ private float[] getCurve(int colorChannel) {
+ switch (colorChannel) {
+ case CHANNEL_RED:
+ return mRed;
+ case CHANNEL_GREEN:
+ return mGreen;
+ case CHANNEL_BLUE:
+ return mBlue;
+ default:
+ throw new AssertionError("colorChannel out of range");
+ }
+ }
+
+ private final static int OFFSET_POINT_IN = 0;
+ private final static int OFFSET_POINT_OUT = 1;
+
+ private final float[] mRed;
+ private final float[] mGreen;
+ private final float[] mBlue;
+
+ private int mHashCode;
+ private boolean mHashCalculated = false;
+};
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index 40a7905..988f8f9 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -647,7 +647,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
* should have arrived. The following line checks whether this holds.
*/
if (frameNumber != mCompletedFrameNumber + 1) {
- throw new AssertionError(String.format(
+ Log.e(TAG, String.format(
"result frame number %d comes out of order, should be %d + 1",
frameNumber, mCompletedFrameNumber));
}
diff --git a/core/java/android/hardware/camera2/impl/HashCodeHelpers.java b/core/java/android/hardware/camera2/impl/HashCodeHelpers.java
new file mode 100644
index 0000000..2d63827
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/HashCodeHelpers.java
@@ -0,0 +1,143 @@
+/*
+ * 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.hardware.camera2.impl;
+
+/**
+ * Provide hashing functions using the Modified Bernstein hash
+ */
+public final class HashCodeHelpers {
+
+ /**
+ * Hash every element uniformly using the Modified Bernstein hash.
+ *
+ * <p>Useful to implement a {@link Object#hashCode} for uniformly distributed data.</p>
+ *
+ * @param array a non-{@code null} array of integers
+ *
+ * @return the numeric hash code
+ */
+ public static int hashCode(int[] array) {
+ if (array == null) {
+ return 0;
+ }
+
+ /*
+ * Note that we use 31 here instead of 33 since it's preferred in Effective Java
+ * and used elsewhere in the runtime (e.g. Arrays#hashCode)
+ *
+ * That being said 33 and 31 are nearly identical in terms of their usefulness
+ * according to http://svn.apache.org/repos/asf/apr/apr/trunk/tables/apr_hash.c
+ */
+ int h = 1;
+ for (int x : array) {
+ // Strength reduction; in case the compiler has illusions about divisions being faster
+ h = ((h << 5) - h) ^ x; // (h * 31) XOR x
+ }
+
+ return h;
+ }
+
+ /**
+ * Hash every element uniformly using the Modified Bernstein hash.
+ *
+ * <p>Useful to implement a {@link Object#hashCode} for uniformly distributed data.</p>
+ *
+ * @param array a non-{@code null} array of floats
+ *
+ * @return the numeric hash code
+ */
+ public static int hashCode(float[] array) {
+ if (array == null) {
+ return 0;
+ }
+
+ int h = 1;
+ for (float f : array) {
+ int x = Float.floatToIntBits(f);
+ h = ((h << 5) - h) ^ x; // (h * 31) XOR x
+ }
+
+ return h;
+ }
+
+ /**
+ * Hash every element uniformly using the Modified Bernstein hash.
+ *
+ * <p>Useful to implement a {@link Object#hashCode} for uniformly distributed data.</p>
+ *
+ * @param array a non-{@code null} array of objects
+ *
+ * @return the numeric hash code
+ */
+ public static <T> int hashCode(T[] array) {
+ if (array == null) {
+ return 0;
+ }
+
+ int h = 1;
+ for (T o : array) {
+ int x = (o == null) ? 0 : o.hashCode();
+ h = ((h << 5) - h) ^ x; // (h * 31) XOR x
+ }
+
+ return h;
+ }
+
+ public static <T> int hashCode(T a) {
+ return (a == null) ? 0 : a.hashCode();
+ }
+
+ public static <T> int hashCode(T a, T b) {
+ int h = hashCode(a);
+
+ int x = (b == null) ? 0 : b.hashCode();
+ h = ((h << 5) - h) ^ x; // (h * 31) XOR x
+
+ return h;
+ }
+
+ public static <T> int hashCode(T a, T b, T c) {
+ int h = hashCode(a, b);
+
+ int x = (a == null) ? 0 : a.hashCode();
+ h = ((h << 5) - h) ^ x; // (h * 31) XOR x
+
+ return h;
+ }
+
+ public static int hashCode(int x) {
+ return hashCode(new int[] { x } );
+ }
+
+ public static int hashCode(int x, int y) {
+ return hashCode(new int[] { x, y } );
+ }
+
+ public static int hashCode(int x, int y, int z) {
+ return hashCode(new int[] { x, y, z } );
+ }
+
+ public static int hashCode(int x, int y, int z, int w) {
+ return hashCode(new int[] { x, y, z, w } );
+ }
+
+ public static int hashCode(int x, int y, int z, int w, int t) {
+ return hashCode(new int[] { x, y, z, w, t } );
+ }
+
+
+}
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
index 8578a32..7213c78 100644
--- a/core/java/android/hardware/hdmi/HdmiCec.java
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -85,7 +85,7 @@ public final class HdmiCec {
public static final int ADDR_RESERVED_2 = 13;
/** Logical address for TV other than the one assigned with {@link #ADDR_TV} */
- public static final int ADDR_FREE_USE = 14;
+ public static final int ADDR_SPECIFIC_USE = 14;
/** Logical address for devices to which address cannot be allocated */
public static final int ADDR_UNREGISTERED = 15;
@@ -160,6 +160,8 @@ public final class HdmiCec {
public static final int MESSAGE_SET_EXTERNAL_TIMER = 0xA2;
public static final int MESSAGE_ABORT = 0xFF;
+ public static final int UNKNOWN_VENDOR_ID = 0xFFFFFF;
+
public static final int POWER_STATUS_UNKNOWN = -1;
public static final int POWER_STATUS_ON = 0;
public static final int POWER_STATUS_STANDBY = 1;
@@ -179,6 +181,7 @@ public final class HdmiCec {
DEVICE_RECORDER, // ADDR_RECORDER_3
DEVICE_TUNER, // ADDR_TUNER_4
DEVICE_PLAYBACK, // ADDR_PLAYBACK_3
+ DEVICE_TV, // ADDR_SPECIFIC_USE
};
private static final String[] DEFAULT_NAMES = {
@@ -194,6 +197,7 @@ public final class HdmiCec {
"Recorder_3",
"Tuner_4",
"Playback_3",
+ "Secondary_TV",
};
private HdmiCec() { } // Prevents instantiation.
@@ -221,9 +225,7 @@ public final class HdmiCec {
* @return true if the given address is valid
*/
public static boolean isValidAddress(int address) {
- // TODO: We leave out the address 'free use(14)' for now. Check this later
- // again to make sure it is a valid address for communication.
- return (ADDR_TV <= address && address <= ADDR_PLAYBACK_3);
+ return (ADDR_TV <= address && address <= ADDR_SPECIFIC_USE);
}
/**
diff --git a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
new file mode 100644
index 0000000..9698445
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
@@ -0,0 +1,168 @@
+/*
+ * 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.hardware.hdmi;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class to encapsulate device information for HDMI-CEC. This container
+ * include basic information such as logical address, physical address and
+ * device type, and additional information like vendor id and osd name.
+ */
+public final class HdmiCecDeviceInfo implements Parcelable {
+ // Logical address, phsical address, device type, vendor id and display name
+ // are immutable value.
+ private final int mLogicalAddress;
+ private final int mPhysicalAddress;
+ private final int mDeviceType;
+ private final int mVendorId;
+ private final String mDisplayName;
+
+
+ /**
+ * A helper class to deserialize {@link HdmiCecDeviceInfo} for a parcel.
+ */
+ public static final Parcelable.Creator<HdmiCecDeviceInfo> CREATOR =
+ new Parcelable.Creator<HdmiCecDeviceInfo>() {
+ @Override
+ public HdmiCecDeviceInfo createFromParcel(Parcel source) {
+ int logicalAddress = source.readInt();
+ int physicalAddress = source.readInt();
+ int deviceType = source.readInt();
+ int vendorId = source.readInt();
+ String displayName = source.readString();
+ return new HdmiCecDeviceInfo(logicalAddress, physicalAddress, deviceType,
+ vendorId, displayName);
+ }
+
+ @Override
+ public HdmiCecDeviceInfo[] newArray(int size) {
+ return new HdmiCecDeviceInfo[size];
+ }
+ };
+
+ /**
+ * Constructor.
+ *
+ * @param logicalAddress logical address of HDMI-Cec device.
+ * For more details, refer {@link HdmiCec}
+ * @param physicalAddress physical address of HDMI-Cec device
+ * @param deviceType type of device. For more details, refer {@link HdmiCec}
+ * @param vendorId vendor id of device. It's used for vendor specific command
+ * @param displayName name of device
+ * @hide
+ */
+ public HdmiCecDeviceInfo(int logicalAddress, int physicalAddress, int deviceType,
+ int vendorId, String displayName) {
+ mLogicalAddress = logicalAddress;
+ mPhysicalAddress = physicalAddress;
+ mDeviceType = deviceType;
+ mDisplayName = displayName;
+ mVendorId = vendorId;
+ }
+
+ /**
+ * Return the logical address of the device. It can have 0-15 values.
+ * For more details, refer constants between {@link HdmiCec#ADDR_TV}
+ * and {@link HdmiCec#ADDR_UNREGISTERED}.
+ */
+ public int getLogicalAddress() {
+ return mLogicalAddress;
+ }
+
+ /**
+ * Return the physical address of the device.
+ */
+ public int getPhysicalAddress() {
+ return mPhysicalAddress;
+ }
+
+ /**
+ * Return type of the device. For more details, refer constants between
+ * {@link HdmiCec#DEVICE_TV} and {@link HdmiCec#DEVICE_INACTIVE}.
+ */
+ public int getDeviceType() {
+ return mDeviceType;
+ }
+
+ /**
+ * Return display (OSD) name of the device.
+ */
+ public String getDisplayName() {
+ return mDisplayName;
+ }
+
+ /**
+ * Return vendor id of the device. Vendor id is used to distinguish devices
+ * built by other manufactures. This is required for vendor-specific command
+ * on CEC standard.
+ */
+ public int getVendorId() {
+ return mVendorId;
+ }
+
+ /**
+ * Describe the kinds of special objects contained in this Parcelable's
+ * marshalled representation.
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Serialize this object into a {@link Parcel}.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE}.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mLogicalAddress);
+ dest.writeInt(mPhysicalAddress);
+ dest.writeInt(mDeviceType);
+ dest.writeInt(mVendorId);
+ dest.writeString(mDisplayName);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer s = new StringBuffer();
+ s.append("logical_address: ").append(mLogicalAddress).append(", ");
+ s.append("physical_address: ").append(mPhysicalAddress).append(", ");
+ s.append("device_type: ").append(mDeviceType).append(", ");
+ s.append("vendor_id: ").append(mVendorId).append(", ");
+ s.append("display_name: ").append(mDisplayName);
+ return s.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof HdmiCecDeviceInfo)) {
+ return false;
+ }
+
+ HdmiCecDeviceInfo other = (HdmiCecDeviceInfo) obj;
+ return mLogicalAddress == other.mLogicalAddress
+ && mPhysicalAddress == other.mPhysicalAddress
+ && mDeviceType == other.mDeviceType
+ && mVendorId == other.mVendorId
+ && mDisplayName.equals(other.mDisplayName);
+ }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.java b/core/java/android/hardware/hdmi/HdmiCecMessage.java
index be94d97..ddaf870 100644
--- a/core/java/android/hardware/hdmi/HdmiCecMessage.java
+++ b/core/java/android/hardware/hdmi/HdmiCecMessage.java
@@ -19,6 +19,8 @@ package android.hardware.hdmi;
import android.os.Parcel;
import android.os.Parcelable;
+import libcore.util.EmptyArray;
+
import java.util.Arrays;
/**
@@ -28,6 +30,8 @@ import java.util.Arrays;
*/
public final class HdmiCecMessage implements Parcelable {
+ public static final byte[] EMPTY_PARAM = EmptyArray.BYTE;
+
private static final int MAX_MESSAGE_LENGTH = 16;
private final int mSource;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 505ef9c..f6438b4 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -666,7 +666,8 @@ public class InputMethodService extends AbstractInputMethodService {
mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
mInflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
- mWindow = new SoftInputWindow(this, mTheme, mDispatcherState);
+ mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
+ false);
if (mHardwareAccelerated) {
mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
}
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index df1afee..a9bace1 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -30,11 +30,20 @@ import android.view.WindowManager;
* method window. It will be displayed along the edge of the screen, moving
* the application user interface away from it so that the focused item is
* always visible.
+ * @hide
*/
-class SoftInputWindow extends Dialog {
+public class SoftInputWindow extends Dialog {
+ final String mName;
+ final Callback mCallback;
+ final KeyEvent.Callback mKeyEventCallback;
final KeyEvent.DispatcherState mDispatcherState;
+ final boolean mTakesFocus;
private final Rect mBounds = new Rect();
-
+
+ public interface Callback {
+ public void onBackPressed();
+ }
+
public void setToken(IBinder token) {
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.token = token;
@@ -53,10 +62,15 @@ class SoftInputWindow extends Dialog {
* using styles. This theme is applied on top of the current theme in
* <var>context</var>. If 0, the default dialog theme will be used.
*/
- public SoftInputWindow(Context context, int theme,
- KeyEvent.DispatcherState dispatcherState) {
+ public SoftInputWindow(Context context, String name, int theme, Callback callback,
+ KeyEvent.Callback keyEventCallback, KeyEvent.DispatcherState dispatcherState,
+ boolean takesFocus) {
super(context, theme);
+ mName = name;
+ mCallback = callback;
+ mKeyEventCallback = keyEventCallback;
mDispatcherState = dispatcherState;
+ mTakesFocus = takesFocus;
initDockWindow();
}
@@ -148,11 +162,47 @@ class SoftInputWindow extends Dialog {
}
}
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (mKeyEventCallback != null && mKeyEventCallback.onKeyDown(keyCode, event)) {
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+ if (mKeyEventCallback != null && mKeyEventCallback.onKeyLongPress(keyCode, event)) {
+ return true;
+ }
+ return super.onKeyLongPress(keyCode, event);
+ }
+
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (mKeyEventCallback != null && mKeyEventCallback.onKeyUp(keyCode, event)) {
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
+ if (mKeyEventCallback != null && mKeyEventCallback.onKeyMultiple(keyCode, count, event)) {
+ return true;
+ }
+ return super.onKeyMultiple(keyCode, count, event);
+ }
+
+ public void onBackPressed() {
+ if (mCallback != null) {
+ mCallback.onBackPressed();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
private void initDockWindow() {
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.type = WindowManager.LayoutParams.TYPE_INPUT_METHOD;
- lp.setTitle("InputMethod");
+ lp.setTitle(mName);
lp.gravity = Gravity.BOTTOM;
lp.width = -1;
@@ -161,11 +211,19 @@ class SoftInputWindow extends Dialog {
//lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
getWindow().setAttributes(lp);
- getWindow().setFlags(
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
+
+ int windowSetFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+ int windowModFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
- WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+
+ if (!mTakesFocus) {
+ windowSetFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ } else {
+ windowSetFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ windowModFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ }
+
+ getWindow().setFlags(windowSetFlags, windowModFlags);
}
}
diff --git a/core/java/android/net/INetworkScoreCache.aidl b/core/java/android/net/INetworkScoreCache.aidl
new file mode 100644
index 0000000..35601ce
--- /dev/null
+++ b/core/java/android/net/INetworkScoreCache.aidl
@@ -0,0 +1,43 @@
+/**
+ * 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.net.ScoredNetwork;
+
+/**
+ * A service which stores a subset of scored networks from the active network scorer.
+ *
+ * <p>To be implemented by network subsystems (e.g. Wi-Fi). NetworkScoreService will propagate
+ * scores down to each subsystem depending on the network type. Implementations may register for
+ * a given network type by calling NetworkScoreManager.registerNetworkSubsystem.
+ *
+ * <p>A proper implementation should throw SecurityException whenever the caller is not privileged.
+ * It may request scores by calling NetworkScoreManager#requestScores(NetworkKey[]); a call to
+ * updateScores may follow but may not depending on the active scorer's implementation, and in
+ * general this method may be called at any time.
+ *
+ * <p>Implementations should also override dump() so that "adb shell dumpsys network_score" includes
+ * the current scores for each network for debugging purposes.
+ * @hide
+ */
+interface INetworkScoreCache
+{
+ void updateScores(in List<ScoredNetwork> networks);
+
+ void clearScores();
+}
+
diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl
index a72d9a0..626bd2a 100644
--- a/core/java/android/net/INetworkScoreService.aidl
+++ b/core/java/android/net/INetworkScoreService.aidl
@@ -16,6 +16,7 @@
package android.net;
+import android.net.INetworkScoreCache;
import android.net.ScoredNetwork;
/**
@@ -34,8 +35,7 @@ interface INetworkScoreService
/**
* Clear all scores.
* @return whether the clear was successful.
- * @throws SecurityException if the caller is neither the current active scorer nor the scorer
- * manager.
+ * @throws SecurityException if the caller is neither the current active scorer nor the system.
*/
boolean clearScores();
@@ -43,7 +43,19 @@ interface INetworkScoreService
* Set the active scorer and clear existing scores.
* @param packageName the package name of the new scorer to use.
* @return true if the operation succeeded, or false if the new package is not a valid scorer.
- * @throws SecurityException if the caller is not the scorer manager.
+ * @throws SecurityException if the caller is not the system.
*/
boolean setActiveScorer(in String packageName);
+
+ /**
+ * Register a network subsystem for scoring.
+ *
+ * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
+ * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
+ * @throws SecurityException if the caller is not the system.
+ * @throws IllegalArgumentException if a score cache is already registed for this type.
+ * @hide
+ */
+ void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache);
+
}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index a470e88..30b61c5 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -54,7 +54,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class MobileDataStateTracker extends BaseNetworkStateTracker {
private static final String TAG = "MobileDataStateTracker";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false;
private PhoneConstants.DataState mMobileDataState;
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 6dd56d9..352512e 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -19,6 +19,7 @@ package android.net;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
+import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -101,7 +102,7 @@ public class NetworkScoreManager {
* determine the current scorer and offer the user the ability to select a different scorer via
* the {@link #ACTION_CHANGE_ACTIVE} intent.
* @return the full package name of the current active scorer, or null if there is no active
- * scorer.
+ * scorer.
*/
public String getActiveScorerPackage() {
return NetworkScorerAppManager.getActiveScorer(mContext);
@@ -151,8 +152,8 @@ public class NetworkScoreManager {
*
* @return true if the operation succeeded, or false if the new package is not a valid scorer.
* @throws SecurityException if the caller does not hold the
- * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission indicating that
- * it can manage scorer applications.
+ * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission indicating
+ * that it can manage scorer applications.
* @hide
*/
public boolean setActiveScorer(String packageName) throws SecurityException {
@@ -162,4 +163,44 @@ public class NetworkScoreManager {
return false;
}
}
+
+ /**
+ * Request scoring for networks.
+ *
+ * <p>Note that this is just a helper method to assemble the broadcast, and will run in the
+ * calling process.
+ *
+ * @return true if the broadcast was sent, or false if there is no active scorer.
+ * @throws SecurityException if the caller does not hold the
+ * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission.
+ * @hide
+ */
+ public boolean requestScores(NetworkKey[] networks) throws SecurityException {
+ String activeScorer = getActiveScorerPackage();
+ if (activeScorer == null) {
+ return false;
+ }
+ Intent intent = new Intent(ACTION_SCORE_NETWORKS);
+ intent.setPackage(activeScorer);
+ intent.putExtra(EXTRA_NETWORKS_TO_SCORE, networks);
+ mContext.sendBroadcast(intent);
+ return true;
+ }
+
+ /**
+ * Register a network score cache.
+ *
+ * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
+ * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
+ * @throws SecurityException if the caller does not hold the
+ * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission.
+ * @throws IllegalArgumentException if a score cache is already registered for this type.
+ * @hide
+ */
+ public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
+ try {
+ mService.registerNetworkScoreCache(networkType, scoreCache);
+ } catch (RemoteException e) {
+ }
+ }
}
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index 033332c..bea8d1c 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -274,48 +274,6 @@ public final class Proxy {
return PROXY_VALID;
}
- static class AndroidProxySelectorRoutePlanner
- extends org.apache.http.impl.conn.ProxySelectorRoutePlanner {
-
- private Context mContext;
-
- public AndroidProxySelectorRoutePlanner(SchemeRegistry schreg, ProxySelector prosel,
- Context context) {
- super(schreg, prosel);
- mContext = context;
- }
-
- @Override
- protected java.net.Proxy chooseProxy(List<java.net.Proxy> proxies, HttpHost target,
- HttpRequest request, HttpContext context) {
- return getProxy(mContext, target.getHostName());
- }
-
- @Override
- protected HttpHost determineProxy(HttpHost target, HttpRequest request,
- HttpContext context) {
- return getPreferredHttpHost(mContext, target.getHostName());
- }
-
- @Override
- public HttpRoute determineRoute(HttpHost target, HttpRequest request,
- HttpContext context) {
- HttpHost proxy = getPreferredHttpHost(mContext, target.getHostName());
- if (proxy == null) {
- return new HttpRoute(target);
- } else {
- return new HttpRoute(target, null, proxy, false);
- }
- }
- }
-
- /** @hide */
- public static final HttpRoutePlanner getAndroidProxySelectorRoutePlanner(Context context) {
- AndroidProxySelectorRoutePlanner ret = new AndroidProxySelectorRoutePlanner(
- new SchemeRegistry(), ProxySelector.getDefault(), context);
- return ret;
- }
-
/** @hide */
public static final void setHttpProxySystemProperty(ProxyProperties p) {
String host = null;
diff --git a/core/java/android/net/RssiCurve.java b/core/java/android/net/RssiCurve.java
index 33e81c2..dd744d3 100644
--- a/core/java/android/net/RssiCurve.java
+++ b/core/java/android/net/RssiCurve.java
@@ -98,6 +98,27 @@ public class RssiCurve implements Parcelable {
}
/**
+ * Lookup the score for a given RSSI value.
+ *
+ * @param rssi The RSSI to lookup. If the RSSI falls below the start of the curve, the score at
+ * the start of the curve will be returned. If it falls after the end of the curve, the
+ * score at the end of the curve will be returned.
+ * @return the score for the given RSSI.
+ */
+ public byte lookupScore(int rssi) {
+ int index = (rssi - start) / bucketWidth;
+
+ // Snap the index to the closest bucket if it falls outside the curve.
+ if (index < 0) {
+ index = 0;
+ } else if (index > rssiBuckets.length - 1) {
+ index = rssiBuckets.length - 1;
+ }
+
+ return rssiBuckets[index];
+ }
+
+ /**
* Determine if two RSSI curves are defined in the same way.
*
* <p>Note that two curves can be equivalent but defined differently, e.g. if one bucket in one
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index f05ddde..e78ce33 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -173,6 +173,10 @@ public abstract class BatteryStats implements Parcelable {
private static final String BLUETOOTH_STATE_COUNT_DATA = "bsc";
private static final String POWER_USE_SUMMARY_DATA = "pws";
private static final String POWER_USE_ITEM_DATA = "pwi";
+ private static final String DISCHARGE_STEP_DATA = "dsd";
+ private static final String CHARGE_STEP_DATA = "csd";
+ private static final String DISCHARGE_TIME_REMAIN_DATA = "dtr";
+ private static final String CHARGE_TIME_REMAIN_DATA = "ctr";
private final StringBuilder mFormatBuilder = new StringBuilder(32);
private final Formatter mFormatter = new Formatter(mFormatBuilder);
@@ -907,6 +911,8 @@ public abstract class BatteryStats implements Parcelable {
*/
public abstract int getScreenOnCount(int which);
+ public abstract long getInteractiveTime(long elapsedRealtimeUs, int which);
+
public static final int SCREEN_BRIGHTNESS_DARK = 0;
public static final int SCREEN_BRIGHTNESS_DIM = 1;
public static final int SCREEN_BRIGHTNESS_MEDIUM = 2;
@@ -932,8 +938,6 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getScreenBrightnessTime(int brightnessBin,
long elapsedRealtimeUs, int which);
- public abstract int getInputEventCount(int which);
-
/**
* Returns the time in microseconds that the phone has been on while the device was
* running on battery.
@@ -1340,6 +1344,18 @@ public abstract class BatteryStats implements Parcelable {
public abstract long computeBatteryTimeRemaining(long curTime);
/**
+ * Return the historical number of discharge steps we currently have.
+ */
+ public abstract int getNumDischargeStepDurations();
+
+ /**
+ * Return the array of discharge step durations; the number of valid
+ * items in it is returned by {@link #getNumDischargeStepDurations()}.
+ * These values are in milliseconds.
+ */
+ public abstract long[] getDischargeStepDurationsArray();
+
+ /**
* Compute an approximation for how much time (in microseconds) remains until the battery
* is fully charged. Returns -1 if no time can be computed: either there is not
* enough current data to make a decision, or the battery is currently
@@ -1349,6 +1365,18 @@ public abstract class BatteryStats implements Parcelable {
*/
public abstract long computeChargeTimeRemaining(long curTime);
+ /**
+ * Return the historical number of charge steps we currently have.
+ */
+ public abstract int getNumChargeStepDurations();
+
+ /**
+ * Return the array of charge step durations; the number of valid
+ * items in it is returned by {@link #getNumChargeStepDurations()}.
+ * These values are in milliseconds.
+ */
+ public abstract long[] getChargeStepDurationsArray();
+
public abstract Map<String, ? extends LongCounter> getWakeupReasonStats();
public abstract Map<String, ? extends Timer> getKernelWakelockStats();
@@ -1543,6 +1571,7 @@ public abstract class BatteryStats implements Parcelable {
final long totalRealtime = computeRealtime(rawRealtime, which);
final long totalUptime = computeUptime(rawUptime, which);
final long screenOnTime = getScreenOnTime(rawRealtime, which);
+ final long interactiveTime = getInteractiveTime(rawRealtime, which);
final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
final long wifiOnTime = getWifiOnTime(rawRealtime, which);
final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which);
@@ -1611,8 +1640,8 @@ public abstract class BatteryStats implements Parcelable {
wifiRunningTime / 1000, bluetoothOnTime / 1000,
mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes,
fullWakeLockTimeTotal, partialWakeLockTimeTotal,
- getInputEventCount(which), getMobileRadioActiveTime(rawRealtime, which),
- getMobileRadioActiveAdjustedTime(which));
+ 0 /*legacy input event count*/, getMobileRadioActiveTime(rawRealtime, which),
+ getMobileRadioActiveAdjustedTime(which), interactiveTime / 1000);
// Dump screen brightness stats
Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -1985,6 +2014,7 @@ public abstract class BatteryStats implements Parcelable {
sb.append("realtime, ");
formatTimeMs(sb, totalUptime / 1000);
sb.append("uptime");
+ pw.println(sb.toString());
if (batteryTimeRemaining >= 0) {
sb.setLength(0);
sb.append(prefix);
@@ -2003,16 +2033,25 @@ public abstract class BatteryStats implements Parcelable {
pw.println(DateFormat.format("yyyy-MM-dd-HH-mm-ss", getStartClockTime()).toString());
final long screenOnTime = getScreenOnTime(rawRealtime, which);
+ final long interactiveTime = getInteractiveTime(rawRealtime, which);
final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which);
final long wifiOnTime = getWifiOnTime(rawRealtime, which);
final long bluetoothOnTime = getBluetoothOnTime(rawRealtime, which);
sb.setLength(0);
sb.append(prefix);
+ sb.append(" Interactive: "); formatTimeMs(sb, interactiveTime / 1000);
+ sb.append("("); sb.append(formatRatioLocked(interactiveTime, whichBatteryRealtime));
+ sb.append(")");
+ pw.println(sb.toString());
+ sb.setLength(0);
+ sb.append(prefix);
sb.append(" Screen on: "); formatTimeMs(sb, screenOnTime / 1000);
sb.append("("); sb.append(formatRatioLocked(screenOnTime, whichBatteryRealtime));
sb.append(") "); sb.append(getScreenOnCount(which));
- sb.append("x, Input events: "); sb.append(getInputEventCount(which));
+ sb.append("x, Active phone call: "); formatTimeMs(sb, phoneOnTime / 1000);
+ sb.append("("); sb.append(formatRatioLocked(phoneOnTime, whichBatteryRealtime));
+ sb.append(")");
pw.println(sb.toString());
if (phoneOnTime != 0) {
sb.setLength(0);
@@ -2871,7 +2910,7 @@ public abstract class BatteryStats implements Parcelable {
}
}
if (!didWake && wakelockTag != null) {
- pw.print(longNames ? "wake_lock=" : "w=");
+ pw.print(longNames ? " wake_lock=" : ",w=");
if (longNames) {
UserHandle.formatUid(pw, wakelockTag.uid);
pw.print(":\"");
@@ -3050,7 +3089,7 @@ public abstract class BatteryStats implements Parcelable {
HISTORY_STATE2_DESCRIPTIONS, !checkin);
if (rec.wakeReasonTag != null) {
if (checkin) {
- pw.print(",Wr=");
+ pw.print(",wr=");
pw.print(rec.wakeReasonTag.poolIdx);
} else {
pw.print(" wake_reason=");
@@ -3120,6 +3159,28 @@ public abstract class BatteryStats implements Parcelable {
pw.print(suffix);
}
+ private static boolean dumpDurationSteps(PrintWriter pw, String header, long[] steps,
+ int count, boolean checkin) {
+ if (count <= 0) {
+ return false;
+ }
+ if (!checkin) {
+ pw.println(header);
+ }
+ String[] lineArgs = new String[1];
+ for (int i=0; i<count; i++) {
+ if (checkin) {
+ lineArgs[0] = Long.toString(steps[i]);
+ dumpLine(pw, 0 /* uid */, "i" /* category */, header, (Object[])lineArgs);
+ } else {
+ pw.print(" #"); pw.print(i); pw.print(": ");
+ TimeUtils.formatDuration(steps[i], pw);
+ pw.println();
+ }
+ }
+ return true;
+ }
+
public static final int DUMP_UNPLUGGED_ONLY = 1<<0;
public static final int DUMP_CHARGED_ONLY = 1<<1;
public static final int DUMP_HISTORY_ONLY = 1<<2;
@@ -3239,7 +3300,27 @@ public abstract class BatteryStats implements Parcelable {
}
}
if (didPid) {
- pw.println("");
+ pw.println();
+ }
+ if (dumpDurationSteps(pw, "Discharge step durations:", getDischargeStepDurationsArray(),
+ getNumDischargeStepDurations(), false)) {
+ long timeRemaining = computeBatteryTimeRemaining(SystemClock.elapsedRealtime());
+ if (timeRemaining >= 0) {
+ pw.print(" Estimated discharge time remaining: ");
+ TimeUtils.formatDuration(timeRemaining / 1000, pw);
+ pw.println();
+ }
+ pw.println();
+ }
+ if (dumpDurationSteps(pw, "Charge step durations:", getChargeStepDurationsArray(),
+ getNumChargeStepDurations(), false)) {
+ long timeRemaining = computeChargeTimeRemaining(SystemClock.elapsedRealtime());
+ if (timeRemaining >= 0) {
+ pw.print(" Estimated charge time remaining: ");
+ TimeUtils.formatDuration(timeRemaining / 1000, pw);
+ pw.println();
+ }
+ pw.println();
}
}
@@ -3248,7 +3329,7 @@ public abstract class BatteryStats implements Parcelable {
pw.println(" System starts: " + getStartCount()
+ ", currently on battery: " + getIsOnBattery());
dumpLocked(context, pw, "", STATS_SINCE_CHARGED, reqUid);
- pw.println("");
+ pw.println();
}
if (!filtering || (flags&DUMP_UNPLUGGED_ONLY) != 0) {
pw.println("Statistics since last unplugged:");
@@ -3352,6 +3433,25 @@ public abstract class BatteryStats implements Parcelable {
}
}
}
+ if (!filtering) {
+ dumpDurationSteps(pw, DISCHARGE_STEP_DATA, getDischargeStepDurationsArray(),
+ getNumDischargeStepDurations(), true);
+ String[] lineArgs = new String[1];
+ long timeRemaining = computeBatteryTimeRemaining(SystemClock.elapsedRealtime());
+ if (timeRemaining >= 0) {
+ lineArgs[0] = Long.toString(timeRemaining);
+ dumpLine(pw, 0 /* uid */, "i" /* category */, DISCHARGE_TIME_REMAIN_DATA,
+ (Object[])lineArgs);
+ }
+ dumpDurationSteps(pw, CHARGE_STEP_DATA, getChargeStepDurationsArray(),
+ getNumChargeStepDurations(), true);
+ timeRemaining = computeChargeTimeRemaining(SystemClock.elapsedRealtime());
+ if (timeRemaining >= 0) {
+ lineArgs[0] = Long.toString(timeRemaining);
+ dumpLine(pw, 0 /* uid */, "i" /* category */, CHARGE_TIME_REMAIN_DATA,
+ (Object[])lineArgs);
+ }
+ }
if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
dumpCheckinLocked(context, pw, STATS_SINCE_CHARGED, -1);
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 0336dd6..1ca6b90 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -81,12 +81,38 @@ public class Build {
public static final String SERIAL = getString("ro.serialno");
/**
- * A list of ABIs (in priority) order supported by this device.
+ * An ordered list of ABIs supported by this device. The most preferred ABI is the first
+ * element in the list.
+ *
+ * See {@link #SUPPORTED_32_BIT_ABIS} and {@link #SUPPORTED_64_BIT_ABIS}.
*
* @hide
*/
public static final String[] SUPPORTED_ABIS = getString("ro.product.cpu.abilist").split(",");
+ /**
+ * An ordered list of <b>32 bit</b> ABIs supported by this device. The most preferred ABI
+ * is the first element in the list.
+ *
+ * See {@link #SUPPORTED_ABIS} and {@link #SUPPORTED_64_BIT_ABIS}.
+ *
+ * @hide
+ */
+ public static final String[] SUPPORTED_32_BIT_ABIS = getString("ro.product.cpu.abilist32")
+ .split(",");
+
+ /**
+ * An ordered list of <b>64 bit</b> ABIs supported by this device. The most preferred ABI
+ * is the first element in the list.
+ *
+ * See {@link #SUPPORTED_ABIS} and {@link #SUPPORTED_32_BIT_ABIS}.
+ *
+ * @hide
+ */
+ public static final String[] SUPPORTED_64_BIT_ABIS = getString("ro.product.cpu.abilist64")
+ .split(",");
+
+
/** Various version strings. */
public static class VERSION {
/**
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index c3f7370..899a958 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -29,6 +29,7 @@ import android.graphics.Bitmap;
interface IUserManager {
UserInfo createUser(in String name, int flags);
UserInfo createProfileForUser(in String name, int flags, int userHandle);
+ void setUserEnabled(int userHandle);
boolean removeUser(int userHandle);
void setUserName(int userHandle, String name);
void setUserIcon(int userHandle, in Bitmap icon);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index b4ed68c..1b3aa0a 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -707,16 +707,6 @@ public class Process {
return primaryZygoteState;
}
- // TODO: Get rid of this. This is a temporary workaround until all the
- // compilation related pieces for the dual zygote stack are ready.
- // b/3647418.
- if (System.getenv("ANDROID_SOCKET_" + SECONDARY_ZYGOTE_SOCKET) == null) {
- Log.e(LOG_TAG, "Forcing app to primary zygote, secondary unavailable (ABI= " + abi + ")");
- // Should be :
- // throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
- return primaryZygoteState;
- }
-
// The primary zygote didn't match. Try the secondary.
if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET,
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 1fe9337..84639eb 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -91,7 +91,6 @@ public class UserManager {
* @see #setUserRestrictions(Bundle)
* @see #getUserRestrictions()
*/
-
public static final String DISALLOW_SHARE_LOCATION = "no_share_location";
/**
@@ -145,6 +144,96 @@ public class UserManager {
*/
public static final String DISALLOW_REMOVE_USER = "no_remove_user";
+ /**
+ * Key for user restrictions. Specifies if a user is disallowed from enabling or
+ * accessing debugging features. The default value is <code>false</code>.
+ * <p/>
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";
+
+ /**
+ * Key for user restrictions. Specifies if a user is disallowed from configuring VPN.
+ * The default value is <code>false</code>.
+ * <p/>
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CONFIG_VPN = "no_config_vpn";
+
+ /**
+ * Key for user restrictions. Specifies if a user is disallowed from configuring Tethering
+ * & portable hotspots. The default value is <code>false</code>.
+ * <p/>
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
+
+ /**
+ * Key for user restrictions. Specifies if a user is disallowed from factory resetting
+ * from Settings.
+ * The default value is <code>false</code>.
+ * <p>
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_FACTORY_RESET = "no_factory_reset";
+
+ /**
+ * Key for user restrictions. Specifies if a user is disallowed from adding new users and
+ * profiles. The default value is <code>false</code>.
+ * <p>
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_ADD_USER = "no_add_user";
+
+ /**
+ * Key for user restrictions. Specifies if a user is disallowed from disabling application
+ * verification. The default value is <code>false</code>.
+ * <p>
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String ENSURE_VERIFY_APPS = "ensure_verify_apps";
+
+ /**
+ * Key for user restrictions. Specifies if a user is disallowed from configuring cell
+ * broadcasts. The default value is <code>false</code>.
+ * <p>
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
+
+ /**
+ * Key for user restrictions. Specifies if a user is disallowed from configuring mobile
+ * networks. The default value is <code>false</code>.
+ * <p>
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
+
+ /**
+ * Key for user restrictions. Specifies if a user is disallowed from configuring
+ * applications in Settings. The default value is <code>false</code>.
+ * <p>
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CONFIG_APPS = "no_config_apps";
+
/** @hide */
public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3;
/** @hide */
@@ -437,6 +526,22 @@ public class UserManager {
}
/**
+ * Sets the user as enabled, if such an user exists.
+ * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * Note that the default is true, it's only that managed profiles might not be enabled.
+ *
+ * @param userHandle the id of the profile to enable
+ * @hide
+ */
+ public void setUserEnabled(int userHandle) {
+ try {
+ mService.setUserEnabled(userHandle);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not enable the profile", e);
+ }
+ }
+
+ /**
* Return the number of users currently created on the device.
*/
public int getUserCount() {
@@ -488,8 +593,7 @@ public class UserManager {
ArrayList<UserHandle> profiles = new ArrayList<UserHandle>();
List<UserInfo> users = new ArrayList<UserInfo>();
try {
- // TODO: Switch enabledOnly to true once client apps are updated
- users = mService.getProfiles(UserHandle.myUserId(), false /* enabledOnly */);
+ users = mService.getProfiles(UserHandle.myUserId(), true /* enabledOnly */);
} catch (RemoteException re) {
Log.w(TAG, "Could not get user list", re);
return null;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ab06230..d5a3bcb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3480,6 +3480,12 @@ public final class Settings {
"lock_screen_appwidget_ids";
/**
+ * List of enrolled fingerprint identifiers (comma-delimited).
+ * @hide
+ */
+ public static final String USER_FINGERPRINT_IDS = "user_fingerprint_ids";
+
+ /**
* Id of the appwidget shown on the lock screen when appwidgets are disabled.
* @hide
*/
@@ -4395,6 +4401,13 @@ public final class Settings {
public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
/**
+ * (Experimental). If nonzero, WebView uses data reduction proxy to save network
+ * bandwidth. Otherwise, WebView does not use data reduction proxy.
+ * @hide
+ */
+ public static final String WEBVIEW_DATA_REDUCTION_PROXY = "webview_data_reduction_proxy";
+
+ /**
* The {@link ComponentName} string of the service to be used as the voice recognition
* service.
*
diff --git a/core/java/android/provider/TvContract.java b/core/java/android/provider/TvContract.java
index 233e0ca..62252be 100644
--- a/core/java/android/provider/TvContract.java
+++ b/core/java/android/provider/TvContract.java
@@ -16,9 +16,13 @@
package android.provider;
+import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.ContentUris;
import android.net.Uri;
+import java.util.List;
+
/**
* <p>
* The contract between the TV provider and applications. Contains definitions for the supported
@@ -42,6 +46,35 @@ public final class TvContract {
/** The authority for the TV provider. */
public static final String AUTHORITY = "com.android.tv";
+ private static final String PATH_CHANNEL = "channel";
+ private static final String PATH_PROGRAM = "program";
+ private static final String PATH_INPUT = "input";
+
+ /**
+ * An optional query, update or delete URI parameter that allows the caller to specify start
+ * time (in milliseconds since the epoch) to filter programs.
+ *
+ * @hide
+ */
+ public static final String PARAM_START_TIME = "start_time";
+
+ /**
+ * An optional query, update or delete URI parameter that allows the caller to specify end time
+ * (in milliseconds since the epoch) to filter programs.
+ *
+ * @hide
+ */
+ public static final String PARAM_END_TIME = "end_time";
+
+ /**
+ * A query, update or delete URI parameter that allows the caller to operate on all or
+ * browsable-only channels. If set to "true", the rows that contain non-browsable channels are
+ * not affected.
+ *
+ * @hide
+ */
+ public static final String PARAM_BROWSABLE_ONLY = "browable_only";
+
/**
* Builds a URI that points to a specific channel.
*
@@ -52,6 +85,32 @@ public final class TvContract {
}
/**
+ * Builds a URI that points to all browsable channels from a given TV input.
+ *
+ * @param name {@link ComponentName} of the {@link android.tv.TvInputService} that implements
+ * the given TV input.
+ */
+ public static final Uri buildChannelsUriForInput(ComponentName name) {
+ return buildChannelsUriForInput(name, true);
+ }
+
+ /**
+ * Builds a URI that points to all or browsable-only channels from a given TV input.
+ *
+ * @param name {@link ComponentName} of the {@link android.tv.TvInputService} that implements
+ * the given TV input.
+ * @param browsableOnly If set to {@code true} the URI points to only browsable channels. If set
+ * to {@code false} the URI points to all channels regardless of whether they are
+ * browsable or not.
+ */
+ public static final Uri buildChannelsUriForInput(ComponentName name, boolean browsableOnly) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
+ .appendPath(PATH_INPUT).appendPath(name.getPackageName())
+ .appendPath(name.getClassName()).appendPath(PATH_CHANNEL)
+ .appendQueryParameter(PARAM_BROWSABLE_ONLY, String.valueOf(browsableOnly)).build();
+ }
+
+ /**
* Builds a URI that points to a specific program.
*
* @param programId The ID of the program to point to.
@@ -61,6 +120,37 @@ public final class TvContract {
}
/**
+ * Builds a URI that points to all programs on a given channel.
+ *
+ * @param channelUri The URI of the channel to return programs for.
+ */
+ public static final Uri buildProgramsUriForChannel(Uri channelUri) {
+ if (!PATH_CHANNEL.equals(channelUri.getPathSegments().get(0))) {
+ throw new IllegalArgumentException("Not a channel: " + channelUri);
+ }
+ String channelId = String.valueOf(ContentUris.parseId(channelUri));
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
+ .appendPath(PATH_CHANNEL).appendPath(channelId).appendPath(PATH_PROGRAM).build();
+ }
+
+ /**
+ * Builds a URI that points to programs on a specific channel whose schedules overlap with the
+ * given time frame.
+ *
+ * @param channelUri The URI of the channel to return programs for.
+ * @param startTime The start time used to filter programs. The returned programs should have
+ * {@link Programs#END_TIME_UTC_MILLIS} that is greater than this time.
+ * @param endTime The end time used to filter programs. The returned programs should have
+ * {@link Programs#START_TIME_UTC_MILLIS} that is less than this time.
+ */
+ public static final Uri buildProgramsUriForChannel(Uri channelUri, long startTime,
+ long endTime) {
+ Uri uri = buildProgramsUriForChannel(channelUri);
+ return uri.buildUpon().appendQueryParameter(PARAM_START_TIME, String.valueOf(startTime))
+ .appendQueryParameter(PARAM_END_TIME, String.valueOf(endTime)).build();
+ }
+
+ /**
* Builds a URI that points to a specific program the user watched.
*
* @param watchedProgramId The ID of the watched program to point to.
@@ -70,6 +160,61 @@ public final class TvContract {
return ContentUris.withAppendedId(WatchedPrograms.CONTENT_URI, watchedProgramId);
}
+ /**
+ * Extracts the {@link Channels#PACKAGE_NAME} from a given URI.
+ *
+ * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or
+ * {@link #buildChannelsUriForInput(ComponentName, boolean)}.
+ * @hide
+ */
+ public static final String getPackageName(Uri channelsUri) {
+ final List<String> paths = channelsUri.getPathSegments();
+ if (paths.size() < 4) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(3))) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ return paths.get(1);
+ }
+
+ /**
+ * Extracts the {@link Channels#SERVICE_NAME} from a given URI.
+ *
+ * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or
+ * {@link #buildChannelsUriForInput(ComponentName, boolean)}.
+ * @hide
+ */
+ public static final String getServiceName(Uri channelsUri) {
+ final List<String> paths = channelsUri.getPathSegments();
+ if (paths.size() < 4) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(3))) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ return paths.get(2);
+ }
+
+ /**
+ * Extracts the {@link Channels#_ID} from a given URI.
+ *
+ * @param programsUri A URI constructed by {@link #buildProgramsUriForChannel(Uri)} or
+ * {@link #buildProgramsUriForChannel(Uri, long, long)}.
+ * @hide
+ */
+ public static final String getChannelId(Uri programsUri) {
+ final List<String> paths = programsUri.getPathSegments();
+ if (paths.size() < 3) {
+ throw new IllegalArgumentException("Not programs: " + programsUri);
+ }
+ if (!PATH_CHANNEL.equals(paths.get(0)) || !PATH_PROGRAM.equals(paths.get(2))) {
+ throw new IllegalArgumentException("Not programs: " + programsUri);
+ }
+ return paths.get(1);
+ }
+
+
private TvContract() {}
/**
@@ -93,7 +238,8 @@ public final class TvContract {
public static final class Channels implements BaseTvColumns {
/** The content:// style URI for this table. */
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/channel");
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+ + PATH_CHANNEL);
/** The MIME type of a directory of TV channels. */
public static final String CONTENT_TYPE =
@@ -276,7 +422,8 @@ public final class TvContract {
public static final class Programs implements BaseTvColumns {
/** The content:// style URI for this table. */
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/program");
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+ + PATH_PROGRAM);
/** The MIME type of a directory of TV programs. */
public static final String CONTENT_TYPE =
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 2303d65..b02a79d 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -300,10 +300,6 @@ public class DreamService extends Service implements Window.Callback {
public void onDetachedFromWindow() {
}
- @Override
- public void onWindowDismissed() {
- }
-
/** {@inheritDoc} */
@Override
public void onPanelClosed(int featureId, Menu menu) {
diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java
new file mode 100644
index 0000000..0d14c59
--- /dev/null
+++ b/core/java/android/service/fingerprint/FingerprintManager.java
@@ -0,0 +1,200 @@
+/**
+ * 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.fingerprint;
+
+import android.app.ActivityManagerNative;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+/**
+ * A class that coordinates access to the fingerprint hardware.
+ */
+
+public class FingerprintManager {
+ private static final String TAG = "FingerprintManager";
+ protected static final boolean DEBUG = true;
+ private static final String FINGERPRINT_SERVICE_PACKAGE = "com.android.service.fingerprint";
+ private static final String FINGERPRINT_SERVICE_CLASS =
+ "com.android.service.fingerprint.FingerprintService";
+ private static final int MSG_ENROLL_RESULT = 100;
+ private static final int MSG_SCANNED = 101;
+ private static final int MSG_ERROR = 102;
+ private static final int MSG_REMOVED = 103;
+
+ public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10;
+ public static final int FINGERPRINT_ERROR = -1; // One of the error messages below.
+
+ // Progress messages.
+ public static final int FINGERPRINT_SCANNED = 1;
+ public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2;
+ public static final int FINGERPRINT_TEMPLATE_REMOVED = 4;
+
+ // Error messages. Must agree with fingerprint HAL definitions.
+ public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
+ public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2;
+ public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
+ public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+
+ private IFingerprintService mService;
+ private FingerprintManagerReceiver mClientReceiver;
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(android.os.Message msg) {
+ if (mClientReceiver != null) {
+ switch(msg.what) {
+ case MSG_ENROLL_RESULT:
+ mClientReceiver.onEnrollResult(msg.arg1, msg.arg2);
+ break;
+ case MSG_SCANNED:
+ mClientReceiver.onScanned(msg.arg1, msg.arg2);
+ break;
+ case MSG_ERROR:
+ mClientReceiver.onError(msg.arg1);
+ break;
+ case MSG_REMOVED:
+ mClientReceiver.onRemoved(msg.arg1);
+ }
+ }
+ }
+ };
+
+ public FingerprintManager(Context context) {
+ // Connect to service...
+ Intent intent = new Intent();
+ intent.setClassName(FINGERPRINT_SERVICE_PACKAGE, FINGERPRINT_SERVICE_CLASS);
+ if (!context.bindServiceAsUser(intent, mFingerprintConnection,
+ Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF)) {
+ if (DEBUG) Log.v(TAG, "Can't bind to " + FINGERPRINT_SERVICE_CLASS);
+ }
+ }
+
+ private final ServiceConnection mFingerprintConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG) Log.v(TAG, "Connected to FingerprintService");
+ mService = IFingerprintService.Stub.asInterface(service);
+ try {
+ mService.startListening(mServiceReceiver, getCurrentUserId());
+ } catch (RemoteException e) {
+ if (DEBUG) Log.v(TAG, "Failed to set callback", e);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG) Log.v(TAG, "Disconnected from FingerprintService");
+ mService = null;
+ }
+ };
+
+ private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
+
+ public void onEnrollResult(int fingerprintId, int remaining) {
+ mHandler.obtainMessage(MSG_ENROLL_RESULT, fingerprintId, remaining).sendToTarget();
+ }
+
+ public void onScanned(int fingerprintId, int confidence) {
+ mHandler.obtainMessage(MSG_SCANNED, fingerprintId, confidence)
+ .sendToTarget();;
+ }
+
+ public void onError(int error) {
+ mHandler.obtainMessage(MSG_ERROR, error, 0).sendToTarget();
+ }
+
+ public void onRemoved(int fingerprintId) {
+ mHandler.obtainMessage(MSG_REMOVED, fingerprintId, 0).sendToTarget();
+ }
+ };
+
+ /**
+ * Start the enrollment process. Timeout dictates how long to wait for the user to
+ * enroll a fingerprint.
+ *
+ * @param timeout
+ */
+ public void enroll(long timeout) {
+ if (mServiceReceiver == null) {
+ throw new IllegalStateException("enroll: Call registerCallback() first");
+ }
+ if (mService != null) try {
+ mService.enroll(timeout, getCurrentUserId());
+ } catch (RemoteException e) {
+ Log.v(TAG, "Remote exception while enrolling: ", e);
+ }
+ }
+
+ /**
+ * Remove the given fingerprintId from the system. FingerprintId of 0 has special meaning
+ * which is to delete all fingerprint data for the current user. Use with caution.
+ * @param fingerprintId
+ */
+ public void remove(int fingerprintId) {
+ if (mService != null) try {
+ mService.remove(fingerprintId, getCurrentUserId());
+ } catch (RemoteException e) {
+ Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e);
+ }
+ }
+
+ /**
+ * Starts listening for fingerprint events. When a finger is scanned or recognized, the
+ * client will be notified via the callback.
+ */
+ public void startListening(FingerprintManagerReceiver receiver) {
+ mClientReceiver = receiver;
+ if (mService != null) {
+ try {
+ mService.startListening(mServiceReceiver, getCurrentUserId());
+ } catch (RemoteException e) {
+ Log.v(TAG, "Remote exception in startListening(): ", e);
+ }
+ }
+ }
+
+ private int getCurrentUserId() {
+ try {
+ return ActivityManagerNative.getDefault().getCurrentUser().id;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to get current user id\n");
+ return UserHandle.USER_NULL;
+ }
+ }
+
+ /**
+ * Stops the client from listening to fingerprint events.
+ */
+ public void stopListening() {
+ mClientReceiver = null;
+ if (mService != null) {
+ try {
+ mService.stopListening(getCurrentUserId());
+ } catch (RemoteException e) {
+ Log.v(TAG, "Remote exception in stopListening(): ", e);
+ }
+ } else {
+ Log.w(TAG, "stopListening(): Service not connected!");
+ }
+ }
+} \ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
new file mode 100644
index 0000000..34f1655
--- /dev/null
+++ b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
@@ -0,0 +1,59 @@
+package android.service.fingerprint;
+/**
+ * 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.
+ */
+
+public class FingerprintManagerReceiver {
+ /**
+ * Fingerprint enrollment progress update. Enrollment is considered complete if
+ * remaining hits 0 without {@link #onError(int)} being called.
+ *
+ * @param fingerprintId the fingerprint we're currently enrolling
+ * @param remaining the number of samples required to complete enrollment. It's up to
+ * the hardware to define what each step in enrollment means. Some hardware
+ * requires multiple samples of the same part of the finger. Others require sampling of
+ * different parts of the finger. The enrollment flow can use remaining to
+ * mean "step x" of the process or "just need another sample."
+ */
+ public void onEnrollResult(int fingerprintId, int remaining) { }
+
+ /**
+ * Fingerprint scan detected. Most clients will use this function to detect a fingerprint
+ *
+ * @param fingerprintId is the finger the hardware has detected.
+ * @param confidence from 0 (no confidence) to 65535 (high confidence). Fingerprint 0 has
+ * special meaning - the finger wasn't recognized.
+ */
+ public void onScanned(int fingerprintId, int confidence) { }
+
+ /**
+ * An error was detected during scan or enrollment. One of
+ * {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE},
+ * {@link FingerprintManager#FINGERPRINT_ERROR_BAD_CAPTURE} or
+ * {@link FingerprintManager#FINGERPRINT_ERROR_TIMEOUT}
+ * {@link FingerprintManager#FINGERPRINT_ERROR_NO_SPACE}
+ *
+ * @param error one of the above error codes
+ */
+ public void onError(int error) { }
+
+ /**
+ * The given fingerprint template was successfully removed by the driver.
+ * See {@link FingerprintManager#remove(int)}
+ *
+ * @param fingerprintId id of template to remove.
+ */
+ public void onRemoved(int fingerprintId) { }
+} \ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintService.java b/core/java/android/service/fingerprint/FingerprintService.java
new file mode 100644
index 0000000..c7fa7cd
--- /dev/null
+++ b/core/java/android/service/fingerprint/FingerprintService.java
@@ -0,0 +1,219 @@
+/**
+ * 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.fingerprint;
+
+import android.app.Service;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+
+/**
+ * A service to manage multiple clients that want to access the fingerprint HAL API.
+ * The service is responsible for maintaining a list of clients and dispatching all
+ * fingerprint -related events.
+ *
+ * @hide
+ */
+public class FingerprintService extends Service {
+ private final String TAG = FingerprintService.class.getSimpleName() +
+ "[" + getClass().getSimpleName() + "]";
+ private static final boolean DEBUG = true;
+ HashMap<IFingerprintServiceReceiver, ClientData> mClients =
+ new HashMap<IFingerprintServiceReceiver, ClientData>();
+
+ private static final int MSG_NOTIFY = 10;
+
+ Handler mHandler = new Handler() {
+ public void handleMessage(android.os.Message msg) {
+ switch (msg.what) {
+ case MSG_NOTIFY:
+ handleNotify(msg.arg1, msg.arg2, (Integer) msg.obj);
+ break;
+
+ default:
+ Slog.w(TAG, "Unknown message:" + msg.what);
+ }
+ }
+ };
+
+ private static final int STATE_IDLE = 0;
+ private static final int STATE_LISTENING = 1;
+ private static final int STATE_ENROLLING = 2;
+ private static final int STATE_DELETING = 3;
+ private static final long MS_PER_SEC = 1000;
+
+ private static final class ClientData {
+ public IFingerprintServiceReceiver receiver;
+ int state;
+ int userId;
+ }
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent);
+ return new FingerprintServiceWrapper();
+ }
+
+ // JNI methods to communicate from FingerprintManagerService to HAL
+ native int nativeEnroll(int timeout);
+ native int nativeRemove(int fingerprintId);
+
+ // JNI methods for communicating from HAL to clients
+ void notify(int msg, int arg1, int arg2) {
+ mHandler.obtainMessage(MSG_NOTIFY, msg, arg1, arg2).sendToTarget();
+ }
+
+ void handleNotify(int msg, int arg1, int arg2) {
+ for (int i = 0; i < mClients.size(); i++) {
+ ClientData clientData = mClients.get(i);
+ switch (msg) {
+ case FingerprintManager.FINGERPRINT_ERROR: {
+ if (clientData.state != STATE_IDLE) {
+ // FINGERPRINT_ERROR_HW_UNAVAILABLE
+ // FINGERPRINT_ERROR_BAD_CAPTURE
+ // FINGERPRINT_ERROR_TIMEOUT
+ // FINGERPRINT_ERROR_NO_SPACE
+ final int error = arg1;
+ clientData.state = STATE_IDLE;
+ if (clientData.receiver != null) {
+ try {
+ clientData.receiver.onError(error);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't send message to client. Did it die?", e);
+ }
+ }
+ }
+ }
+ break;
+ case FingerprintManager.FINGERPRINT_SCANNED: {
+ final int fingerId = arg1;
+ final int confidence = arg2;
+ if (clientData.state == STATE_LISTENING && clientData.receiver != null) {
+ try {
+ clientData.receiver.onScanned(fingerId, confidence);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't send message to client. Did it die?", e);
+ }
+ }
+ break;
+ }
+ case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: {
+ if (clientData.state == STATE_ENROLLING) {
+ final int fingerId = arg1;
+ final int remaining = arg2;
+ if (remaining == 0) {
+ FingerprintUtils.addFingerprintIdForUser(fingerId,
+ getContentResolver(), clientData.userId);
+ clientData.state = STATE_IDLE; // Nothing left to do
+ }
+ if (clientData.receiver != null) {
+ try {
+ clientData.receiver.onEnrollResult(fingerId, remaining);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't send message to client. Did it die?", e);
+ }
+ }
+ }
+ break;
+ }
+ case FingerprintManager.FINGERPRINT_TEMPLATE_REMOVED: {
+ int fingerId = arg1;
+ if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL");
+ if (clientData.state == STATE_DELETING) {
+ FingerprintUtils.removeFingerprintIdForUser(fingerId, getContentResolver(),
+ clientData.userId);
+ if (clientData.receiver != null) {
+ try {
+ clientData.receiver.onRemoved(fingerId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "can't send message to client. Did it die?", e);
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ int enroll(IFingerprintServiceReceiver receiver, long timeout, int userId) {
+ ClientData clientData = mClients.get(receiver);
+ if (clientData != null) {
+ if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+ clientData.state = STATE_ENROLLING;
+ return nativeEnroll((int) (timeout / MS_PER_SEC));
+ }
+ return -1;
+ }
+
+ int remove(IFingerprintServiceReceiver receiver, int fingerId, int userId) {
+ ClientData clientData = mClients.get(receiver);
+ if (clientData != null) {
+ if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+ clientData.state = STATE_DELETING;
+ // The fingerprint id will be removed when we get confirmation from the HAL
+ return nativeRemove(fingerId);
+ }
+ return -1;
+ }
+
+ void startListening(IFingerprintServiceReceiver receiver, int userId) {
+ ClientData clientData = new ClientData();
+ clientData.state = STATE_LISTENING;
+ clientData.receiver = receiver;
+ clientData.userId = userId;
+ mClients.put(receiver, clientData);
+ }
+
+ void stopListening(IFingerprintServiceReceiver receiver, int userId) {
+ ClientData clientData = mClients.get(receiver);
+ if (clientData != null) {
+ clientData.state = STATE_IDLE;
+ clientData.userId = -1;
+ clientData.receiver = null;
+ }
+ mClients.remove(receiver);
+ }
+
+ private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+ IFingerprintServiceReceiver mReceiver;
+ public int enroll(long timeout, int userId) {
+ return mReceiver != null ? FingerprintService.this.enroll(mReceiver, timeout, userId)
+ : FingerprintManager.FINGERPRINT_ERROR_NO_RECEIVER;
+ }
+
+ public int remove(int fingerprintId, int userId) {
+ return FingerprintService.this.remove(mReceiver, fingerprintId, userId);
+ }
+
+ public void startListening(IFingerprintServiceReceiver receiver, int userId) {
+ mReceiver = receiver;
+ FingerprintService.this.startListening(receiver, userId);
+ }
+
+ public void stopListening(int userId) {
+ FingerprintService.this.stopListening(mReceiver, userId);
+ }
+ }
+}
diff --git a/core/java/android/service/fingerprint/FingerprintUtils.java b/core/java/android/service/fingerprint/FingerprintUtils.java
new file mode 100644
index 0000000..81a2aac
--- /dev/null
+++ b/core/java/android/service/fingerprint/FingerprintUtils.java
@@ -0,0 +1,85 @@
+/**
+ * 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.fingerprint;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.Arrays;
+
+class FingerprintUtils {
+ private static final boolean DEBUG = true;
+ private static final String TAG = "FingerprintUtils";
+
+ public static int[] getFingerprintIdsForUser(ContentResolver res, int userId) {
+ String fingerIdsRaw = Settings.Secure.getStringForUser(res,
+ Settings.Secure.USER_FINGERPRINT_IDS, userId);
+
+ String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", ");
+ int result[] = new int[fingerStringIds.length];
+ for (int i = 0; i < result.length; i++) {
+ try {
+ result[i] = Integer.decode(fingerStringIds[i]);
+ } catch (NumberFormatException e) {
+ if (DEBUG) Log.d(TAG, "Error when parsing finger id " + fingerStringIds[i]);
+ }
+ }
+ return result;
+ }
+
+ public static void addFingerprintIdForUser(int fingerId, ContentResolver res, int userId) {
+ int[] fingerIds = getFingerprintIdsForUser(res, userId);
+
+ // FingerId 0 has special meaning.
+ if (fingerId == 0) return;
+
+ // Don't allow dups
+ for (int i = 0; i < fingerIds.length; i++) {
+ if (fingerIds[i] == fingerId) return;
+ }
+ int[] newList = Arrays.copyOf(fingerIds, fingerIds.length + 1);
+ newList[fingerIds.length] = fingerId;
+ Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS,
+ Arrays.toString(newList), userId);
+ }
+
+ public static boolean removeFingerprintIdForUser(int fingerId, ContentResolver res, int userId)
+ {
+ // FingerId 0 has special meaning. The HAL layer is supposed to remove each finger one
+ // at a time and invoke notify() for each fingerId. If we get called with 0 here, it means
+ // something bad has happened.
+ if (fingerId == 0) throw new IllegalStateException("Bad fingerId");
+
+ int[] fingerIds = getFingerprintIdsForUser(res, userId);
+ int[] resultIds = Arrays.copyOf(fingerIds, fingerIds.length);
+ int resultCount = 0;
+ for (int i = 0; i < fingerIds.length; i++) {
+ if (fingerId != fingerIds[i]) {
+ resultIds[resultCount++] = fingerIds[i];
+ }
+ }
+ if (resultCount > 0) {
+ Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS,
+ Arrays.toString(Arrays.copyOf(resultIds, resultCount)), userId);
+ return true;
+ }
+ return false;
+ }
+
+};
+
diff --git a/core/java/android/service/fingerprint/IFingerprintService.aidl b/core/java/android/service/fingerprint/IFingerprintService.aidl
new file mode 100644
index 0000000..e92c20c
--- /dev/null
+++ b/core/java/android/service/fingerprint/IFingerprintService.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.fingerprint;
+
+import android.os.Bundle;
+import android.service.fingerprint.IFingerprintServiceReceiver;
+
+/**
+ * Communication channel from client to the fingerprint service.
+ * @hide
+ */
+interface IFingerprintService {
+ // Returns 0 if successfully started, -1 otherwise
+ int enroll(long timeout, int userId);
+
+ // Returns 0 if fingerprintId's template can be removed, -1 otherwise
+ int remove(int fingerprintId, int userId);
+
+ // Start listening for fingerprint events. This has the side effect of starting
+ // the hardware if not already started.
+ oneway void startListening(IFingerprintServiceReceiver receiver, int userId);
+
+ // Stops listening for fingerprints
+ oneway void stopListening(int userId);
+}
diff --git a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
new file mode 100644
index 0000000..4826b59
--- /dev/null
+++ b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.fingerprint;
+
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * Communication channel from the FingerprintService back to FingerprintManager.
+ * @hide
+ */
+oneway interface IFingerprintServiceReceiver {
+ void onEnrollResult(int fingerprintId, int remaining);
+ void onScanned(int fingerprintId, int confidence);
+ void onError(int error);
+ void onRemoved(int fingerprintId);
+}
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 71e3166..aa724f0 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -41,16 +41,25 @@ public class Condition implements Parcelable {
public static final int FLAG_RELEVANT_ALWAYS = 1 << 1;
public final Uri id;
- public String caption;
- public int state;
- public int flags;
-
- public Condition(Uri id, String caption, int state, int flags) {
+ public final String summary;
+ public final String line1;
+ public final String line2;
+ public final int icon;
+ public final int state;
+ public final int flags;
+
+ public Condition(Uri id, String summary, String line1, String line2, int icon,
+ int state, int flags) {
if (id == null) throw new IllegalArgumentException("id is required");
- if (caption == null) throw new IllegalArgumentException("caption is required");
+ if (summary == null) throw new IllegalArgumentException("summary is required");
+ if (line1 == null) throw new IllegalArgumentException("line1 is required");
+ if (line2 == null) throw new IllegalArgumentException("line2 is required");
if (!isValidState(state)) throw new IllegalArgumentException("state is invalid: " + state);
this.id = id;
- this.caption = caption;
+ this.summary = summary;
+ this.line1 = line1;
+ this.line2 = line2;
+ this.icon = icon;
this.state = state;
this.flags = flags;
}
@@ -58,6 +67,9 @@ public class Condition implements Parcelable {
private Condition(Parcel source) {
this((Uri)source.readParcelable(Condition.class.getClassLoader()),
source.readString(),
+ source.readString(),
+ source.readString(),
+ source.readInt(),
source.readInt(),
source.readInt());
}
@@ -69,16 +81,22 @@ public class Condition implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(id, 0);
- dest.writeString(caption);
+ dest.writeString(summary);
+ dest.writeString(line1);
+ dest.writeString(line2);
+ dest.writeInt(icon);
dest.writeInt(state);
- dest.writeInt(flags);
+ dest.writeInt(this.flags);
}
@Override
public String toString() {
return new StringBuilder(Condition.class.getSimpleName()).append('[')
.append("id=").append(id)
- .append(",caption=").append(caption)
+ .append(",summary=").append(summary)
+ .append(",line1=").append(line1)
+ .append(",line2=").append(line2)
+ .append(",icon=").append(icon)
.append(",state=").append(stateToString(state))
.append(",flags=").append(flags)
.append(']').toString();
@@ -92,20 +110,31 @@ public class Condition implements Parcelable {
throw new IllegalArgumentException("state is invalid: " + state);
}
+ public static String relevanceToString(int flags) {
+ final boolean now = (flags & FLAG_RELEVANT_NOW) != 0;
+ final boolean always = (flags & FLAG_RELEVANT_ALWAYS) != 0;
+ if (!now && !always) return "NONE";
+ if (now && always) return "NOW, ALWAYS";
+ return now ? "NOW" : "ALWAYS";
+ }
+
@Override
public boolean equals(Object o) {
if (!(o instanceof Condition)) return false;
if (o == this) return true;
final Condition other = (Condition) o;
return Objects.equals(other.id, id)
- && Objects.equals(other.caption, caption)
+ && Objects.equals(other.summary, summary)
+ && Objects.equals(other.line1, line1)
+ && Objects.equals(other.line2, line2)
+ && other.icon == icon
&& other.state == state
&& other.flags == flags;
}
@Override
public int hashCode() {
- return Objects.hash(id, caption, state, flags);
+ return Objects.hash(id, summary, line1, line2, icon, state, flags);
}
@Override
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index d6ef8f5..326412f 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -22,7 +22,9 @@ import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Message;
import android.os.ServiceManager;
import android.util.Log;
@@ -46,6 +48,8 @@ public abstract class ConditionProviderService extends Service {
private final String TAG = ConditionProviderService.class.getSimpleName()
+ "[" + getClass().getSimpleName() + "]";
+ private final H mHandler = new H();
+
private Provider mProvider;
private INotificationManager mNoMan;
@@ -100,41 +104,57 @@ public abstract class ConditionProviderService extends Service {
}
private final class Provider extends IConditionProvider.Stub {
- private final ConditionProviderService mService = ConditionProviderService.this;
-
@Override
public void onConnected() {
- try {
- mService.onConnected();
- } catch (Throwable t) {
- Log.w(TAG, "Error running onConnected", t);
- }
+ mHandler.obtainMessage(H.ON_CONNECTED).sendToTarget();
}
@Override
public void onRequestConditions(int relevance) {
- try {
- mService.onRequestConditions(relevance);
- } catch (Throwable t) {
- Log.w(TAG, "Error running onRequestConditions", t);
- }
+ mHandler.obtainMessage(H.ON_REQUEST_CONDITIONS, relevance, 0).sendToTarget();
}
@Override
public void onSubscribe(Uri conditionId) {
- try {
- mService.onSubscribe(conditionId);
- } catch (Throwable t) {
- Log.w(TAG, "Error running onSubscribe", t);
- }
+ mHandler.obtainMessage(H.ON_SUBSCRIBE, conditionId).sendToTarget();
}
@Override
public void onUnsubscribe(Uri conditionId) {
+ mHandler.obtainMessage(H.ON_UNSUBSCRIBE, conditionId).sendToTarget();
+ }
+ }
+
+ private final class H extends Handler {
+ private static final int ON_CONNECTED = 1;
+ private static final int ON_REQUEST_CONDITIONS = 2;
+ private static final int ON_SUBSCRIBE = 3;
+ private static final int ON_UNSUBSCRIBE = 4;
+
+ @Override
+ public void handleMessage(Message msg) {
+ String name = null;
try {
- mService.onUnsubscribe(conditionId);
+ switch(msg.what) {
+ case ON_CONNECTED:
+ name = "onConnected";
+ onConnected();
+ break;
+ case ON_REQUEST_CONDITIONS:
+ name = "onRequestConditions";
+ onRequestConditions(msg.arg1);
+ break;
+ case ON_SUBSCRIBE:
+ name = "onSubscribe";
+ onSubscribe((Uri)msg.obj);
+ break;
+ case ON_UNSUBSCRIBE:
+ name = "onUnsubscribe";
+ onUnsubscribe((Uri)msg.obj);
+ break;
+ }
} catch (Throwable t) {
- Log.w(TAG, "Error running onUnsubscribe", t);
+ Log.w(TAG, "Error running " + name, t);
}
}
}
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 72720d1..e7cdc4e 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -87,7 +87,7 @@ public class StatusBarNotification implements Parcelable {
}
private String key() {
- return pkg + '|' + id + '|' + tag + '|' + uid;
+ return user.getIdentifier() + "|" + pkg + "|" + id + "|" + tag + "|" + uid;
}
public void writeToParcel(Parcel out, int flags) {
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 925ddcf..846e292 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -16,6 +16,8 @@
package android.service.notification;
+import android.content.ComponentName;
+import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -25,6 +27,8 @@ import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -51,6 +55,10 @@ public class ZenModeConfig implements Parcelable {
private static final String SLEEP_ATT_END_HR = "endHour";
private static final String SLEEP_ATT_END_MIN = "endMin";
+ private static final String CONDITION_TAG = "condition";
+ private static final String CONDITION_ATT_COMPONENT = "component";
+ private static final String CONDITION_ATT_ID = "id";
+
public boolean allowCalls;
public boolean allowMessages;
@@ -59,6 +67,8 @@ public class ZenModeConfig implements Parcelable {
public int sleepStartMinute;
public int sleepEndHour;
public int sleepEndMinute;
+ public ComponentName[] conditionComponents;
+ public Uri[] conditionIds;
public ZenModeConfig() { }
@@ -72,6 +82,16 @@ public class ZenModeConfig implements Parcelable {
sleepStartMinute = source.readInt();
sleepEndHour = source.readInt();
sleepEndMinute = source.readInt();
+ int len = source.readInt();
+ if (len > 0) {
+ conditionComponents = new ComponentName[len];
+ source.readTypedArray(conditionComponents, ComponentName.CREATOR);
+ }
+ len = source.readInt();
+ if (len > 0) {
+ conditionIds = new Uri[len];
+ source.readTypedArray(conditionIds, Uri.CREATOR);
+ }
}
@Override
@@ -88,6 +108,18 @@ public class ZenModeConfig implements Parcelable {
dest.writeInt(sleepStartMinute);
dest.writeInt(sleepEndHour);
dest.writeInt(sleepEndMinute);
+ if (conditionComponents != null && conditionComponents.length > 0) {
+ dest.writeInt(conditionComponents.length);
+ dest.writeTypedArray(conditionComponents, 0);
+ } else {
+ dest.writeInt(0);
+ }
+ if (conditionIds != null && conditionIds.length > 0) {
+ dest.writeInt(conditionIds.length);
+ dest.writeTypedArray(conditionIds, 0);
+ } else {
+ dest.writeInt(0);
+ }
}
@Override
@@ -98,6 +130,10 @@ public class ZenModeConfig implements Parcelable {
.append(",sleepMode=").append(sleepMode)
.append(",sleepStart=").append(sleepStartHour).append('.').append(sleepStartMinute)
.append(",sleepEnd=").append(sleepEndHour).append('.').append(sleepEndMinute)
+ .append(",conditionComponents=")
+ .append(conditionComponents == null ? null : TextUtils.join(",", conditionComponents))
+ .append(",conditionIds=")
+ .append(conditionIds == null ? null : TextUtils.join(",", conditionIds))
.append(']').toString();
}
@@ -112,13 +148,16 @@ public class ZenModeConfig implements Parcelable {
&& other.sleepStartHour == sleepStartHour
&& other.sleepStartMinute == sleepStartMinute
&& other.sleepEndHour == sleepEndHour
- && other.sleepEndMinute == sleepEndMinute;
+ && other.sleepEndMinute == sleepEndMinute
+ && Objects.deepEquals(other.conditionComponents, conditionComponents)
+ && Objects.deepEquals(other.conditionIds, conditionIds);
}
@Override
public int hashCode() {
return Objects.hash(allowCalls, allowMessages, sleepMode, sleepStartHour,
- sleepStartMinute, sleepEndHour, sleepEndMinute);
+ sleepStartMinute, sleepEndHour, sleepEndMinute,
+ Arrays.hashCode(conditionComponents), Arrays.hashCode(conditionIds));
}
public boolean isValid() {
@@ -136,9 +175,18 @@ public class ZenModeConfig implements Parcelable {
if (!ZEN_TAG.equals(tag)) return null;
final ZenModeConfig rt = new ZenModeConfig();
final int version = Integer.parseInt(parser.getAttributeValue(null, ZEN_ATT_VERSION));
+ final ArrayList<ComponentName> conditionComponents = new ArrayList<ComponentName>();
+ final ArrayList<Uri> conditionIds = new ArrayList<Uri>();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
tag = parser.getName();
- if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) return rt;
+ if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) {
+ if (!conditionComponents.isEmpty()) {
+ rt.conditionComponents = conditionComponents
+ .toArray(new ComponentName[conditionComponents.size()]);
+ rt.conditionIds = conditionIds.toArray(new Uri[conditionIds.size()]);
+ }
+ return rt;
+ }
if (type == XmlPullParser.START_TAG) {
if (ALLOW_TAG.equals(tag)) {
rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false);
@@ -155,10 +203,18 @@ public class ZenModeConfig implements Parcelable {
rt.sleepStartMinute = isValidMinute(startMinute) ? startMinute : 0;
rt.sleepEndHour = isValidHour(endHour) ? endHour : 0;
rt.sleepEndMinute = isValidMinute(endMinute) ? endMinute : 0;
+ } else if (CONDITION_TAG.equals(tag)) {
+ final ComponentName component =
+ safeComponentName(parser, CONDITION_ATT_COMPONENT);
+ final Uri conditionId = safeUri(parser, CONDITION_ATT_ID);
+ if (component != null && conditionId != null) {
+ conditionComponents.add(component);
+ conditionIds.add(conditionId);
+ }
}
}
}
- return rt;
+ throw new IllegalStateException("Failed to reach END_DOCUMENT");
}
public void writeXml(XmlSerializer out) throws IOException {
@@ -180,6 +236,16 @@ public class ZenModeConfig implements Parcelable {
out.attribute(null, SLEEP_ATT_END_MIN, Integer.toString(sleepEndMinute));
out.endTag(null, SLEEP_TAG);
+ if (conditionComponents != null && conditionIds != null
+ && conditionComponents.length == conditionIds.length) {
+ for (int i = 0; i < conditionComponents.length; i++) {
+ out.startTag(null, CONDITION_TAG);
+ out.attribute(null, CONDITION_ATT_COMPONENT,
+ conditionComponents[i].flattenToString());
+ out.attribute(null, CONDITION_ATT_ID, conditionIds[i].toString());
+ out.endTag(null, CONDITION_TAG);
+ }
+ }
out.endTag(null, ZEN_TAG);
}
@@ -203,6 +269,18 @@ public class ZenModeConfig implements Parcelable {
return Integer.valueOf(val);
}
+ private static ComponentName safeComponentName(XmlPullParser parser, String att) {
+ final String val = parser.getAttributeValue(null, att);
+ if (TextUtils.isEmpty(val)) return null;
+ return ComponentName.unflattenFromString(val);
+ }
+
+ private static Uri safeUri(XmlPullParser parser, String att) {
+ final String val = parser.getAttributeValue(null, att);
+ if (TextUtils.isEmpty(val)) return null;
+ return Uri.parse(val);
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/service/voice/IVoiceInteractionSession.aidl b/core/java/android/service/voice/IVoiceInteractionSession.aidl
index 7dbf66b..9f9c312 100644
--- a/core/java/android/service/voice/IVoiceInteractionSession.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl
@@ -16,13 +16,14 @@
package android.service.voice;
-import android.os.Bundle;
-
-import com.android.internal.app.IVoiceInteractorCallback;
-import com.android.internal.app.IVoiceInteractorRequest;
+import android.content.Intent;
/**
* @hide
*/
-interface IVoiceInteractionSession {
+oneway interface IVoiceInteractionSession {
+ void taskStarted(in Intent intent, int taskId);
+ void taskFinished(in Intent intent, int taskId);
+ void closeSystemDialogs();
+ void destroy();
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index d005890..e15489b 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -27,6 +27,19 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import com.android.internal.app.IVoiceInteractionManagerService;
+/**
+ * Top-level service of the current global voice interactor, which is providing
+ * support for hotwording, the back-end of a {@link android.app.VoiceInteractor}, etc.
+ * The current VoiceInteractionService that has been selected by the user is kept
+ * always running by the system, to allow it to do things like listen for hotwords
+ * in the background to instigate voice interactions.
+ *
+ * <p>Because this service is always running, it should be kept as lightweight as
+ * possible. Heavy-weight operations (including showing UI) should be implemented
+ * in the associated {@link android.service.voice.VoiceInteractionSessionService} when
+ * an actual voice interaction is taking place, and that service should run in a
+ * separate process from this one.
+ */
public class VoiceInteractionService extends Service {
/**
* The {@link Intent} that must be declared as handled by the service.
@@ -51,11 +64,9 @@ public class VoiceInteractionService extends Service {
IVoiceInteractionManagerService mSystemService;
- public void startVoiceActivity(Intent intent, Bundle sessionArgs) {
+ public void startSession(Bundle args) {
try {
- mSystemService.startVoiceActivity(intent,
- intent.resolveType(getContentResolver()),
- mInterface, sessionArgs);
+ mSystemService.startSession(mInterface, args);
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 963b6b4..a83544d 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -16,7 +16,14 @@
package android.service.voice;
+import android.app.Dialog;
+import android.app.Instrumentation;
import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Region;
+import android.inputmethodservice.SoftInputWindow;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -25,16 +32,53 @@ import android.os.Message;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.IVoiceInteractorCallback;
import com.android.internal.app.IVoiceInteractorRequest;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
-public abstract class VoiceInteractionSession {
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+public abstract class VoiceInteractionSession implements KeyEvent.Callback {
static final String TAG = "VoiceInteractionSession";
static final boolean DEBUG = true;
+ final Context mContext;
+ final HandlerCaller mHandlerCaller;
+
+ final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
+
+ IVoiceInteractionManagerService mSystemService;
+ IBinder mToken;
+
+ int mTheme = 0;
+ LayoutInflater mInflater;
+ TypedArray mThemeAttrs;
+ View mRootView;
+ FrameLayout mContentFrame;
+ SoftInputWindow mWindow;
+
+ boolean mInitialized;
+ boolean mWindowAdded;
+ boolean mWindowVisible;
+ boolean mWindowWasVisible;
+ boolean mInShowWindow;
+
+ final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
+
+ final Insets mTmpInsets = new Insets();
+ final int[] mTmpLocation = new int[2];
+
final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
@Override
public IVoiceInteractorRequest startConfirmation(String callingPackage,
@@ -71,6 +115,27 @@ public abstract class VoiceInteractionSession {
};
final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() {
+ @Override
+ public void taskStarted(Intent intent, int taskId) {
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_STARTED,
+ taskId, intent));
+ }
+
+ @Override
+ public void taskFinished(Intent intent, int taskId) {
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_FINISHED,
+ taskId, intent));
+ }
+
+ @Override
+ public void closeSystemDialogs() {
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CLOSE_SYSTEM_DIALOGS));
+ }
+
+ @Override
+ public void destroy() {
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY));
+ }
};
public static class Request {
@@ -129,38 +194,128 @@ public abstract class VoiceInteractionSession {
static final int MSG_SUPPORTS_COMMANDS = 3;
static final int MSG_CANCEL = 4;
- final Context mContext;
- final HandlerCaller mHandlerCaller;
- final HandlerCaller.Callback mHandlerCallerCallback = new HandlerCaller.Callback() {
+ static final int MSG_TASK_STARTED = 100;
+ static final int MSG_TASK_FINISHED = 101;
+ static final int MSG_CLOSE_SYSTEM_DIALOGS = 102;
+ static final int MSG_DESTROY = 103;
+
+ class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback {
@Override
public void executeMessage(Message msg) {
- SomeArgs args = (SomeArgs)msg.obj;
+ SomeArgs args;
switch (msg.what) {
case MSG_START_CONFIRMATION:
+ args = (SomeArgs)msg.obj;
if (DEBUG) Log.d(TAG, "onConfirm: req=" + ((Request) args.arg2).mInterface
+ " prompt=" + args.arg3 + " extras=" + args.arg4);
onConfirm((Caller)args.arg1, (Request)args.arg2, (String)args.arg3,
(Bundle)args.arg4);
break;
case MSG_START_COMMAND:
+ args = (SomeArgs)msg.obj;
if (DEBUG) Log.d(TAG, "onCommand: req=" + ((Request) args.arg2).mInterface
+ " command=" + args.arg3 + " extras=" + args.arg4);
onCommand((Caller) args.arg1, (Request) args.arg2, (String) args.arg3,
(Bundle) args.arg4);
break;
case MSG_SUPPORTS_COMMANDS:
+ args = (SomeArgs)msg.obj;
if (DEBUG) Log.d(TAG, "onGetSupportedCommands: cmds=" + args.arg2);
args.arg1 = onGetSupportedCommands((Caller) args.arg1, (String[]) args.arg2);
break;
case MSG_CANCEL:
+ args = (SomeArgs)msg.obj;
if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request) args.arg1).mInterface);
onCancel((Request)args.arg1);
break;
+ case MSG_TASK_STARTED:
+ if (DEBUG) Log.d(TAG, "onTaskStarted: intent=" + msg.obj
+ + " taskId=" + msg.arg1);
+ onTaskStarted((Intent) msg.obj, msg.arg1);
+ break;
+ case MSG_TASK_FINISHED:
+ if (DEBUG) Log.d(TAG, "onTaskFinished: intent=" + msg.obj
+ + " taskId=" + msg.arg1);
+ onTaskFinished((Intent) msg.obj, msg.arg1);
+ break;
+ case MSG_CLOSE_SYSTEM_DIALOGS:
+ if (DEBUG) Log.d(TAG, "onCloseSystemDialogs");
+ onCloseSystemDialogs();
+ break;
+ case MSG_DESTROY:
+ if (DEBUG) Log.d(TAG, "doDestroy");
+ doDestroy();
+ break;
}
}
- };
- final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
+ @Override
+ public void onBackPressed() {
+ VoiceInteractionSession.this.onBackPressed();
+ }
+ }
+
+ final MyCallbacks mCallbacks = new MyCallbacks();
+
+ /**
+ * Information about where interesting parts of the input method UI appear.
+ */
+ public static final class Insets {
+ /**
+ * This is the top part of the UI that is the main content. It is
+ * used to determine the basic space needed, to resize/pan the
+ * application behind. It is assumed that this inset does not
+ * change very much, since any change will cause a full resize/pan
+ * of the application behind. This value is relative to the top edge
+ * of the input method window.
+ */
+ public int contentTopInsets;
+
+ /**
+ * This is the region of the UI that is touchable. It is used when
+ * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
+ * The region should be specified relative to the origin of the window frame.
+ */
+ public final Region touchableRegion = new Region();
+
+ /**
+ * Option for {@link #touchableInsets}: the entire window frame
+ * can be touched.
+ */
+ public static final int TOUCHABLE_INSETS_FRAME
+ = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
+
+ /**
+ * Option for {@link #touchableInsets}: the area inside of
+ * the content insets can be touched.
+ */
+ public static final int TOUCHABLE_INSETS_CONTENT
+ = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
+
+ /**
+ * Option for {@link #touchableInsets}: the region specified by
+ * {@link #touchableRegion} can be touched.
+ */
+ public static final int TOUCHABLE_INSETS_REGION
+ = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
+
+ /**
+ * Determine which area of the window is touchable by the user. May
+ * be one of: {@link #TOUCHABLE_INSETS_FRAME},
+ * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_REGION}.
+ */
+ public int touchableInsets;
+ }
+
+ final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
+ new ViewTreeObserver.OnComputeInternalInsetsListener() {
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
+ onComputeInsets(mTmpInsets);
+ info.contentInsets.top = info.visibleInsets.top = mTmpInsets.contentTopInsets;
+ info.touchableRegion.set(mTmpInsets.touchableRegion);
+ info.setTouchableInsets(mTmpInsets.touchableInsets);
+ }
+ };
public VoiceInteractionSession(Context context) {
this(context, new Handler());
@@ -169,7 +324,7 @@ public abstract class VoiceInteractionSession {
public VoiceInteractionSession(Context context, Handler handler) {
mContext = context;
mHandlerCaller = new HandlerCaller(context, handler.getLooper(),
- mHandlerCallerCallback, true);
+ mCallbacks, true);
}
Request findRequest(IVoiceInteractorCallback callback, boolean newRequest) {
@@ -188,6 +343,192 @@ public abstract class VoiceInteractionSession {
}
}
+ void doCreate(IVoiceInteractionManagerService service, IBinder token, Bundle args) {
+ mSystemService = service;
+ mToken = token;
+ onCreate(args);
+ }
+
+ void doDestroy() {
+ if (mInitialized) {
+ mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
+ mInsetsComputer);
+ if (mWindowAdded) {
+ mWindow.dismiss();
+ mWindowAdded = false;
+ }
+ mInitialized = false;
+ }
+ }
+
+ void initViews() {
+ mInitialized = true;
+
+ mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession);
+ mRootView = mInflater.inflate(
+ com.android.internal.R.layout.voice_interaction_session, null);
+ mRootView.setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
+ mWindow.setContentView(mRootView);
+ mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
+
+ mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content);
+ }
+
+ public void showWindow() {
+ if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded
+ + " mWindowVisible=" + mWindowVisible);
+
+ if (mInShowWindow) {
+ Log.w(TAG, "Re-entrance in to showWindow");
+ return;
+ }
+
+ try {
+ mInShowWindow = true;
+ if (!mWindowVisible) {
+ mWindowVisible = true;
+ if (!mWindowAdded) {
+ mWindowAdded = true;
+ View v = onCreateContentView();
+ if (v != null) {
+ setContentView(v);
+ }
+ }
+ mWindow.show();
+ }
+ } finally {
+ mWindowWasVisible = true;
+ mInShowWindow = false;
+ }
+ }
+
+ public void hideWindow() {
+ if (mWindowVisible) {
+ mWindow.hide();
+ mWindowVisible = false;
+ }
+ }
+
+ /**
+ * You can call this to customize the theme used by your IME's window.
+ * This must be set before {@link #onCreate}, so you
+ * will typically call it in your constructor with the resource ID
+ * of your custom theme.
+ */
+ public void setTheme(int theme) {
+ if (mWindow != null) {
+ throw new IllegalStateException("Must be called before onCreate()");
+ }
+ mTheme = theme;
+ }
+
+ public void startVoiceActivity(Intent intent) {
+ if (mToken == null) {
+ throw new IllegalStateException("Can't call before onCreate()");
+ }
+ try {
+ int res = mSystemService.startVoiceActivity(mToken, intent,
+ intent.resolveType(mContext.getContentResolver()));
+ Instrumentation.checkStartActivityResult(res, intent);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public LayoutInflater getLayoutInflater() {
+ return mInflater;
+ }
+
+ public Dialog getWindow() {
+ return mWindow;
+ }
+
+ public void finish() {
+ if (mToken == null) {
+ throw new IllegalStateException("Can't call before onCreate()");
+ }
+ hideWindow();
+ try {
+ mSystemService.finish(mToken);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void onCreate(Bundle args) {
+ mTheme = mTheme != 0 ? mTheme
+ : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession;
+ mInflater = (LayoutInflater)mContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme,
+ mCallbacks, this, mDispatcherState, true);
+ mWindow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+ initViews();
+ mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
+ mWindow.setToken(mToken);
+ }
+
+ public void onDestroy() {
+ }
+
+ public View onCreateContentView() {
+ return null;
+ }
+
+ public void setContentView(View view) {
+ mContentFrame.removeAllViews();
+ mContentFrame.addView(view, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ }
+
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
+ return false;
+ }
+
+ public void onBackPressed() {
+ finish();
+ }
+
+ public void onCloseSystemDialogs() {
+ finish();
+ }
+
+ /**
+ * Compute the interesting insets into your UI. The default implementation
+ * uses the entire window frame as the insets. The default touchable
+ * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}.
+ *
+ * @param outInsets Fill in with the current UI insets.
+ */
+ public void onComputeInsets(Insets outInsets) {
+ int[] loc = mTmpLocation;
+ View decor = getWindow().getWindow().getDecorView();
+ decor.getLocationInWindow(loc);
+ outInsets.contentTopInsets = loc[1];
+ outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME;
+ outInsets.touchableRegion.setEmpty();
+ }
+
+ public void onTaskStarted(Intent intent, int taskId) {
+ }
+
+ public void onTaskFinished(Intent intent, int taskId) {
+ finish();
+ }
+
public abstract boolean[] onGetSupportedCommands(Caller caller, String[] commands);
public abstract void onConfirm(Caller caller, Request request, String prompt, Bundle extras);
public abstract void onCommand(Caller caller, Request request, String command, Bundle extras);
diff --git a/core/java/android/service/voice/VoiceInteractionSessionService.java b/core/java/android/service/voice/VoiceInteractionSessionService.java
index 40e5bba..e793849 100644
--- a/core/java/android/service/voice/VoiceInteractionSessionService.java
+++ b/core/java/android/service/voice/VoiceInteractionSessionService.java
@@ -29,11 +29,15 @@ import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
+/**
+ * An active voice interaction session, initiated by a {@link VoiceInteractionService}.
+ */
public abstract class VoiceInteractionSessionService extends Service {
static final int MSG_NEW_SESSION = 1;
IVoiceInteractionManagerService mSystemService;
+ VoiceInteractionSession mSession;
IVoiceInteractionSessionService mInterface = new IVoiceInteractionSessionService.Stub() {
public void newSession(IBinder token, Bundle args) {
@@ -73,9 +77,14 @@ public abstract class VoiceInteractionSessionService extends Service {
}
void doNewSession(IBinder token, Bundle args) {
- VoiceInteractionSession session = onNewSession(args);
+ if (mSession != null) {
+ mSession.doDestroy();
+ mSession = null;
+ }
+ mSession = onNewSession(args);
try {
- mSystemService.deliverNewSession(token, session.mSession, session.mInteractor);
+ mSystemService.deliverNewSession(token, mSession.mSession, mSession.mInteractor);
+ mSession.doCreate(mSystemService, token, args);
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/speech/tts/BlockingAudioTrack.java b/core/java/android/speech/tts/BlockingAudioTrack.java
index 186cb49..92bb0ac 100644
--- a/core/java/android/speech/tts/BlockingAudioTrack.java
+++ b/core/java/android/speech/tts/BlockingAudioTrack.java
@@ -83,7 +83,7 @@ class BlockingAudioTrack {
mVolume = volume;
mPan = pan;
- mBytesPerFrame = getBytesPerFrame(mAudioFormat) * mChannelCount;
+ mBytesPerFrame = AudioFormat.getBytesPerSample(mAudioFormat) * mChannelCount;
mIsShortUtterance = false;
mAudioBufferSize = 0;
mBytesWritten = 0;
@@ -229,17 +229,6 @@ class BlockingAudioTrack {
return audioTrack;
}
- private static int getBytesPerFrame(int audioFormat) {
- if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) {
- return 1;
- } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
- return 2;
- }
-
- return -1;
- }
-
-
private void blockUntilDone(AudioTrack audioTrack) {
if (mBytesWritten <= 0) {
return;
diff --git a/core/java/android/speech/tts/FileSynthesisCallback.java b/core/java/android/speech/tts/FileSynthesisCallback.java
index 717aeb6..d84f7f0 100644
--- a/core/java/android/speech/tts/FileSynthesisCallback.java
+++ b/core/java/android/speech/tts/FileSynthesisCallback.java
@@ -278,8 +278,7 @@ class FileSynthesisCallback extends AbstractSynthesisCallback {
private ByteBuffer makeWavHeader(int sampleRateInHz, int audioFormat, int channelCount,
int dataLength) {
- // TODO: is AudioFormat.ENCODING_DEFAULT always the same as ENCODING_PCM_16BIT?
- int sampleSizeInBytes = (audioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2);
+ int sampleSizeInBytes = AudioFormat.getBytesPerSample(audioFormat);
int byteRate = sampleRateInHz * sampleSizeInBytes * channelCount;
short blockAlign = (short) (sampleSizeInBytes * channelCount);
short bitsPerSample = (short) (sampleSizeInBytes * 8);
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 9c98b98..b0cbcd2 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -106,4 +106,69 @@ public final class Formatter {
public static String formatIpAddress(int ipv4Address) {
return NetworkUtils.intToInetAddress(ipv4Address).getHostAddress();
}
+
+ private static final int SECONDS_PER_MINUTE = 60;
+ private static final int SECONDS_PER_HOUR = 60 * 60;
+ private static final int SECONDS_PER_DAY = 24 * 60 * 60;
+
+ /**
+ * Returns elapsed time for the given millis, in the following format:
+ * 1 day 5 hrs; will include at most two units, can go down to seconds precision.
+ * @param context the application context
+ * @param millis the elapsed time in milli seconds
+ * @return the formatted elapsed time
+ * @hide
+ */
+ public static String formatShortElapsedTime(Context context, long millis) {
+ long secondsLong = millis / 1000;
+
+ int days = 0, hours = 0, minutes = 0;
+ if (secondsLong >= SECONDS_PER_DAY) {
+ days = (int)(secondsLong / SECONDS_PER_DAY);
+ secondsLong -= days * SECONDS_PER_DAY;
+ }
+ if (secondsLong >= SECONDS_PER_HOUR) {
+ hours = (int)(secondsLong / SECONDS_PER_HOUR);
+ secondsLong -= hours * SECONDS_PER_HOUR;
+ }
+ if (secondsLong >= SECONDS_PER_MINUTE) {
+ minutes = (int)(secondsLong / SECONDS_PER_MINUTE);
+ secondsLong -= minutes * SECONDS_PER_MINUTE;
+ }
+ int seconds = (int)secondsLong;
+
+ if (days >= 2) {
+ days += (hours+12)/24;
+ return context.getString(com.android.internal.R.string.durationDays, days);
+ } else if (days > 0) {
+ if (hours == 1) {
+ return context.getString(com.android.internal.R.string.durationDayHour, days, hours);
+ }
+ return context.getString(com.android.internal.R.string.durationDayHours, days, hours);
+ } else if (hours >= 2) {
+ hours += (minutes+30)/60;
+ return context.getString(com.android.internal.R.string.durationHours, hours);
+ } else if (hours > 0) {
+ if (minutes == 1) {
+ return context.getString(com.android.internal.R.string.durationHourMinute, hours,
+ minutes);
+ }
+ return context.getString(com.android.internal.R.string.durationHourMinutes, hours,
+ minutes);
+ } else if (minutes >= 2) {
+ minutes += (seconds+30)/60;
+ return context.getString(com.android.internal.R.string.durationMinutes, minutes);
+ } else if (minutes > 0) {
+ if (seconds == 1) {
+ return context.getString(com.android.internal.R.string.durationMinuteSecond, minutes,
+ seconds);
+ }
+ return context.getString(com.android.internal.R.string.durationMinuteSeconds, minutes,
+ seconds);
+ } else if (seconds == 1) {
+ return context.getString(com.android.internal.R.string.durationSecond, seconds);
+ } else {
+ return context.getString(com.android.internal.R.string.durationSeconds, seconds);
+ }
+ }
}
diff --git a/core/java/android/transition/ChangeClipBounds.java b/core/java/android/transition/ChangeClipBounds.java
new file mode 100644
index 0000000..a61b29d
--- /dev/null
+++ b/core/java/android/transition/ChangeClipBounds.java
@@ -0,0 +1,96 @@
+/*
+ * 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.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.RectEvaluator;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * ChangeClipBounds captures the {@link android.view.View#getClipBounds()} before and after the
+ * scene change and animates those changes during the transition.
+ */
+public class ChangeClipBounds extends Transition {
+
+ private static final String TAG = "ChangeTransform";
+
+ private static final String PROPNAME_CLIP = "android:clipBounds:clip";
+ private static final String PROPNAME_BOUNDS = "android:clipBounds:bounds";
+
+ private static final String[] sTransitionProperties = {
+ PROPNAME_CLIP,
+ };
+
+ @Override
+ public String[] getTransitionProperties() {
+ return sTransitionProperties;
+ }
+
+ private void captureValues(TransitionValues values) {
+ View view = values.view;
+ if (view.getVisibility() == View.GONE) {
+ return;
+ }
+
+ Rect clip = view.getClipBounds();
+ values.values.put(PROPNAME_CLIP, clip);
+ if (clip == null) {
+ Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+ values.values.put(PROPNAME_BOUNDS, bounds);
+ }
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public Animator createAnimator(final ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null
+ || !startValues.values.containsKey(PROPNAME_CLIP)
+ || !endValues.values.containsKey(PROPNAME_CLIP)) {
+ return null;
+ }
+ Rect start = (Rect) startValues.values.get(PROPNAME_CLIP);
+ Rect end = (Rect) endValues.values.get(PROPNAME_CLIP);
+ if (start == null && end == null) {
+ return null; // No animation required since there is no clip.
+ }
+
+ if (start == null) {
+ start = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+ } else if (end == null) {
+ end = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ }
+ if (start.equals(end)) {
+ return null;
+ }
+
+ endValues.view.setClipBounds(start);
+ RectEvaluator evaluator = new RectEvaluator(new Rect());
+ return ObjectAnimator.ofObject(endValues.view, "clipBounds", evaluator, start, end);
+ }
+}
diff --git a/core/java/android/transition/ChangeTransform.java b/core/java/android/transition/ChangeTransform.java
new file mode 100644
index 0000000..85cb2c7
--- /dev/null
+++ b/core/java/android/transition/ChangeTransform.java
@@ -0,0 +1,163 @@
+/*
+ * 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.transition;
+
+import android.animation.Animator;
+import android.animation.FloatArrayEvaluator;
+import android.animation.ObjectAnimator;
+import android.util.FloatProperty;
+import android.util.Property;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * This Transition captures scale and rotation for Views before and after the
+ * scene change and animates those changes during the transition.
+ *
+ * <p>ChangeTransform does not work when the pivot changes between scenes, so either the
+ * pivot must be set to prevent automatic pivot adjustment or the View's size must be unchanged.</p>
+ */
+public class ChangeTransform extends Transition {
+
+ private static final String TAG = "ChangeTransform";
+
+ private static final String PROPNAME_SCALE_X = "android:changeTransform:scaleX";
+ private static final String PROPNAME_SCALE_Y = "android:changeTransform:scaleY";
+ private static final String PROPNAME_ROTATION_X = "android:changeTransform:rotationX";
+ private static final String PROPNAME_ROTATION_Y = "android:changeTransform:rotationY";
+ private static final String PROPNAME_ROTATION_Z = "android:changeTransform:rotationZ";
+ private static final String PROPNAME_PIVOT_X = "android:changeTransform:pivotX";
+ private static final String PROPNAME_PIVOT_Y = "android:changeTransform:pivotY";
+
+ private static final String[] sTransitionProperties = {
+ PROPNAME_SCALE_X,
+ PROPNAME_SCALE_Y,
+ PROPNAME_ROTATION_X,
+ PROPNAME_ROTATION_Y,
+ PROPNAME_ROTATION_Z,
+ };
+
+ private static final FloatProperty<View>[] sChangedProperties = new FloatProperty[] {
+ (FloatProperty) View.SCALE_X,
+ (FloatProperty) View.SCALE_Y,
+ (FloatProperty) View.ROTATION_X,
+ (FloatProperty) View.ROTATION_Y,
+ (FloatProperty) View.ROTATION,
+ };
+
+ private static Property<View, float[]> TRANSFORMS = new Property<View, float[]>(float[].class,
+ "transforms") {
+ @Override
+ public float[] get(View object) {
+ return null;
+ }
+
+ @Override
+ public void set(View view, float[] values) {
+ for (int i = 0; i < values.length; i++) {
+ float value = values[i];
+ if (value != Float.NaN) {
+ sChangedProperties[i].setValue(view, value);
+ }
+ }
+ }
+ };
+
+ @Override
+ public String[] getTransitionProperties() {
+ return sTransitionProperties;
+ }
+
+ private void captureValues(TransitionValues values) {
+ View view = values.view;
+ if (view.getVisibility() == View.GONE) {
+ return;
+ }
+
+ values.values.put(PROPNAME_SCALE_X, view.getScaleX());
+ values.values.put(PROPNAME_SCALE_Y, view.getScaleY());
+ values.values.put(PROPNAME_PIVOT_X, view.getPivotX());
+ values.values.put(PROPNAME_PIVOT_Y, view.getPivotY());
+ values.values.put(PROPNAME_ROTATION_X, view.getRotationX());
+ values.values.put(PROPNAME_ROTATION_Y, view.getRotationY());
+ values.values.put(PROPNAME_ROTATION_Z, view.getRotation());
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public Animator createAnimator(final ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null
+ || !startValues.values.containsKey(PROPNAME_SCALE_X)
+ || !endValues.values.containsKey(PROPNAME_SCALE_X)
+ || !isPivotSame(startValues, endValues)
+ || !isChanged(startValues, endValues)) {
+ return null;
+ }
+
+ float[] start = createValues(startValues);
+ float[] end = createValues(endValues);
+ for (int i = 0; i < start.length; i++) {
+ if (start[i] == end[i]) {
+ start[i] = Float.NaN;
+ end[i] = Float.NaN;
+ } else {
+ sChangedProperties[i].setValue(endValues.view, start[i]);
+ }
+ }
+ FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[start.length]);
+ return ObjectAnimator.ofObject(endValues.view, TRANSFORMS, evaluator, start, end);
+ }
+
+ private static float[] createValues(TransitionValues transitionValues) {
+ float[] values = new float[sChangedProperties.length];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = (Float) transitionValues.values.get(sTransitionProperties[i]);
+ }
+ return values;
+ }
+
+ private static boolean isPivotSame(TransitionValues startValues, TransitionValues endValues) {
+ float startPivotX = (Float) startValues.values.get(PROPNAME_PIVOT_X);
+ float startPivotY = (Float) startValues.values.get(PROPNAME_PIVOT_Y);
+ float endPivotX = (Float) endValues.values.get(PROPNAME_PIVOT_X);
+ float endPivotY = (Float) endValues.values.get(PROPNAME_PIVOT_Y);
+
+ // We don't support pivot changes, because they could be automatically set
+ // and we can't end the state in an automatic state.
+ return startPivotX == endPivotX && startPivotY == endPivotY;
+ }
+
+ private static boolean isChanged(TransitionValues startValues, TransitionValues endValues) {
+ for (int i = 0; i < sChangedProperties.length; i++) {
+ Object start = startValues.values.get(sTransitionProperties[i]);
+ Object end = endValues.values.get(sTransitionProperties[i]);
+ if (!start.equals(end)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/core/java/android/transition/CircularPropagation.java b/core/java/android/transition/CircularPropagation.java
index 18a3d22..51beb51 100644
--- a/core/java/android/transition/CircularPropagation.java
+++ b/core/java/android/transition/CircularPropagation.java
@@ -34,7 +34,7 @@ import android.view.ViewGroup;
public class CircularPropagation extends VisibilityPropagation {
private static final String TAG = "CircularPropagation";
- private float mPropagationSpeed = 4.0f;
+ private float mPropagationSpeed = 3.0f;
/**
* Sets the speed at which transition propagation happens, relative to the duration of the
@@ -91,8 +91,12 @@ public class CircularPropagation extends VisibilityPropagation {
float maxDistance = distance(0, 0, sceneRoot.getWidth(), sceneRoot.getHeight());
float distanceFraction = distance/maxDistance;
- return Math.round(transition.getDuration() * directionMultiplier / mPropagationSpeed
- * distanceFraction);
+ long duration = transition.getDuration();
+ if (duration < 0) {
+ duration = 300;
+ }
+
+ return Math.round(duration * directionMultiplier / mPropagationSpeed * distanceFraction);
}
private static float distance(float x1, float y1, float x2, float y2) {
diff --git a/core/java/android/transition/MoveImage.java b/core/java/android/transition/MoveImage.java
index d68e971..183cdd2 100644
--- a/core/java/android/transition/MoveImage.java
+++ b/core/java/android/transition/MoveImage.java
@@ -125,7 +125,7 @@ public class MoveImage extends Transition {
Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
- if (!startMatrix.equals(endMatrix)) {
+ if (startMatrix != null && !startMatrix.equals(endMatrix)) {
changes.add(PropertyValuesHolder.ofObject(MatrixClippedDrawable.MATRIX_PROPERTY,
new MatrixEvaluator(), startMatrix, endMatrix));
}
@@ -170,13 +170,20 @@ public class MoveImage extends Transition {
drawable = drawable.getConstantState().newDrawable();
final MatrixClippedDrawable matrixClippedDrawable = new MatrixClippedDrawable(drawable);
+ final ImageView overlayImage = new ImageView(imageView.getContext());
+ final ViewGroupOverlay overlay = sceneRoot.getOverlay();
+ overlay.add(overlayImage);
+ overlayImage.setLeft(0);
+ overlayImage.setTop(0);
+ overlayImage.setRight(sceneRoot.getWidth());
+ overlayImage.setBottom(sceneRoot.getBottom());
+ overlayImage.setScaleType(ImageView.ScaleType.MATRIX);
+ overlayImage.setImageDrawable(matrixClippedDrawable);
matrixClippedDrawable.setMatrix(startMatrix);
matrixClippedDrawable.setBounds(startBounds);
matrixClippedDrawable.setClipRect(startClip);
imageView.setVisibility(View.INVISIBLE);
- final ViewGroupOverlay overlay = sceneRoot.getOverlay();
- overlay.add(matrixClippedDrawable);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(matrixClippedDrawable,
changes.toArray(new PropertyValuesHolder[changes.size()]));
@@ -184,19 +191,24 @@ public class MoveImage extends Transition {
@Override
public void onAnimationEnd(Animator animation) {
imageView.setVisibility(View.VISIBLE);
- overlay.remove(matrixClippedDrawable);
+ overlay.remove(overlayImage);
}
@Override
public void onAnimationPause(Animator animation) {
imageView.setVisibility(View.VISIBLE);
- overlay.remove(matrixClippedDrawable);
+ overlayImage.setVisibility(View.INVISIBLE);
}
@Override
public void onAnimationResume(Animator animation) {
imageView.setVisibility(View.INVISIBLE);
- overlay.add(matrixClippedDrawable);
+ overlayImage.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ onAnimationEnd(animation);
}
};
@@ -218,7 +230,9 @@ public class MoveImage extends Transition {
private static void expandClip(Rect bounds, Matrix matrix, Rect clip, Rect otherClip) {
RectF boundsF = new RectF(bounds);
- matrix.mapRect(boundsF);
+ if (matrix != null) {
+ matrix.mapRect(boundsF);
+ }
clip.left = expandMinDimension(boundsF.left, clip.left, otherClip.left);
clip.top = expandMinDimension(boundsF.top, clip.top, otherClip.top);
clip.right = expandMaxDimension(boundsF.right, clip.right, otherClip.right);
@@ -244,10 +258,20 @@ public class MoveImage extends Transition {
int drawableWidth = drawable.getIntrinsicWidth();
int drawableHeight = drawable.getIntrinsicHeight();
ImageView.ScaleType scaleType = imageView.getScaleType();
- if (drawableWidth <= 0 || drawableHeight <= 0 || scaleType == ImageView.ScaleType.FIT_XY) {
- return null;
+ Matrix matrix;
+ if (drawableWidth <= 0 || drawableHeight <= 0) {
+ matrix = null;
+ } else if (scaleType == ImageView.ScaleType.FIT_XY) {
+ matrix = new Matrix();
+ float scaleX = imageView.getWidth();
+ scaleX /= drawableWidth;
+ float scaleY = imageView.getHeight();
+ scaleY /= drawableHeight;
+ matrix.setScale(scaleX, scaleY);
+ } else {
+ matrix = new Matrix(imageView.getImageMatrix());
}
- return new Matrix(imageView.getImageMatrix());
+ return matrix;
}
private Rect findClip(ImageView imageView) {
diff --git a/core/java/android/transition/SidePropagation.java b/core/java/android/transition/SidePropagation.java
index c331945..5d38ac8 100644
--- a/core/java/android/transition/SidePropagation.java
+++ b/core/java/android/transition/SidePropagation.java
@@ -52,7 +52,7 @@ public class SidePropagation extends VisibilityPropagation {
*/
public static final int BOTTOM = Slide.BOTTOM;
- private float mPropagationSpeed = 4.0f;
+ private float mPropagationSpeed = 3.0f;
private int mSide = BOTTOM;
/**
@@ -129,8 +129,12 @@ public class SidePropagation extends VisibilityPropagation {
float maxDistance = getMaxDistance(sceneRoot);
float distanceFraction = distance/maxDistance;
- return Math.round(transition.getDuration() * directionMultiplier / mPropagationSpeed
- * distanceFraction);
+ long duration = transition.getDuration();
+ if (duration < 0) {
+ duration = 300;
+ }
+
+ return Math.round(duration * directionMultiplier / mPropagationSpeed * distanceFraction);
}
private int distance(int viewX, int viewY, int epicenterX, int epicenterY,
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index c67d6fa..2549fde 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -66,13 +66,12 @@ import java.util.List;
*
* {@sample development/samples/ApiDemos/res/transition/changebounds.xml ChangeBounds}
*
- * <p>{@link android.transition.Explode} transition:</p>
+ * <p>This TransitionSet contains {@link android.transition.Explode} for visibility,
+ * {@link android.transition.ChangeBounds}, {@link android.transition.ChangeTransform},
+ * and {@link android.transition.ChangeClipBounds} for non-<code>ImageView</code>s and
+ * {@link android.transition.MoveImage} for <code>ImageView</code>s:</p>
*
- * {@sample development/samples/ApiDemos/res/transition/explode.xml Explode}
- *
- * <p>{@link android.transition.MoveImage} transition:</p>
- *
- * {@sample development/samples/ApiDemos/res/transition/move_image.xml MoveImage}
+ * {@sample development/samples/ApiDemos/res/transition/explode_move_together.xml MultipleTransform}
*
* <p>Note that attributes for the transition are not required, just as they are
* optional when declared in code; Transitions created from XML resources will use
@@ -89,7 +88,8 @@ import java.util.List;
* transition uses a fadingMode of {@link Fade#OUT} instead of the default
* out-in behavior. Finally, note the use of the <code>targets</code> sub-tag, which
* takes a set of {@link android.R.styleable#TransitionTarget target} tags, each
- * of which lists a specific <code>targetId</code> which this transition acts upon.
+ * of which lists a specific <code>targetId</code>, <code>targetClass</code>,
+ * <code>excludeId</code>, or <code>excludeClass</code>, which this transition acts upon.
* Use of targets is optional, but can be used to either limit the time spent checking
* attributes on unchanging views, or limiting the types of animations run on specific views.
* In this case, we know that only the <code>grayscaleContainer</code> will be
@@ -116,6 +116,7 @@ public abstract class Transition implements Cloneable {
ArrayList<Integer> mTargetIdExcludes = null;
ArrayList<View> mTargetExcludes = null;
ArrayList<Class> mTargetTypeExcludes = null;
+ ArrayList<Class> mTargetTypes = null;
ArrayList<Integer> mTargetIdChildExcludes = null;
ArrayList<View> mTargetChildExcludes = null;
ArrayList<Class> mTargetTypeChildExcludes = null;
@@ -569,19 +570,15 @@ public abstract class Transition implements Cloneable {
}
}
}
- if (mTargetIds.size() == 0 && mTargets.size() == 0) {
+ if (mTargetIds.size() == 0 && mTargets.size() == 0 && mTargetTypes == null) {
return true;
}
- if (mTargetIds.size() > 0) {
- for (int i = 0; i < mTargetIds.size(); ++i) {
- if (mTargetIds.get(i) == targetId) {
- return true;
- }
- }
+ if (mTargetIds.contains((int) targetId) || mTargets.contains(target)) {
+ return true;
}
- if (target != null && mTargets.size() > 0) {
- for (int i = 0; i < mTargets.size(); ++i) {
- if (mTargets.get(i) == target) {
+ if (mTargetTypes != null) {
+ for (int i = 0; i < mTargetTypes.size(); ++i) {
+ if (mTargetTypes.get(i).isInstance(target)) {
return true;
}
}
@@ -727,6 +724,36 @@ public abstract class Transition implements Cloneable {
}
/**
+ * Adds the Class of a target view that this Transition is interested in
+ * animating. By default, there are no targetTypes, and a Transition will
+ * listen for changes on every view in the hierarchy below the sceneRoot
+ * of the Scene being transitioned into. Setting targetTypes constrains
+ * the Transition to only listen for, and act on, views with these classes.
+ * Views with different classes will be ignored.
+ *
+ * <p>Note that any View that can be cast to targetType will be included, so
+ * if targetType is <code>View.class</code>, all Views will be included.</p>
+ *
+ * @see #addTarget(int)
+ * @see #addTarget(android.view.View)
+ * @see #excludeTarget(Class, boolean)
+ * @see #excludeChildren(Class, boolean)
+ *
+ * @param targetType The type to include when running this transition.
+ * @return The Transition to which the target class was added.
+ * Returning the same object makes it easier to chain calls during
+ * construction, such as
+ * <code>transitionSet.addTransitions(new Fade()).addTarget(ImageView.class);</code>
+ */
+ public Transition addTarget(Class targetType) {
+ if (mTargetTypes == null) {
+ mTargetTypes = new ArrayList<Class>();
+ }
+ mTargetTypes.add(targetType);
+ return this;
+ }
+
+ /**
* Removes the given targetId from the list of ids that this Transition
* is interested in animating.
*
@@ -1116,9 +1143,6 @@ public abstract class Transition implements Cloneable {
if (view == null) {
return;
}
- if (!isValidTarget(view, view.getId())) {
- return;
- }
boolean isListViewItem = false;
if (view.getParent() instanceof ListView) {
isListViewItem = true;
diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index 14ecc15..a5e960a 100644
--- a/core/java/android/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -153,6 +153,12 @@ public class TransitionInflater {
} else if ("moveImage".equals(name)) {
transition = new MoveImage();
newTransition = true;
+ } else if ("changeTransform".equals(name)) {
+ transition = new ChangeTransform();
+ newTransition = true;
+ } else if ("changeClipBounds".equals(name)) {
+ transition = new ChangeClipBounds();
+ newTransition = true;
} else if ("autoTransition".equals(name)) {
transition = new AutoTransition();
newTransition = true;
@@ -210,7 +216,6 @@ public class TransitionInflater {
int type;
int depth = parser.getDepth();
- ArrayList<Integer> targetIds = new ArrayList<Integer>();
while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
@@ -225,18 +230,31 @@ public class TransitionInflater {
int id = a.getResourceId(
com.android.internal.R.styleable.TransitionTarget_targetId, -1);
if (id >= 0) {
- targetIds.add(id);
+ transition.addTarget(id);
+ } else if ((id = a.getResourceId(
+ com.android.internal.R.styleable.TransitionTarget_excludeId, -1)) >= 0) {
+ transition.excludeTarget(id, true);
+ } else {
+ String className = a.getString(
+ com.android.internal.R.styleable.TransitionTarget_excludeClass);
+ try {
+ if (className != null) {
+ Class clazz = Class.forName(className);
+ transition.excludeTarget(clazz, true);
+ } else if ((className = a.getString(
+ com.android.internal.R.styleable.TransitionTarget_targetClass))
+ != null) {
+ Class clazz = Class.forName(className);
+ transition.addTarget(clazz);
+ }
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Could not create " + className, e);
+ }
}
} else {
throw new RuntimeException("Unknown scene name: " + parser.getName());
}
}
- int numTargets = targetIds.size();
- if (numTargets > 0) {
- for (int i = 0; i < numTargets; ++i) {
- transition.addTarget(targetIds.get(i));
- }
- }
}
private Transition loadTransition(Transition transition, AttributeSet attrs)
diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java
index 636e3b4..70e7f95 100644
--- a/core/java/android/tv/TvInputService.java
+++ b/core/java/android/tv/TvInputService.java
@@ -150,6 +150,7 @@ public abstract class TvInputService extends Service {
private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
private final WindowManager mWindowManager;
private WindowManager.LayoutParams mWindowParams;
+ private Surface mSurface;
private View mOverlayView;
private boolean mOverlayViewEnabled;
private IBinder mWindowToken;
@@ -346,6 +347,10 @@ public abstract class TvInputService extends Service {
*/
void release() {
onRelease();
+ if (mSurface != null) {
+ mSurface.release();
+ mSurface = null;
+ }
removeOverlayView(true);
}
@@ -354,6 +359,10 @@ public abstract class TvInputService extends Service {
*/
void setSurface(Surface surface) {
onSetSurface(surface);
+ if (mSurface != null) {
+ mSurface.release();
+ }
+ mSurface = surface;
// TODO: Handle failure.
}
diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java
new file mode 100644
index 0000000..9a4bd4b
--- /dev/null
+++ b/core/java/android/util/Range.java
@@ -0,0 +1,144 @@
+/*
+ * 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.util;
+
+import static com.android.internal.util.Preconditions.*;
+
+import android.hardware.camera2.impl.HashCodeHelpers;
+
+/**
+ * Immutable class for describing the range of two numeric values.
+ * <p>
+ * A range (or "interval") defines the inclusive boundaries around a contiguous span of
+ * values of some {@link Comparable} type; for example,
+ * "integers from 1 to 100 inclusive."
+ * </p>
+ * <p>
+ * All ranges are bounded, and the left side of the range is always {@code >=}
+ * the right side of the range.
+ * </p>
+ *
+ * <p>Although the implementation itself is immutable, there is no restriction that objects
+ * stored must also be immutable. If mutable objects are stored here, then the range
+ * effectively becomes mutable. </p>
+ */
+public final class Range<T extends Comparable<? super T>> {
+ /**
+ * Create a new immutable range.
+ *
+ * <p>
+ * The endpoints are {@code [lower, upper]}; that
+ * is the range is bounded. {@code lower} must be {@link Comparable#compareTo lesser or equal}
+ * to {@code upper}.
+ * </p>
+ *
+ * @param lower The lower endpoint (inclusive)
+ * @param upper The upper endpoint (inclusive)
+ *
+ * @throws NullPointerException if {@code lower} or {@code upper} is {@code null}
+ */
+ public Range(final T lower, final T upper) {
+ mLower = checkNotNull(lower, "lower must not be null");
+ mUpper = checkNotNull(upper, "upper must not be null");
+
+ if (lower.compareTo(upper) > 0) {
+ throw new IllegalArgumentException("lower must be less than or equal to upper");
+ }
+ }
+
+ /**
+ * Create a new immutable range, with the argument types inferred.
+ *
+ * <p>
+ * The endpoints are {@code [lower, upper]}; that
+ * is the range is bounded. {@code lower} must be {@link Comparable#compareTo lesser or equal}
+ * to {@code upper}.
+ * </p>
+ *
+ * @param lower The lower endpoint (inclusive)
+ * @param upper The upper endpoint (inclusive)
+ *
+ * @throws NullPointerException if {@code lower} or {@code upper} is {@code null}
+ */
+ public static <T extends Comparable<? super T>> Range<T> create(final T lower, final T upper) {
+ return new Range<T>(lower, upper);
+ }
+
+ /**
+ * Get the lower endpoint.
+ *
+ * @return a non-{@code null} {@code T} reference
+ */
+ public T getLower() {
+ return mLower;
+ }
+
+ /**
+ * Get the upper endpoint.
+ *
+ * @return a non-{@code null} {@code T} reference
+ */
+ public T getUpper() {
+ return mUpper;
+ }
+
+ /**
+ * Compare two ranges for equality.
+ *
+ * <p>A range is considered equal if and only if both the lower and upper endpoints
+ * are also equal.</p>
+ *
+ * @return {@code true} if the ranges are equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof Range) {
+ @SuppressWarnings("rawtypes")
+ final
+ Range other = (Range) obj;
+ return mLower.equals(other.mLower) && mUpper.equals(other.mUpper);
+ }
+ return false;
+ }
+
+ /**
+ * Return the range as a string representation {@code "[lower, upper]"}.
+ *
+ * @return string representation of the range
+ */
+ @Override
+ public String toString() {
+ return String.format("[%s, %s]", mLower, mUpper);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCodeHelpers.hashCode(mLower, mUpper);
+ }
+
+ private final T mLower;
+ private final T mUpper;
+};
diff --git a/core/java/android/util/Size.java b/core/java/android/util/Size.java
new file mode 100644
index 0000000..ba1a35f
--- /dev/null
+++ b/core/java/android/util/Size.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * Immutable class for describing width and height dimensions in pixels.
+ */
+public final class Size {
+ /**
+ * Create a new immutable Size instance.
+ *
+ * @param width The width of the size, in pixels
+ * @param height The height of the size, in pixels
+ */
+ public Size(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ }
+
+ /**
+ * Get the width of the size (in pixels).
+ * @return width
+ */
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * Get the height of the size (in pixels).
+ * @return height
+ */
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * Check if this size is equal to another size.
+ * <p>
+ * Two sizes are equal if and only if both their widths and heights are
+ * equal.
+ * </p>
+ * <p>
+ * A size object is never equal to any other type of object.
+ * </p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof Size) {
+ Size other = (Size) obj;
+ return mWidth == other.mWidth && mHeight == other.mHeight;
+ }
+ return false;
+ }
+
+ /**
+ * Return the size represented as a string with the format {@code "WxH"}
+ *
+ * @return string representation of the size
+ */
+ @Override
+ public String toString() {
+ return mWidth + "x" + mHeight;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ // assuming most sizes are <2^16, doing a rotate will give us perfect hashing
+ return mHeight ^ ((mWidth << (Integer.SIZE / 2)) | (mWidth >>> (Integer.SIZE / 2)));
+ }
+
+ private final int mWidth;
+ private final int mHeight;
+};
diff --git a/core/java/android/util/SizeF.java b/core/java/android/util/SizeF.java
new file mode 100644
index 0000000..0a8b4ed
--- /dev/null
+++ b/core/java/android/util/SizeF.java
@@ -0,0 +1,108 @@
+/*
+ * 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.util;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Immutable class for describing width and height dimensions in some arbitrary
+ * unit.
+ * <p>
+ * Width and height are finite values stored as a floating point representation.
+ * </p>
+ */
+public final class SizeF {
+ /**
+ * Create a new immutable SizeF instance.
+ *
+ * <p>Both the {@code width} and the {@code height} must be a finite number.
+ * In particular, {@code NaN} and positive/negative infinity are illegal values.</p>
+ *
+ * @param width The width of the size
+ * @param height The height of the size
+ *
+ * @throws IllegalArgumentException
+ * if either {@code width} or {@code height} was not finite.
+ */
+ public SizeF(final float width, final float height) {
+ mWidth = checkArgumentFinite(width, "width");
+ mHeight = checkArgumentFinite(height, "height");
+ }
+
+ /**
+ * Get the width of the size (as an arbitrary unit).
+ * @return width
+ */
+ public float getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * Get the height of the size (as an arbitrary unit).
+ * @return height
+ */
+ public float getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * Check if this size is equal to another size.
+ *
+ * <p>Two sizes are equal if and only if both their widths and heights are the same.</p>
+ *
+ * <p>For this purpose, the width/height float values are considered to be the same if and only
+ * if the method {@link Float#floatToIntBits(float)} returns the identical {@code int} value
+ * when applied to each.</p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof SizeF) {
+ final SizeF other = (SizeF) obj;
+ return mWidth == other.mWidth && mHeight == other.mHeight;
+ }
+ return false;
+ }
+
+ /**
+ * Return the size represented as a string with the format {@code "WxH"}
+ *
+ * @return string representation of the size
+ */
+ @Override
+ public String toString() {
+ return mWidth + "x" + mHeight;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return Float.floatToIntBits(mWidth) ^ Float.floatToIntBits(mHeight);
+ }
+
+ private final float mWidth;
+ private final float mHeight;
+};
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index f1523ae..0a76075 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -259,6 +259,14 @@ public final class Choreographer {
return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay;
}
+ /**
+ * @return The refresh rate as the nanoseconds between frames
+ * @hide
+ */
+ long getFrameIntervalNanos() {
+ return mFrameIntervalNanos;
+ }
+
void dump(String prefix, PrintWriter writer) {
String innerPrefix = prefix + " ";
writer.print(prefix); writer.println("Choreographer:");
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 34b85d9..11948b2 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -18,6 +18,7 @@ package android.view;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
import android.graphics.DrawFilter;
import android.graphics.Matrix;
import android.graphics.NinePatch;
@@ -889,6 +890,16 @@ class GLES20Canvas extends HardwareCanvas {
float radius, long paint);
@Override
+ public void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
+ CanvasProperty<Float> radius, CanvasProperty<Paint> paint) {
+ nDrawCircle(mRenderer, cx.getNativeContainer(), cy.getNativeContainer(),
+ radius.getNativeContainer(), paint.getNativeContainer());
+ }
+
+ private static native void nDrawCircle(long renderer, long propCx,
+ long propCy, long propRadius, long propPaint);
+
+ @Override
public void drawColor(int color) {
drawColor(color, PorterDuff.Mode.SRC_OVER);
}
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index 2b29e5c..a94ec3a 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.NonNull;
import android.util.Pools.SynchronizedPool;
/**
@@ -32,19 +33,25 @@ class GLES20RecordingCanvas extends GLES20Canvas {
private static final SynchronizedPool<GLES20RecordingCanvas> sPool =
new SynchronizedPool<GLES20RecordingCanvas>(POOL_LIMIT);
+ RenderNode mNode;
+
private GLES20RecordingCanvas() {
super(true, true);
}
- static GLES20RecordingCanvas obtain() {
+ static GLES20RecordingCanvas obtain(@NonNull RenderNode node) {
+ if (node == null) throw new IllegalArgumentException("node cannot be null");
+
GLES20RecordingCanvas canvas = sPool.acquire();
if (canvas == null) {
canvas = new GLES20RecordingCanvas();
}
+ canvas.mNode = node;
return canvas;
}
void recycle() {
+ mNode = null;
sPool.release(this);
}
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 233f846..7ec2cc6 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -18,6 +18,7 @@ package android.view;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -189,4 +190,7 @@ public abstract class HardwareCanvas extends Canvas {
* @hide
*/
abstract void clearLayerUpdates();
+
+ public abstract void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
+ CanvasProperty<Float> radius, CanvasProperty<Paint> paint);
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index c3f429c..05e202b 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -645,12 +645,11 @@ public class KeyEvent extends InputEvent implements Parcelable {
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
// frameworks/native/include/android/keycodes.h
- // frameworks/base/include/androidfw/KeycodeLabels.h
+ // frameworks/base/include/androidfw/InputEventAttributes.h
// external/webkit/WebKit/android/plugins/ANPKeyCodes.h
// frameworks/base/core/res/res/values/attrs.xml
// emulator?
// LAST_KEYCODE
- // KEYCODE_SYMBOLIC_NAMES
//
// Also Android currently does not reserve code ranges for vendor-
// specific key codes. If you have new key codes to have, you
@@ -658,237 +657,6 @@ public class KeyEvent extends InputEvent implements Parcelable {
// those new codes. This is intended to maintain a consistent
// set of key code definitions across all Android devices.
- // Symbolic names of all key codes.
- private static final SparseArray<String> KEYCODE_SYMBOLIC_NAMES = new SparseArray<String>();
- private static void populateKeycodeSymbolicNames() {
- SparseArray<String> names = KEYCODE_SYMBOLIC_NAMES;
- names.append(KEYCODE_UNKNOWN, "KEYCODE_UNKNOWN");
- names.append(KEYCODE_SOFT_LEFT, "KEYCODE_SOFT_LEFT");
- names.append(KEYCODE_SOFT_RIGHT, "KEYCODE_SOFT_RIGHT");
- names.append(KEYCODE_HOME, "KEYCODE_HOME");
- names.append(KEYCODE_BACK, "KEYCODE_BACK");
- names.append(KEYCODE_CALL, "KEYCODE_CALL");
- names.append(KEYCODE_ENDCALL, "KEYCODE_ENDCALL");
- names.append(KEYCODE_0, "KEYCODE_0");
- names.append(KEYCODE_1, "KEYCODE_1");
- names.append(KEYCODE_2, "KEYCODE_2");
- names.append(KEYCODE_3, "KEYCODE_3");
- names.append(KEYCODE_4, "KEYCODE_4");
- names.append(KEYCODE_5, "KEYCODE_5");
- names.append(KEYCODE_6, "KEYCODE_6");
- names.append(KEYCODE_7, "KEYCODE_7");
- names.append(KEYCODE_8, "KEYCODE_8");
- names.append(KEYCODE_9, "KEYCODE_9");
- names.append(KEYCODE_STAR, "KEYCODE_STAR");
- names.append(KEYCODE_POUND, "KEYCODE_POUND");
- names.append(KEYCODE_DPAD_UP, "KEYCODE_DPAD_UP");
- names.append(KEYCODE_DPAD_DOWN, "KEYCODE_DPAD_DOWN");
- names.append(KEYCODE_DPAD_LEFT, "KEYCODE_DPAD_LEFT");
- names.append(KEYCODE_DPAD_RIGHT, "KEYCODE_DPAD_RIGHT");
- names.append(KEYCODE_DPAD_CENTER, "KEYCODE_DPAD_CENTER");
- names.append(KEYCODE_VOLUME_UP, "KEYCODE_VOLUME_UP");
- names.append(KEYCODE_VOLUME_DOWN, "KEYCODE_VOLUME_DOWN");
- names.append(KEYCODE_POWER, "KEYCODE_POWER");
- names.append(KEYCODE_CAMERA, "KEYCODE_CAMERA");
- names.append(KEYCODE_CLEAR, "KEYCODE_CLEAR");
- names.append(KEYCODE_A, "KEYCODE_A");
- names.append(KEYCODE_B, "KEYCODE_B");
- names.append(KEYCODE_C, "KEYCODE_C");
- names.append(KEYCODE_D, "KEYCODE_D");
- names.append(KEYCODE_E, "KEYCODE_E");
- names.append(KEYCODE_F, "KEYCODE_F");
- names.append(KEYCODE_G, "KEYCODE_G");
- names.append(KEYCODE_H, "KEYCODE_H");
- names.append(KEYCODE_I, "KEYCODE_I");
- names.append(KEYCODE_J, "KEYCODE_J");
- names.append(KEYCODE_K, "KEYCODE_K");
- names.append(KEYCODE_L, "KEYCODE_L");
- names.append(KEYCODE_M, "KEYCODE_M");
- names.append(KEYCODE_N, "KEYCODE_N");
- names.append(KEYCODE_O, "KEYCODE_O");
- names.append(KEYCODE_P, "KEYCODE_P");
- names.append(KEYCODE_Q, "KEYCODE_Q");
- names.append(KEYCODE_R, "KEYCODE_R");
- names.append(KEYCODE_S, "KEYCODE_S");
- names.append(KEYCODE_T, "KEYCODE_T");
- names.append(KEYCODE_U, "KEYCODE_U");
- names.append(KEYCODE_V, "KEYCODE_V");
- names.append(KEYCODE_W, "KEYCODE_W");
- names.append(KEYCODE_X, "KEYCODE_X");
- names.append(KEYCODE_Y, "KEYCODE_Y");
- names.append(KEYCODE_Z, "KEYCODE_Z");
- names.append(KEYCODE_COMMA, "KEYCODE_COMMA");
- names.append(KEYCODE_PERIOD, "KEYCODE_PERIOD");
- names.append(KEYCODE_ALT_LEFT, "KEYCODE_ALT_LEFT");
- names.append(KEYCODE_ALT_RIGHT, "KEYCODE_ALT_RIGHT");
- names.append(KEYCODE_SHIFT_LEFT, "KEYCODE_SHIFT_LEFT");
- names.append(KEYCODE_SHIFT_RIGHT, "KEYCODE_SHIFT_RIGHT");
- names.append(KEYCODE_TAB, "KEYCODE_TAB");
- names.append(KEYCODE_SPACE, "KEYCODE_SPACE");
- names.append(KEYCODE_SYM, "KEYCODE_SYM");
- names.append(KEYCODE_EXPLORER, "KEYCODE_EXPLORER");
- names.append(KEYCODE_ENVELOPE, "KEYCODE_ENVELOPE");
- names.append(KEYCODE_ENTER, "KEYCODE_ENTER");
- names.append(KEYCODE_DEL, "KEYCODE_DEL");
- names.append(KEYCODE_GRAVE, "KEYCODE_GRAVE");
- names.append(KEYCODE_MINUS, "KEYCODE_MINUS");
- names.append(KEYCODE_EQUALS, "KEYCODE_EQUALS");
- names.append(KEYCODE_LEFT_BRACKET, "KEYCODE_LEFT_BRACKET");
- names.append(KEYCODE_RIGHT_BRACKET, "KEYCODE_RIGHT_BRACKET");
- names.append(KEYCODE_BACKSLASH, "KEYCODE_BACKSLASH");
- names.append(KEYCODE_SEMICOLON, "KEYCODE_SEMICOLON");
- names.append(KEYCODE_APOSTROPHE, "KEYCODE_APOSTROPHE");
- names.append(KEYCODE_SLASH, "KEYCODE_SLASH");
- names.append(KEYCODE_AT, "KEYCODE_AT");
- names.append(KEYCODE_NUM, "KEYCODE_NUM");
- names.append(KEYCODE_HEADSETHOOK, "KEYCODE_HEADSETHOOK");
- names.append(KEYCODE_FOCUS, "KEYCODE_FOCUS");
- names.append(KEYCODE_PLUS, "KEYCODE_PLUS");
- names.append(KEYCODE_MENU, "KEYCODE_MENU");
- names.append(KEYCODE_NOTIFICATION, "KEYCODE_NOTIFICATION");
- names.append(KEYCODE_SEARCH, "KEYCODE_SEARCH");
- names.append(KEYCODE_MEDIA_PLAY_PAUSE, "KEYCODE_MEDIA_PLAY_PAUSE");
- names.append(KEYCODE_MEDIA_STOP, "KEYCODE_MEDIA_STOP");
- names.append(KEYCODE_MEDIA_NEXT, "KEYCODE_MEDIA_NEXT");
- names.append(KEYCODE_MEDIA_PREVIOUS, "KEYCODE_MEDIA_PREVIOUS");
- names.append(KEYCODE_MEDIA_REWIND, "KEYCODE_MEDIA_REWIND");
- names.append(KEYCODE_MEDIA_FAST_FORWARD, "KEYCODE_MEDIA_FAST_FORWARD");
- names.append(KEYCODE_MUTE, "KEYCODE_MUTE");
- names.append(KEYCODE_PAGE_UP, "KEYCODE_PAGE_UP");
- names.append(KEYCODE_PAGE_DOWN, "KEYCODE_PAGE_DOWN");
- names.append(KEYCODE_PICTSYMBOLS, "KEYCODE_PICTSYMBOLS");
- names.append(KEYCODE_SWITCH_CHARSET, "KEYCODE_SWITCH_CHARSET");
- names.append(KEYCODE_BUTTON_A, "KEYCODE_BUTTON_A");
- names.append(KEYCODE_BUTTON_B, "KEYCODE_BUTTON_B");
- names.append(KEYCODE_BUTTON_C, "KEYCODE_BUTTON_C");
- names.append(KEYCODE_BUTTON_X, "KEYCODE_BUTTON_X");
- names.append(KEYCODE_BUTTON_Y, "KEYCODE_BUTTON_Y");
- names.append(KEYCODE_BUTTON_Z, "KEYCODE_BUTTON_Z");
- names.append(KEYCODE_BUTTON_L1, "KEYCODE_BUTTON_L1");
- names.append(KEYCODE_BUTTON_R1, "KEYCODE_BUTTON_R1");
- names.append(KEYCODE_BUTTON_L2, "KEYCODE_BUTTON_L2");
- names.append(KEYCODE_BUTTON_R2, "KEYCODE_BUTTON_R2");
- names.append(KEYCODE_BUTTON_THUMBL, "KEYCODE_BUTTON_THUMBL");
- names.append(KEYCODE_BUTTON_THUMBR, "KEYCODE_BUTTON_THUMBR");
- names.append(KEYCODE_BUTTON_START, "KEYCODE_BUTTON_START");
- names.append(KEYCODE_BUTTON_SELECT, "KEYCODE_BUTTON_SELECT");
- names.append(KEYCODE_BUTTON_MODE, "KEYCODE_BUTTON_MODE");
- names.append(KEYCODE_ESCAPE, "KEYCODE_ESCAPE");
- names.append(KEYCODE_FORWARD_DEL, "KEYCODE_FORWARD_DEL");
- names.append(KEYCODE_CTRL_LEFT, "KEYCODE_CTRL_LEFT");
- names.append(KEYCODE_CTRL_RIGHT, "KEYCODE_CTRL_RIGHT");
- names.append(KEYCODE_CAPS_LOCK, "KEYCODE_CAPS_LOCK");
- names.append(KEYCODE_SCROLL_LOCK, "KEYCODE_SCROLL_LOCK");
- names.append(KEYCODE_META_LEFT, "KEYCODE_META_LEFT");
- names.append(KEYCODE_META_RIGHT, "KEYCODE_META_RIGHT");
- names.append(KEYCODE_FUNCTION, "KEYCODE_FUNCTION");
- names.append(KEYCODE_SYSRQ, "KEYCODE_SYSRQ");
- names.append(KEYCODE_BREAK, "KEYCODE_BREAK");
- names.append(KEYCODE_MOVE_HOME, "KEYCODE_MOVE_HOME");
- names.append(KEYCODE_MOVE_END, "KEYCODE_MOVE_END");
- names.append(KEYCODE_INSERT, "KEYCODE_INSERT");
- names.append(KEYCODE_FORWARD, "KEYCODE_FORWARD");
- names.append(KEYCODE_MEDIA_PLAY, "KEYCODE_MEDIA_PLAY");
- names.append(KEYCODE_MEDIA_PAUSE, "KEYCODE_MEDIA_PAUSE");
- names.append(KEYCODE_MEDIA_CLOSE, "KEYCODE_MEDIA_CLOSE");
- names.append(KEYCODE_MEDIA_EJECT, "KEYCODE_MEDIA_EJECT");
- names.append(KEYCODE_MEDIA_RECORD, "KEYCODE_MEDIA_RECORD");
- names.append(KEYCODE_F1, "KEYCODE_F1");
- names.append(KEYCODE_F2, "KEYCODE_F2");
- names.append(KEYCODE_F3, "KEYCODE_F3");
- names.append(KEYCODE_F4, "KEYCODE_F4");
- names.append(KEYCODE_F5, "KEYCODE_F5");
- names.append(KEYCODE_F6, "KEYCODE_F6");
- names.append(KEYCODE_F7, "KEYCODE_F7");
- names.append(KEYCODE_F8, "KEYCODE_F8");
- names.append(KEYCODE_F9, "KEYCODE_F9");
- names.append(KEYCODE_F10, "KEYCODE_F10");
- names.append(KEYCODE_F11, "KEYCODE_F11");
- names.append(KEYCODE_F12, "KEYCODE_F12");
- names.append(KEYCODE_NUM_LOCK, "KEYCODE_NUM_LOCK");
- names.append(KEYCODE_NUMPAD_0, "KEYCODE_NUMPAD_0");
- names.append(KEYCODE_NUMPAD_1, "KEYCODE_NUMPAD_1");
- names.append(KEYCODE_NUMPAD_2, "KEYCODE_NUMPAD_2");
- names.append(KEYCODE_NUMPAD_3, "KEYCODE_NUMPAD_3");
- names.append(KEYCODE_NUMPAD_4, "KEYCODE_NUMPAD_4");
- names.append(KEYCODE_NUMPAD_5, "KEYCODE_NUMPAD_5");
- names.append(KEYCODE_NUMPAD_6, "KEYCODE_NUMPAD_6");
- names.append(KEYCODE_NUMPAD_7, "KEYCODE_NUMPAD_7");
- names.append(KEYCODE_NUMPAD_8, "KEYCODE_NUMPAD_8");
- names.append(KEYCODE_NUMPAD_9, "KEYCODE_NUMPAD_9");
- names.append(KEYCODE_NUMPAD_DIVIDE, "KEYCODE_NUMPAD_DIVIDE");
- names.append(KEYCODE_NUMPAD_MULTIPLY, "KEYCODE_NUMPAD_MULTIPLY");
- names.append(KEYCODE_NUMPAD_SUBTRACT, "KEYCODE_NUMPAD_SUBTRACT");
- names.append(KEYCODE_NUMPAD_ADD, "KEYCODE_NUMPAD_ADD");
- names.append(KEYCODE_NUMPAD_DOT, "KEYCODE_NUMPAD_DOT");
- names.append(KEYCODE_NUMPAD_COMMA, "KEYCODE_NUMPAD_COMMA");
- names.append(KEYCODE_NUMPAD_ENTER, "KEYCODE_NUMPAD_ENTER");
- names.append(KEYCODE_NUMPAD_EQUALS, "KEYCODE_NUMPAD_EQUALS");
- names.append(KEYCODE_NUMPAD_LEFT_PAREN, "KEYCODE_NUMPAD_LEFT_PAREN");
- names.append(KEYCODE_NUMPAD_RIGHT_PAREN, "KEYCODE_NUMPAD_RIGHT_PAREN");
- names.append(KEYCODE_VOLUME_MUTE, "KEYCODE_VOLUME_MUTE");
- names.append(KEYCODE_INFO, "KEYCODE_INFO");
- names.append(KEYCODE_CHANNEL_UP, "KEYCODE_CHANNEL_UP");
- names.append(KEYCODE_CHANNEL_DOWN, "KEYCODE_CHANNEL_DOWN");
- names.append(KEYCODE_ZOOM_IN, "KEYCODE_ZOOM_IN");
- names.append(KEYCODE_ZOOM_OUT, "KEYCODE_ZOOM_OUT");
- names.append(KEYCODE_TV, "KEYCODE_TV");
- names.append(KEYCODE_WINDOW, "KEYCODE_WINDOW");
- names.append(KEYCODE_GUIDE, "KEYCODE_GUIDE");
- names.append(KEYCODE_DVR, "KEYCODE_DVR");
- names.append(KEYCODE_BOOKMARK, "KEYCODE_BOOKMARK");
- names.append(KEYCODE_CAPTIONS, "KEYCODE_CAPTIONS");
- names.append(KEYCODE_SETTINGS, "KEYCODE_SETTINGS");
- names.append(KEYCODE_TV_POWER, "KEYCODE_TV_POWER");
- names.append(KEYCODE_TV_INPUT, "KEYCODE_TV_INPUT");
- names.append(KEYCODE_STB_INPUT, "KEYCODE_STB_INPUT");
- names.append(KEYCODE_STB_POWER, "KEYCODE_STB_POWER");
- names.append(KEYCODE_AVR_POWER, "KEYCODE_AVR_POWER");
- names.append(KEYCODE_AVR_INPUT, "KEYCODE_AVR_INPUT");
- names.append(KEYCODE_PROG_RED, "KEYCODE_PROG_RED");
- names.append(KEYCODE_PROG_GREEN, "KEYCODE_PROG_GREEN");
- names.append(KEYCODE_PROG_YELLOW, "KEYCODE_PROG_YELLOW");
- names.append(KEYCODE_PROG_BLUE, "KEYCODE_PROG_BLUE");
- names.append(KEYCODE_APP_SWITCH, "KEYCODE_APP_SWITCH");
- names.append(KEYCODE_BUTTON_1, "KEYCODE_BUTTON_1");
- names.append(KEYCODE_BUTTON_2, "KEYCODE_BUTTON_2");
- names.append(KEYCODE_BUTTON_3, "KEYCODE_BUTTON_3");
- names.append(KEYCODE_BUTTON_4, "KEYCODE_BUTTON_4");
- names.append(KEYCODE_BUTTON_5, "KEYCODE_BUTTON_5");
- names.append(KEYCODE_BUTTON_6, "KEYCODE_BUTTON_6");
- names.append(KEYCODE_BUTTON_7, "KEYCODE_BUTTON_7");
- names.append(KEYCODE_BUTTON_8, "KEYCODE_BUTTON_8");
- names.append(KEYCODE_BUTTON_9, "KEYCODE_BUTTON_9");
- names.append(KEYCODE_BUTTON_10, "KEYCODE_BUTTON_10");
- names.append(KEYCODE_BUTTON_11, "KEYCODE_BUTTON_11");
- names.append(KEYCODE_BUTTON_12, "KEYCODE_BUTTON_12");
- names.append(KEYCODE_BUTTON_13, "KEYCODE_BUTTON_13");
- names.append(KEYCODE_BUTTON_14, "KEYCODE_BUTTON_14");
- names.append(KEYCODE_BUTTON_15, "KEYCODE_BUTTON_15");
- names.append(KEYCODE_BUTTON_16, "KEYCODE_BUTTON_16");
- names.append(KEYCODE_LANGUAGE_SWITCH, "KEYCODE_LANGUAGE_SWITCH");
- names.append(KEYCODE_MANNER_MODE, "KEYCODE_MANNER_MODE");
- names.append(KEYCODE_3D_MODE, "KEYCODE_3D_MODE");
- names.append(KEYCODE_CONTACTS, "KEYCODE_CONTACTS");
- names.append(KEYCODE_CALENDAR, "KEYCODE_CALENDAR");
- names.append(KEYCODE_MUSIC, "KEYCODE_MUSIC");
- names.append(KEYCODE_CALCULATOR, "KEYCODE_CALCULATOR");
- names.append(KEYCODE_ZENKAKU_HANKAKU, "KEYCODE_ZENKAKU_HANKAKU");
- names.append(KEYCODE_EISU, "KEYCODE_EISU");
- names.append(KEYCODE_MUHENKAN, "KEYCODE_MUHENKAN");
- names.append(KEYCODE_HENKAN, "KEYCODE_HENKAN");
- names.append(KEYCODE_KATAKANA_HIRAGANA, "KEYCODE_KATAKANA_HIRAGANA");
- names.append(KEYCODE_YEN, "KEYCODE_YEN");
- names.append(KEYCODE_RO, "KEYCODE_RO");
- names.append(KEYCODE_KANA, "KEYCODE_KANA");
- names.append(KEYCODE_ASSIST, "KEYCODE_ASSIST");
- names.append(KEYCODE_BRIGHTNESS_DOWN, "KEYCODE_BRIGHTNESS_DOWN");
- names.append(KEYCODE_BRIGHTNESS_UP, "KEYCODE_BRIGHTNESS_UP");
- names.append(KEYCODE_MEDIA_AUDIO_TRACK, "KEYCODE_MEDIA_AUDIO_TRACK");
- names.append(KEYCODE_SLEEP, "KEYCODE_SLEEP");
- names.append(KEYCODE_WAKEUP, "KEYCODE_WAKEUP");
- };
-
// Symbolic names of all metakeys in bit order from least significant to most significant.
// Accordingly there are exactly 32 values in this table.
private static final String[] META_SYMBOLIC_NAMES = new String[] {
@@ -926,6 +694,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
"0x80000000",
};
+ private static final String LABEL_PREFIX = "KEYCODE_";
+
/**
* @deprecated There are now more than MAX_KEYCODE keycodes.
* Use {@link #getMaxKeyCode()} instead.
@@ -1367,9 +1137,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
}
- static {
- populateKeycodeSymbolicNames();
- }
+ private static native String nativeKeyCodeToString(int keyCode);
+ private static native int nativeKeyCodeFromString(String keyCode);
private KeyEvent() {
}
@@ -1792,19 +1561,15 @@ public class KeyEvent extends InputEvent implements Parcelable {
return mAction == ACTION_DOWN;
}
- /**
- * Is this a system key? System keys can not be used for menu shortcuts.
- *
- * TODO: this information should come from a table somewhere.
- * TODO: should the dpad keys be here? arguably, because they also shouldn't be menu shortcuts
+ /** Is this a system key? System keys can not be used for menu shortcuts.
*/
public final boolean isSystem() {
- return native_isSystemKey(mKeyCode);
+ return isSystemKey(mKeyCode);
}
/** @hide */
- public final boolean hasDefaultAction() {
- return native_hasDefaultAction(mKeyCode);
+ public final boolean isWakeKey() {
+ return isWakeKey(mKeyCode);
}
/**
@@ -1887,6 +1652,62 @@ public class KeyEvent extends InputEvent implements Parcelable {
return false;
}
+
+ /** Is this a system key? System keys can not be used for menu shortcuts.
+ * @hide
+ */
+ public static final boolean isSystemKey(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_MENU:
+ case KeyEvent.KEYCODE_SOFT_RIGHT:
+ case KeyEvent.KEYCODE_HOME:
+ case KeyEvent.KEYCODE_BACK:
+ case KeyEvent.KEYCODE_CALL:
+ case KeyEvent.KEYCODE_ENDCALL:
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ case KeyEvent.KEYCODE_VOLUME_MUTE:
+ case KeyEvent.KEYCODE_MUTE:
+ case KeyEvent.KEYCODE_POWER:
+ case KeyEvent.KEYCODE_HEADSETHOOK:
+ case KeyEvent.KEYCODE_MEDIA_PLAY:
+ case KeyEvent.KEYCODE_MEDIA_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_STOP:
+ case KeyEvent.KEYCODE_MEDIA_NEXT:
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ case KeyEvent.KEYCODE_MEDIA_RECORD:
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+ case KeyEvent.KEYCODE_CAMERA:
+ case KeyEvent.KEYCODE_FOCUS:
+ case KeyEvent.KEYCODE_SEARCH:
+ 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;
+ }
+
+ return false;
+ }
+
+ /** @hide */
+ public static final boolean isWakeKey(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_BACK:
+ case KeyEvent.KEYCODE_POWER:
+ case KeyEvent.KEYCODE_MENU:
+ case KeyEvent.KEYCODE_SLEEP:
+ case KeyEvent.KEYCODE_WAKEUP:
+ return true;
+ }
+ return false;
+ }
+
/** {@inheritDoc} */
@Override
public final int getDeviceId() {
@@ -2866,8 +2687,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
* @see KeyCharacterMap#getDisplayLabel
*/
public static String keyCodeToString(int keyCode) {
- String symbolicName = KEYCODE_SYMBOLIC_NAMES.get(keyCode);
- return symbolicName != null ? symbolicName : Integer.toString(keyCode);
+ String symbolicName = nativeKeyCodeToString(keyCode);
+ return symbolicName != null ? LABEL_PREFIX + symbolicName : Integer.toString(keyCode);
}
/**
@@ -2879,17 +2700,13 @@ public class KeyEvent extends InputEvent implements Parcelable {
* @see #keycodeToString(int)
*/
public static int keyCodeFromString(String symbolicName) {
- if (symbolicName == null) {
- throw new IllegalArgumentException("symbolicName must not be null");
+ if (symbolicName.startsWith(LABEL_PREFIX)) {
+ symbolicName = symbolicName.substring(LABEL_PREFIX.length());
}
-
- final int count = KEYCODE_SYMBOLIC_NAMES.size();
- for (int i = 0; i < count; i++) {
- if (symbolicName.equals(KEYCODE_SYMBOLIC_NAMES.valueAt(i))) {
- return i;
- }
+ int keyCode = nativeKeyCodeFromString(symbolicName);
+ if (keyCode > 0) {
+ return keyCode;
}
-
try {
return Integer.parseInt(symbolicName, 10);
} catch (NumberFormatException ex) {
@@ -2977,7 +2794,4 @@ public class KeyEvent extends InputEvent implements Parcelable {
out.writeLong(mDownTime);
out.writeLong(mEventTime);
}
-
- private native boolean native_isSystemKey(int keyCode);
- private native boolean native_hasDefaultAction(int keyCode);
}
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index e19bda9..b9ed801 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -21,13 +21,16 @@ import android.os.Handler;
import android.os.Message;
import android.os.Trace;
import android.widget.FrameLayout;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.Xml;
import java.io.IOException;
@@ -61,7 +64,8 @@ import java.util.HashMap;
* @see Context#getSystemService
*/
public abstract class LayoutInflater {
- private final boolean DEBUG = false;
+ private static final String TAG = LayoutInflater.class.getSimpleName();
+ private static final boolean DEBUG = false;
/**
* This field should be made private, so it is hidden from the SDK.
@@ -395,8 +399,13 @@ public abstract class LayoutInflater {
* the inflated XML file.
*/
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
- if (DEBUG) System.out.println("INFLATING from resource: " + resource);
- XmlResourceParser parser = getContext().getResources().getLayout(resource);
+ final Resources res = getContext().getResources();
+ if (DEBUG) {
+ Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ + Integer.toHexString(resource) + ")");
+ }
+
+ final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 6378ffd..0626ab9 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -167,6 +167,7 @@ import android.util.SparseArray;
*/
public final class MotionEvent extends InputEvent implements Parcelable {
private static final long NS_PER_MS = 1000000;
+ private static final String LABEL_PREFIX = "AXIS_";
/**
* An invalid pointer id.
@@ -1369,6 +1370,9 @@ public final class MotionEvent extends InputEvent implements Parcelable {
private static native long nativeReadFromParcel(long nativePtr, Parcel parcel);
private static native void nativeWriteToParcel(long nativePtr, Parcel parcel);
+ private static native String nativeAxisToString(int axis);
+ private static native int nativeAxisFromString(String label);
+
private MotionEvent() {
}
@@ -3051,8 +3055,8 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* @return The symbolic name of the specified axis.
*/
public static String axisToString(int axis) {
- String symbolicName = AXIS_SYMBOLIC_NAMES.get(axis);
- return symbolicName != null ? symbolicName : Integer.toString(axis);
+ String symbolicName = nativeAxisToString(axis);
+ return symbolicName != null ? LABEL_PREFIX + symbolicName : Integer.toString(axis);
}
/**
@@ -3064,17 +3068,13 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* @see KeyEvent#keyCodeToString(int)
*/
public static int axisFromString(String symbolicName) {
- if (symbolicName == null) {
- throw new IllegalArgumentException("symbolicName must not be null");
+ if (symbolicName.startsWith(LABEL_PREFIX)) {
+ symbolicName = symbolicName.substring(LABEL_PREFIX.length());
}
-
- final int count = AXIS_SYMBOLIC_NAMES.size();
- for (int i = 0; i < count; i++) {
- if (symbolicName.equals(AXIS_SYMBOLIC_NAMES.valueAt(i))) {
- return i;
- }
+ int axis = nativeAxisFromString(symbolicName);
+ if (axis >= 0) {
+ return axis;
}
-
try {
return Integer.parseInt(symbolicName, 10);
} catch (NumberFormatException ex) {
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 3dc09c4..0cfde94 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -20,6 +20,9 @@ import android.annotation.NonNull;
import android.graphics.Matrix;
import android.graphics.Outline;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* <p>A display list records a series of graphics related operations and can replay
* them later. Display lists are usually built by recording operations on a
@@ -176,11 +179,24 @@ public class RenderNode {
private boolean mValid;
private final long mNativeRenderNode;
+ // We need to keep a strong reference to all running animators to ensure that
+ // they can call removeAnimator when they have finished, as the native-side
+ // object can only hold a WeakReference<> to avoid leaking memory due to
+ // cyclic references.
+ private List<RenderNodeAnimator> mActiveAnimators;
+
private RenderNode(String name) {
mNativeRenderNode = nCreate(name);
}
/**
+ * @see RenderNode#adopt(long)
+ */
+ private RenderNode(long nativePtr) {
+ mNativeRenderNode = nativePtr;
+ }
+
+ /**
* Creates a new display list that can be used to record batches of
* drawing operations.
*
@@ -195,6 +211,17 @@ public class RenderNode {
}
/**
+ * Adopts an existing native render node.
+ *
+ * Note: This will *NOT* incRef() on the native object, however it will
+ * decRef() when it is destroyed. The caller should have already incRef'd it
+ */
+ public static RenderNode adopt(long nativePtr) {
+ return new RenderNode(nativePtr);
+ }
+
+
+ /**
* Starts recording a display list for the render node. All
* operations performed on the returned canvas are recorded and
* stored in this display list.
@@ -212,7 +239,7 @@ public class RenderNode {
* @see #isValid()
*/
public HardwareCanvas start(int width, int height) {
- HardwareCanvas canvas = GLES20RecordingCanvas.obtain();
+ HardwareCanvas canvas = GLES20RecordingCanvas.obtain(this);
canvas.setViewport(width, height);
// The dirty rect should always be null for a display list
canvas.onPreDraw(null);
@@ -822,6 +849,23 @@ public class RenderNode {
}
///////////////////////////////////////////////////////////////////////////
+ // Animations
+ ///////////////////////////////////////////////////////////////////////////
+
+ public void addAnimator(RenderNodeAnimator animator) {
+ if (mActiveAnimators == null) {
+ mActiveAnimators = new ArrayList<RenderNodeAnimator>();
+ }
+ mActiveAnimators.add(animator);
+ nAddAnimator(mNativeRenderNode, animator.getNativeAnimator());
+ }
+
+ public void removeAnimator(RenderNodeAnimator animator) {
+ nRemoveAnimator(mNativeRenderNode, animator.getNativeAnimator());
+ mActiveAnimators.remove(animator);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
// Native methods
///////////////////////////////////////////////////////////////////////////
@@ -896,6 +940,13 @@ public class RenderNode {
private static native void nOutput(long renderNode);
///////////////////////////////////////////////////////////////////////////
+ // Animations
+ ///////////////////////////////////////////////////////////////////////////
+
+ private static native void nAddAnimator(long renderNode, long animatorPtr);
+ private static native void nRemoveAnimator(long renderNode, long animatorPtr);
+
+ ///////////////////////////////////////////////////////////////////////////
// Finalization
///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
new file mode 100644
index 0000000..a675821
--- /dev/null
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -0,0 +1,155 @@
+/*
+ * 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;
+
+import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
+import android.graphics.Paint;
+import android.util.SparseIntArray;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * @hide
+ */
+public final class RenderNodeAnimator {
+
+ // Keep in sync with enum RenderProperty in Animator.h
+ public static final int TRANSLATION_X = 0;
+ public static final int TRANSLATION_Y = 1;
+ public static final int TRANSLATION_Z = 2;
+ public static final int SCALE_X = 3;
+ public static final int SCALE_Y = 4;
+ public static final int ROTATION = 5;
+ public static final int ROTATION_X = 6;
+ public static final int ROTATION_Y = 7;
+ public static final int X = 8;
+ public static final int Y = 9;
+ public static final int Z = 10;
+ public static final int ALPHA = 11;
+
+ // Keep in sync with enum PaintFields in Animator.h
+ public static final int PAINT_STROKE_WIDTH = 0;
+ public static final int PAINT_ALPHA = 1;
+
+ // ViewPropertyAnimator uses a mask for its values, we need to remap them
+ // to the enum values here. RenderPropertyAnimator can't use the mask values
+ // directly as internally it uses a lookup table so it needs the values to
+ // be sequential starting from 0
+ private static final SparseIntArray sViewPropertyAnimatorMap = new SparseIntArray(15) {{
+ put(ViewPropertyAnimator.TRANSLATION_X, TRANSLATION_X);
+ put(ViewPropertyAnimator.TRANSLATION_Y, TRANSLATION_Y);
+ put(ViewPropertyAnimator.TRANSLATION_Z, TRANSLATION_Z);
+ put(ViewPropertyAnimator.SCALE_X, SCALE_X);
+ put(ViewPropertyAnimator.SCALE_Y, SCALE_Y);
+ put(ViewPropertyAnimator.ROTATION, ROTATION);
+ put(ViewPropertyAnimator.ROTATION_X, ROTATION_X);
+ put(ViewPropertyAnimator.ROTATION_Y, ROTATION_Y);
+ put(ViewPropertyAnimator.X, X);
+ put(ViewPropertyAnimator.Y, Y);
+ put(ViewPropertyAnimator.Z, Z);
+ put(ViewPropertyAnimator.ALPHA, ALPHA);
+ }};
+
+ // Keep in sync DeltaValueType in Animator.h
+ public static final int DELTA_TYPE_ABSOLUTE = 0;
+ public static final int DELTA_TYPE_DELTA = 1;
+
+ private RenderNode mTarget;
+ private long 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);
+ }
+
+ public RenderNodeAnimator(CanvasProperty<Float> property, int deltaType, float deltaValue) {
+ mNativePtr = nCreateCanvasPropertyFloatAnimator(
+ new WeakReference<RenderNodeAnimator>(this),
+ property.getNativeContainer(), deltaType, deltaValue);
+ }
+
+ public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField,
+ int deltaType, float deltaValue) {
+ mNativePtr = nCreateCanvasPropertyPaintAnimator(
+ new WeakReference<RenderNodeAnimator>(this),
+ property.getNativeContainer(), paintField, deltaType, deltaValue);
+ }
+
+ public void start(View target) {
+ mTarget = target.mRenderNode;
+ mTarget.addAnimator(this);
+ // Kick off a frame to start the process
+ target.invalidateViewProperty(true, false);
+ }
+
+ public void start(Canvas canvas) {
+ if (!(canvas instanceof GLES20RecordingCanvas)) {
+ throw new IllegalArgumentException("Not a GLES20RecordingCanvas");
+ }
+ GLES20RecordingCanvas recordingCanvas = (GLES20RecordingCanvas) canvas;
+ mTarget = recordingCanvas.mNode;
+ mTarget.addAnimator(this);
+ }
+
+ public void cancel() {
+ mTarget.removeAnimator(this);
+ }
+
+ public void setDuration(int duration) {
+ nSetDuration(mNativePtr, duration);
+ }
+
+ long getNativeAnimator() {
+ return mNativePtr;
+ }
+
+ private void onFinished() {
+ mTarget.removeAnimator(this);
+ }
+
+ // Called by native
+ private static void callOnFinished(WeakReference<RenderNodeAnimator> weakThis) {
+ RenderNodeAnimator animator = weakThis.get();
+ if (animator != null) {
+ animator.onFinished();
+ }
+ }
+
+ @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,
+ long canvasProperty, int deltaValueType, float deltaValue);
+ 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/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 2d55a01..c15ce44 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -74,8 +74,10 @@ public class SurfaceControl {
IBinder displayToken, int orientation,
int l, int t, int r, int b,
int L, int T, int R, int B);
- private static native boolean nativeGetDisplayInfo(
- IBinder displayToken, SurfaceControl.PhysicalDisplayInfo outInfo);
+ private static native SurfaceControl.PhysicalDisplayInfo[] nativeGetDisplayConfigs(
+ IBinder displayToken);
+ private static native int nativeGetActiveConfig(IBinder displayToken);
+ private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
private static native void nativeBlankDisplay(IBinder displayToken);
private static native void nativeUnblankDisplay(IBinder displayToken);
@@ -499,14 +501,25 @@ public class SurfaceControl {
nativeBlankDisplay(displayToken);
}
- public static boolean getDisplayInfo(IBinder displayToken, SurfaceControl.PhysicalDisplayInfo outInfo) {
+ public static SurfaceControl.PhysicalDisplayInfo[] getDisplayConfigs(IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- if (outInfo == null) {
- throw new IllegalArgumentException("outInfo must not be null");
+ return nativeGetDisplayConfigs(displayToken);
+ }
+
+ public static int getActiveConfig(IBinder displayToken) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ return nativeGetActiveConfig(displayToken);
+ }
+
+ public static boolean setActiveConfig(IBinder displayToken, int id) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
}
- return nativeGetDisplayInfo(displayToken, outInfo);
+ return nativeSetActiveConfig(displayToken, id);
}
public static void setDisplayProjection(IBinder displayToken,
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 3cfe5e9..1765c43 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -23,7 +23,6 @@ import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
-import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
@@ -119,8 +118,6 @@ public class TextureView extends View {
private boolean mUpdateLayer;
private boolean mUpdateSurface;
- private SurfaceTexture.OnFrameAvailableListener mUpdateListener;
-
private Canvas mCanvas;
private int mSaveCount;
@@ -370,21 +367,7 @@ public class TextureView extends View {
mSurface.setDefaultBufferSize(getWidth(), getHeight());
nCreateNativeWindow(mSurface);
- mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
- @Override
- public void onFrameAvailable(SurfaceTexture surfaceTexture) {
- // Per SurfaceTexture's documentation, the callback may be invoked
- // from an arbitrary thread
- updateLayer();
-
- if (Looper.myLooper() == Looper.getMainLooper()) {
- invalidate();
- } else {
- postInvalidate();
- }
- }
- };
- mSurface.setOnFrameAvailableListener(mUpdateListener);
+ mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
if (mListener != null && !mUpdateSurface) {
mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
@@ -422,7 +405,7 @@ public class TextureView extends View {
// To cancel updates, the easiest thing to do is simply to remove the
// updates listener
if (visibility == VISIBLE) {
- mSurface.setOnFrameAvailableListener(mUpdateListener);
+ mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
updateLayerAndInvalidate();
} else {
mSurface.setOnFrameAvailableListener(null);
@@ -767,6 +750,15 @@ public class TextureView extends View {
mListener = listener;
}
+ private final SurfaceTexture.OnFrameAvailableListener mUpdateListener =
+ new SurfaceTexture.OnFrameAvailableListener() {
+ @Override
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ updateLayer();
+ invalidate();
+ }
+ };
+
/**
* This listener can be used to be notified when the surface texture
* associated with this texture view is available.
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index c7a6d41..0bf99d3 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -19,7 +19,6 @@ package android.view;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
-import android.os.SystemClock;
import android.os.Trace;
import android.view.Surface.OutOfResourcesException;
import android.view.View.AttachInfo;
@@ -50,17 +49,31 @@ import java.io.PrintWriter;
public class ThreadedRenderer extends HardwareRenderer {
private static final String LOGTAG = "ThreadedRenderer";
- private static final Rect NULL_RECT = new Rect(-1, -1, -1, -1);
+ private static final Rect NULL_RECT = new Rect();
+
+ private static final long NANOS_PER_MS = 1000000;
+
+ // Keep in sync with DrawFrameTask.h SYNC_* flags
+ // Nothing interesting to report
+ private static final int SYNC_OK = 0x0;
+ // Needs a ViewRoot invalidate
+ private static final int SYNC_INVALIDATE_REQUIRED = 0x1;
private int mWidth, mHeight;
private long mNativeProxy;
private boolean mInitialized = false;
private RenderNode mRootNode;
+ private Choreographer mChoreographer;
ThreadedRenderer(boolean translucent) {
- mNativeProxy = nCreateProxy(translucent);
- mRootNode = RenderNode.create("RootNode");
+ long rootNodePtr = nCreateRootRenderNode();
+ mRootNode = RenderNode.adopt(rootNodePtr);
mRootNode.setClipToBounds(false);
+ mNativeProxy = nCreateProxy(translucent, rootNodePtr);
+
+ // Setup timing
+ mChoreographer = Choreographer.getInstance();
+ nSetFrameInterval(mNativeProxy, mChoreographer.getFrameIntervalNanos());
}
@Override
@@ -157,16 +170,7 @@ public class ThreadedRenderer extends HardwareRenderer {
@Override
boolean loadSystemProperties() {
- return false;
- }
-
- /**
- * TODO: Remove
- * Temporary hack to allow RenderThreadTest prototype app to trigger
- * replaying a DisplayList after modifying the displaylist properties
- *
- * @hide */
- public void repeatLastDraw() {
+ return nLoadSystemProperties(mNativeProxy);
}
private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
@@ -193,7 +197,8 @@ public class ThreadedRenderer extends HardwareRenderer {
@Override
void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) {
attachInfo.mIgnoreDirtyState = true;
- attachInfo.mDrawingTime = SystemClock.uptimeMillis();
+ long frameTimeNanos = mChoreographer.getFrameTimeNanos();
+ attachInfo.mDrawingTime = frameTimeNanos / NANOS_PER_MS;
updateRootDisplayList(view, callbacks);
@@ -202,8 +207,11 @@ public class ThreadedRenderer extends HardwareRenderer {
if (dirty == null) {
dirty = NULL_RECT;
}
- nDrawDisplayList(mNativeProxy, mRootNode.getNativeDisplayList(),
+ int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
dirty.left, dirty.top, dirty.right, dirty.bottom);
+ if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
+ attachInfo.mViewRootImpl.invalidate();
+ }
}
@Override
@@ -293,16 +301,20 @@ public class ThreadedRenderer extends HardwareRenderer {
/** @hide */
public static native void postToRenderThread(Runnable runnable);
- private static native long nCreateProxy(boolean translucent);
+ private static native long nCreateRootRenderNode();
+ private static native long nCreateProxy(boolean translucent, long rootRenderNode);
private static native void nDeleteProxy(long nativeProxy);
+ private static native void nSetFrameInterval(long nativeProxy, long frameIntervalNanos);
+ private static native boolean nLoadSystemProperties(long nativeProxy);
+
private static native boolean nInitialize(long nativeProxy, Surface window);
private static native void nUpdateSurface(long nativeProxy, Surface window);
private static native void nPauseSurface(long nativeProxy, Surface window);
private static native void nSetup(long nativeProxy, int width, int height);
private static native void nSetDisplayListData(long nativeProxy, long displayList,
long newData);
- private static native void nDrawDisplayList(long nativeProxy, long displayList,
+ private static native int nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos,
int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
private static native void nRunWithGlContext(long nativeProxy, Runnable runnable);
private static native void nDestroyCanvasAndSurface(long nativeProxy);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 463a2f7..69840c4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6147,12 +6147,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// call into it as a fallback in case we're in a class that overrides it
// and has logic to perform.
if (fitSystemWindows(insets.getSystemWindowInsets())) {
- return insets.cloneWithSystemWindowInsetsConsumed();
+ return insets.consumeSystemWindowInsets();
}
} else {
// We were called from within a direct call to fitSystemWindows.
if (fitSystemWindowsInt(insets.getSystemWindowInsets())) {
- return insets.cloneWithSystemWindowInsetsConsumed();
+ return insets.consumeSystemWindowInsets();
}
}
return insets;
@@ -9958,7 +9958,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @attr ref android.R.styleable#View_transformPivotX
*/
public void setPivotX(float pivotX) {
- if (mRenderNode.isPivotExplicitlySet() || pivotX != getPivotX()) {
+ if (!mRenderNode.isPivotExplicitlySet() || pivotX != getPivotX()) {
invalidateViewProperty(true, false);
mRenderNode.setPivotX(pivotX);
invalidateViewProperty(false, true);
@@ -9999,7 +9999,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @attr ref android.R.styleable#View_transformPivotY
*/
public void setPivotY(float pivotY) {
- if (mRenderNode.isPivotExplicitlySet() || pivotY != getPivotY()) {
+ if (!mRenderNode.isPivotExplicitlySet() || pivotY != getPivotY()) {
invalidateViewProperty(true, false);
mRenderNode.setPivotY(pivotY);
invalidateViewProperty(false, true);
@@ -10633,8 +10633,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
} else {
// always copy the path since caller may reuse
if (mOutline == null) {
- mOutline = new Outline(outline);
+ mOutline = new Outline();
}
+ mOutline.set(outline);
}
mRenderNode.setOutline(mOutline);
}
@@ -17988,7 +17989,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* <p>If this property is set to true the view will be permitted to initiate nested
* scrolling operations with a compatible parent view in the current hierarchy. If this
- * view does not implement nested scrolling this will have no effect.</p>
+ * view does not implement nested scrolling this will have no effect. Disabling nested scrolling
+ * while a nested scroll is in progress has the effect of {@link #stopNestedScroll() stopping}
+ * the nested scroll.</p>
*
* @param enabled true to enable nested scrolling, false to disable
*
@@ -17998,6 +18001,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (enabled) {
mPrivateFlags3 |= PFLAG3_NESTED_SCROLLING_ENABLED;
} else {
+ stopNestedScroll();
mPrivateFlags3 &= ~PFLAG3_NESTED_SCROLLING_ENABLED;
}
}
@@ -18137,23 +18141,29 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
- int startX = 0;
- int startY = 0;
- if (offsetInWindow != null) {
- getLocationInWindow(offsetInWindow);
- startX = offsetInWindow[0];
- startY = offsetInWindow[1];
- }
+ if (dxConsumed != 0 || dyConsumed != 0 || dxUnconsumed != 0 || dyUnconsumed != 0) {
+ int startX = 0;
+ int startY = 0;
+ if (offsetInWindow != null) {
+ getLocationInWindow(offsetInWindow);
+ startX = offsetInWindow[0];
+ startY = offsetInWindow[1];
+ }
- mNestedScrollingParent.onNestedScroll(this, dxConsumed, dyConsumed,
- dxUnconsumed, dyUnconsumed);
+ mNestedScrollingParent.onNestedScroll(this, dxConsumed, dyConsumed,
+ dxUnconsumed, dyUnconsumed);
- if (offsetInWindow != null) {
- getLocationInWindow(offsetInWindow);
- offsetInWindow[0] -= startX;
- offsetInWindow[1] -= startY;
+ if (offsetInWindow != null) {
+ getLocationInWindow(offsetInWindow);
+ offsetInWindow[0] -= startX;
+ offsetInWindow[1] -= startY;
+ }
+ return true;
+ } else if (offsetInWindow != null) {
+ // No motion, no dispatch. Keep offsetInWindow up to date.
+ offsetInWindow[0] = 0;
+ offsetInWindow[1] = 0;
}
- return true;
}
return false;
}
@@ -18179,30 +18189,35 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
- int startX = 0;
- int startY = 0;
- if (offsetInWindow != null) {
- getLocationInWindow(offsetInWindow);
- startX = offsetInWindow[0];
- startY = offsetInWindow[1];
- }
-
- if (consumed == null) {
- if (mTempNestedScrollConsumed == null) {
- mTempNestedScrollConsumed = new int[2];
+ if (dx != 0 || dy != 0) {
+ int startX = 0;
+ int startY = 0;
+ if (offsetInWindow != null) {
+ getLocationInWindow(offsetInWindow);
+ startX = offsetInWindow[0];
+ startY = offsetInWindow[1];
}
- consumed = mTempNestedScrollConsumed;
- }
- consumed[0] = 0;
- consumed[1] = 0;
- mNestedScrollingParent.onNestedPreScroll(this, dx, dy, consumed);
- if (offsetInWindow != null) {
- getLocationInWindow(offsetInWindow);
- offsetInWindow[0] -= startX;
- offsetInWindow[1] -= startY;
+ if (consumed == null) {
+ if (mTempNestedScrollConsumed == null) {
+ mTempNestedScrollConsumed = new int[2];
+ }
+ consumed = mTempNestedScrollConsumed;
+ }
+ consumed[0] = 0;
+ consumed[1] = 0;
+ mNestedScrollingParent.onNestedPreScroll(this, dx, dy, consumed);
+
+ if (offsetInWindow != null) {
+ getLocationInWindow(offsetInWindow);
+ offsetInWindow[0] -= startX;
+ offsetInWindow[1] -= startY;
+ }
+ return consumed[0] != 0 || consumed[1] != 0;
+ } else if (offsetInWindow != null) {
+ offsetInWindow[0] = 0;
+ offsetInWindow[1] = 0;
}
- return consumed[0] != 0 || consumed[1] != 0;
}
return false;
}
@@ -18210,18 +18225,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Dispatch a fling to a nested scrolling parent.
*
- * <p>If a nested scrolling child view would normally fling but it is at the edge of its
- * own content it should use this method to delegate the fling to its nested scrolling parent.
- * The view implementation can use a {@link VelocityTracker} to obtain the velocity values
- * to pass.</p>
+ * <p>This method should be used to indicate that a nested scrolling child has detected
+ * suitable conditions for a fling. Generally this means that a touch scroll has ended with a
+ * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
+ * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
+ * along a scrollable axis.</p>
+ *
+ * <p>If a nested scrolling child view would normally fling but it is at the edge of
+ * its own content, it can use this method to delegate the fling to its nested scrolling
+ * parent instead. The parent may optionally consume the fling or observe a child fling.</p>
*
* @param velocityX Horizontal fling velocity in pixels per second
* @param velocityY Vertical fling velocity in pixels per second
- * @return true if the nested scrolling parent consumed the fling
+ * @param consumed true if the child consumed the fling, false otherwise
+ * @return true if the nested scrolling parent consumed or otherwise reacted to the fling
*/
- public boolean dispatchNestedFling(float velocityX, float velocityY) {
+ public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
- return mNestedScrollingParent.onNestedFling(this, velocityX, velocityY);
+ return mNestedScrollingParent.onNestedFling(this, velocityX, velocityY, consumed);
}
return false;
}
@@ -18863,6 +18884,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
};
/**
+ * A Property wrapper around the <code>z</code> functionality handled by the
+ * {@link View#setZ(float)} and {@link View#getZ()} methods.
+ */
+ public static final Property<View, Float> Z = new FloatProperty<View>("z") {
+ @Override
+ public void setValue(View object, float value) {
+ object.setZ(value);
+ }
+
+ @Override
+ public Float get(View object) {
+ return object.getZ();
+ }
+ };
+
+ /**
* A Property wrapper around the <code>rotation</code> functionality handled by the
* {@link View#setRotation(float)} and {@link View#getRotation()} methods.
*/
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 5112b9a..4e91ad4 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -234,6 +234,7 @@ public class ViewConfiguration {
private final int mOverscrollDistance;
private final int mOverflingDistance;
private final boolean mFadingMarqueeEnabled;
+ private final long mGlobalActionsKeyTimeout;
private boolean sHasPermanentMenuKey;
private boolean sHasPermanentMenuKeySet;
@@ -261,6 +262,7 @@ public class ViewConfiguration {
mOverscrollDistance = OVERSCROLL_DISTANCE;
mOverflingDistance = OVERFLING_DISTANCE;
mFadingMarqueeEnabled = true;
+ mGlobalActionsKeyTimeout = GLOBAL_ACTIONS_KEY_TIMEOUT;
}
/**
@@ -287,8 +289,6 @@ public class ViewConfiguration {
mEdgeSlop = (int) (sizeAndDensity * EDGE_SLOP + 0.5f);
mFadingEdgeLength = (int) (sizeAndDensity * FADING_EDGE_LENGTH + 0.5f);
- mMinimumFlingVelocity = (int) (density * MINIMUM_FLING_VELOCITY + 0.5f);
- mMaximumFlingVelocity = (int) (density * MAXIMUM_FLING_VELOCITY + 0.5f);
mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);
mDoubleTapSlop = (int) (sizeAndDensity * DOUBLE_TAP_SLOP + 0.5f);
mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f);
@@ -339,6 +339,13 @@ public class ViewConfiguration {
mPagingTouchSlop = mTouchSlop * 2;
mDoubleTapTouchSlop = mTouchSlop;
+
+ mMinimumFlingVelocity = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.config_viewMinFlingVelocity);
+ mMaximumFlingVelocity = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.config_viewMaxFlingVelocity);
+ mGlobalActionsKeyTimeout = res.getInteger(
+ com.android.internal.R.integer.config_globalActionsKeyTimeout);
}
/**
@@ -695,12 +702,26 @@ public class ViewConfiguration {
*
* @return how long a user needs to press the relevant key to bring up
* the global actions dialog.
+ * @deprecated This timeout should not be used by applications
*/
+ @Deprecated
public static long getGlobalActionKeyTimeout() {
return GLOBAL_ACTIONS_KEY_TIMEOUT;
}
/**
+ * The amount of time a user needs to press the relevant key to bring up
+ * the global actions dialog.
+ *
+ * @return how long a user needs to press the relevant key to bring up
+ * the global actions dialog.
+ * @hide
+ */
+ public long getDeviceGlobalActionKeyTimeout() {
+ return mGlobalActionsKeyTimeout;
+ }
+
+ /**
* The amount of friction applied to scrolls and flings.
*
* @return A scalar dimensionless value representing the coefficient of
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 8865ab4..43bc0b6 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2342,7 +2342,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
if (disallowIntercept) {
mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
- stopNestedScroll();
} else {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
@@ -5914,7 +5913,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @inheritDoc
*/
@Override
- public boolean onNestedFling(View target, float velocityX, float velocityY) {
+ public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
return false;
}
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 3cd6449..588b9cd 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -512,14 +512,21 @@ public interface ViewParent {
/**
* Request a fling from a nested scroll.
*
+ * <p>This method signifies that a nested scrolling child has detected suitable conditions
+ * for a fling. Generally this means that a touch scroll has ended with a
+ * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
+ * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
+ * along a scrollable axis.</p>
+ *
* <p>If a nested scrolling child view would normally fling but it is at the edge of
- * its own content, it can delegate the fling to its nested scrolling parent instead.
- * This method allows the parent to optionally consume the fling.</p>
+ * its own content, it can use this method to delegate the fling to its nested scrolling
+ * parent instead. The parent may optionally consume the fling or observe a child fling.</p>
*
* @param target View that initiated the nested scroll
* @param velocityX Horizontal velocity in pixels per second.
* @param velocityY Vertical velocity in pixels per second
- * @return true if this parent consumed the fling
+ * @param consumed true if the child consumed the fling, false otherwise
+ * @return true if this parent consumed or otherwise reacted to the fling
*/
- public boolean onNestedFling(View target, float velocityX, float velocityY);
+ public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
}
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index b1aa7b2..11d2622 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -133,19 +133,19 @@ public class ViewPropertyAnimator {
* Constants used to associate a property being requested and the mechanism used to set
* the property (this class calls directly into View to set the properties in question).
*/
- private static final int NONE = 0x0000;
- private static final int TRANSLATION_X = 0x0001;
- private static final int TRANSLATION_Y = 0x0002;
- private static final int TRANSLATION_Z = 0x0004;
- private static final int SCALE_X = 0x0008;
- private static final int SCALE_Y = 0x0010;
- private static final int ROTATION = 0x0020;
- private static final int ROTATION_X = 0x0040;
- private static final int ROTATION_Y = 0x0080;
- private static final int X = 0x0100;
- private static final int Y = 0x0200;
- private static final int Z = 0x0400;
- private static final int ALPHA = 0x0800;
+ static final int NONE = 0x0000;
+ static final int TRANSLATION_X = 0x0001;
+ static final int TRANSLATION_Y = 0x0002;
+ static final int TRANSLATION_Z = 0x0004;
+ static final int SCALE_X = 0x0008;
+ static final int SCALE_Y = 0x0010;
+ static final int ROTATION = 0x0020;
+ static final int ROTATION_X = 0x0040;
+ static final int ROTATION_Y = 0x0080;
+ static final int X = 0x0100;
+ static final int Y = 0x0200;
+ static final int Z = 0x0400;
+ static final int ALPHA = 0x0800;
private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | TRANSLATION_Z |
SCALE_X | SCALE_Y | ROTATION | ROTATION_X | ROTATION_Y | X | Y | Z;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 14e422c..db87394 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -234,6 +234,7 @@ public final class ViewRootImpl implements ViewParent,
InputStage mFirstInputStage;
InputStage mFirstPostImeInputStage;
+ InputStage mSyntheticInputStage;
boolean mWindowAttributesChanged = false;
int mWindowAttributesChangesFlag = 0;
@@ -599,8 +600,8 @@ public final class ViewRootImpl implements ViewParent,
// Set up the input pipeline.
CharSequence counterSuffix = attrs.getTitle();
- InputStage syntheticInputStage = new SyntheticInputStage();
- InputStage viewPostImeStage = new ViewPostImeInputStage(syntheticInputStage);
+ mSyntheticInputStage = new SyntheticInputStage();
+ InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
@@ -2587,10 +2588,6 @@ public final class ViewRootImpl implements ViewParent,
* @param canvas The canvas on which to draw.
*/
private void drawAccessibilityFocusedDrawableIfNeeded(Canvas canvas) {
- if (!mAttachInfo.mHasWindowFocus) {
- return;
- }
-
final AccessibilityManager manager = AccessibilityManager.getInstance(mView.mContext);
if (!manager.isEnabled() || !manager.isTouchExplorationEnabled()) {
return;
@@ -3007,6 +3004,7 @@ public final class ViewRootImpl implements ViewParent,
private final static int MSG_INVALIDATE_WORLD = 23;
private final static int MSG_WINDOW_MOVED = 24;
private final static int MSG_FLUSH_LAYER_UPDATES = 25;
+ private final static int MSG_SYNTHESIZE_INPUT_EVENT = 26;
final class ViewRootHandler extends Handler {
@Override
@@ -3056,6 +3054,8 @@ public final class ViewRootImpl implements ViewParent,
return "MSG_WINDOW_MOVED";
case MSG_FLUSH_LAYER_UPDATES:
return "MSG_FLUSH_LAYER_UPDATES";
+ case MSG_SYNTHESIZE_INPUT_EVENT:
+ return "MSG_SYNTHESIZE_INPUT_EVENT";
}
return super.getMessageName(message);
}
@@ -3218,6 +3218,10 @@ public final class ViewRootImpl implements ViewParent,
enqueueInputEvent(event, receiver, 0, true);
args.recycle();
} break;
+ case MSG_SYNTHESIZE_INPUT_EVENT: {
+ InputEvent event = (InputEvent)msg.obj;
+ enqueueInputEvent(event, null, QueuedInputEvent.FLAG_UNHANDLED, true);
+ } break;
case MSG_DISPATCH_KEY_FROM_IME: {
if (LOCAL_LOGV) Log.v(
TAG, "Dispatching key "
@@ -3227,7 +3231,8 @@ public final class ViewRootImpl implements ViewParent,
// The IME is trying to say this event is from the
// system! Bad bad bad!
//noinspection UnusedAssignment
- event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
+ event = KeyEvent.changeFlags(event, event.getFlags() &
+ ~KeyEvent.FLAG_FROM_SYSTEM);
}
enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true);
} break;
@@ -4023,6 +4028,7 @@ public final class ViewRootImpl implements ViewParent,
private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler();
private final SyntheticTouchNavigationHandler mTouchNavigation =
new SyntheticTouchNavigationHandler();
+ private final SyntheticKeyboardHandler mKeyboard = new SyntheticKeyboardHandler();
public SyntheticInputStage() {
super(null);
@@ -4045,7 +4051,11 @@ public final class ViewRootImpl implements ViewParent,
mTouchNavigation.process(event);
return FINISH_HANDLED;
}
+ } else if ((q.mFlags & QueuedInputEvent.FLAG_UNHANDLED) != 0) {
+ mKeyboard.process((KeyEvent)q.mEvent);
+ return FINISH_HANDLED;
}
+
return FORWARD;
}
@@ -4882,6 +4892,33 @@ public final class ViewRootImpl implements ViewParent,
};
}
+ final class SyntheticKeyboardHandler {
+ public void process(KeyEvent event) {
+ if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) {
+ return;
+ }
+
+ final KeyCharacterMap kcm = event.getKeyCharacterMap();
+ final int keyCode = event.getKeyCode();
+ final int metaState = event.getMetaState();
+
+ // Check for fallback actions specified by the key character map.
+ KeyCharacterMap.FallbackAction fallbackAction =
+ kcm.getFallbackAction(keyCode, metaState);
+ if (fallbackAction != null) {
+ final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
+ KeyEvent fallbackEvent = KeyEvent.obtain(
+ event.getDownTime(), event.getEventTime(),
+ event.getAction(), fallbackAction.keyCode,
+ event.getRepeatCount(), fallbackAction.metaState,
+ event.getDeviceId(), event.getScanCode(),
+ flags, event.getSource(), null);
+ fallbackAction.recycle();
+ enqueueInputEvent(fallbackEvent);
+ }
+ }
+ }
+
/**
* Returns true if the key is used for keyboard navigation.
* @param keyEvent The key event.
@@ -5461,6 +5498,7 @@ public final class ViewRootImpl implements ViewParent,
public static final int FLAG_FINISHED = 1 << 2;
public static final int FLAG_FINISHED_HANDLED = 1 << 3;
public static final int FLAG_RESYNTHESIZED = 1 << 4;
+ public static final int FLAG_UNHANDLED = 1 << 5;
public QueuedInputEvent mNext;
@@ -5475,6 +5513,14 @@ public final class ViewRootImpl implements ViewParent,
return mEvent instanceof MotionEvent
&& mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER);
}
+
+ public boolean shouldSendToSynthesizer() {
+ if ((mFlags & FLAG_UNHANDLED) != 0) {
+ return true;
+ }
+
+ return false;
+ }
}
private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,
@@ -5578,7 +5624,13 @@ public final class ViewRootImpl implements ViewParent,
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
- InputStage stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
+ InputStage stage;
+ if (q.shouldSendToSynthesizer()) {
+ stage = mSyntheticInputStage;
+ } else {
+ stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
+ }
+
if (stage != null) {
stage.deliver(q);
} else {
@@ -5810,43 +5862,29 @@ public final class ViewRootImpl implements ViewParent,
mHandler.sendMessage(msg);
}
- public void dispatchKeyFromIme(KeyEvent event) {
- Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_IME, event);
+ public void synthesizeInputEvent(InputEvent event) {
+ Message msg = mHandler.obtainMessage(MSG_SYNTHESIZE_INPUT_EVENT, event);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
- public void dispatchUnhandledKey(KeyEvent event) {
- if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
- // Some fallback keys are decided by the ViewRoot as they might have special
- // properties (e.g. are locale aware). These take precedence over fallbacks defined by
- // the kcm.
- final KeyCharacterMap kcm = event.getKeyCharacterMap();
- final int keyCode = event.getKeyCode();
- final int metaState = event.getMetaState();
-
- // Check for fallback actions specified by the key character map.
- KeyCharacterMap.FallbackAction fallbackAction =
- kcm.getFallbackAction(keyCode, metaState);
- if (fallbackAction != null) {
- final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
- KeyEvent fallbackEvent = KeyEvent.obtain(
- event.getDownTime(), event.getEventTime(),
- event.getAction(), fallbackAction.keyCode,
- event.getRepeatCount(), fallbackAction.metaState,
- event.getDeviceId(), event.getScanCode(),
- flags, event.getSource(), null);
- fallbackAction.recycle();
- dispatchInputEvent(fallbackEvent);
- }
- }
+ public void dispatchKeyFromIme(KeyEvent event) {
+ Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_IME, event);
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
}
+ /**
+ * Reinject unhandled {@link InputEvent}s in order to synthesize fallbacks events.
+ *
+ * Note that it is the responsibility of the caller of this API to recycle the InputEvent it
+ * passes in.
+ */
public void dispatchUnhandledInputEvent(InputEvent event) {
- if (event instanceof KeyEvent) {
- dispatchUnhandledKey((KeyEvent) event);
- return;
+ if (event instanceof MotionEvent) {
+ event = MotionEvent.obtain((MotionEvent) event);
}
+ synthesizeInputEvent(event);
}
public void dispatchAppVisibility(boolean visible) {
@@ -6131,7 +6169,7 @@ public final class ViewRootImpl implements ViewParent,
}
@Override
- public boolean onNestedFling(View target, float velocityX, float velocityY) {
+ public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
return false;
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 9c44bd1..375f5e3 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -147,6 +147,7 @@ public abstract class Window {
private TypedArray mWindowStyle;
private Callback mCallback;
+ private OnWindowDismissedCallback mOnWindowDismissedCallback;
private WindowManager mWindowManager;
private IBinder mAppToken;
private String mAppName;
@@ -405,7 +406,10 @@ public abstract class Window {
* @param mode The mode that was just finished.
*/
public void onActionModeFinished(ActionMode mode);
+ }
+ /** @hide */
+ public interface OnWindowDismissedCallback {
/**
* Called when a window is dismissed. This informs the callback that the
* window is gone, and it should finish itself.
@@ -586,6 +590,18 @@ public abstract class Window {
return mCallback;
}
+ /** @hide */
+ public final void setOnWindowDismissedCallback(OnWindowDismissedCallback dcb) {
+ mOnWindowDismissedCallback = dcb;
+ }
+
+ /** @hide */
+ public final void dispatchOnWindowDismissed() {
+ if (mOnWindowDismissedCallback != null) {
+ mOnWindowDismissedCallback.onWindowDismissed();
+ }
+ }
+
/**
* Take ownership of this window's surface. The window's view hierarchy
* will no longer draw into the surface, though it will otherwise continue
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 2160efe..294f472 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -29,7 +29,7 @@ import android.graphics.Rect;
* @see View.OnApplyWindowInsetsListener
* @see View#onApplyWindowInsets(WindowInsets)
*/
-public class WindowInsets {
+public final class WindowInsets {
private Rect mSystemWindowInsets;
private Rect mWindowDecorInsets;
private Rect mTempRect;
@@ -151,6 +151,7 @@ public class WindowInsets {
* This can include action bars, title bars, toolbars, etc.</p>
*
* @return The left window decor inset
+ * @hide pending API
*/
public int getWindowDecorInsetLeft() {
return mWindowDecorInsets.left;
@@ -164,6 +165,7 @@ public class WindowInsets {
* This can include action bars, title bars, toolbars, etc.</p>
*
* @return The top window decor inset
+ * @hide pending API
*/
public int getWindowDecorInsetTop() {
return mWindowDecorInsets.top;
@@ -177,6 +179,7 @@ public class WindowInsets {
* This can include action bars, title bars, toolbars, etc.</p>
*
* @return The right window decor inset
+ * @hide pending API
*/
public int getWindowDecorInsetRight() {
return mWindowDecorInsets.right;
@@ -190,6 +193,7 @@ public class WindowInsets {
* This can include action bars, title bars, toolbars, etc.</p>
*
* @return The bottom window decor inset
+ * @hide pending API
*/
public int getWindowDecorInsetBottom() {
return mWindowDecorInsets.bottom;
@@ -217,6 +221,7 @@ public class WindowInsets {
* This can include action bars, title bars, toolbars, etc.</p>
*
* @return true if any of the window decor inset values are nonzero
+ * @hide pending API
*/
public boolean hasWindowDecorInsets() {
return mWindowDecorInsets.left != 0 || mWindowDecorInsets.top != 0 ||
@@ -246,13 +251,28 @@ public class WindowInsets {
return mIsRound;
}
- public WindowInsets cloneWithSystemWindowInsetsConsumed() {
+ /**
+ * Returns a copy of this WindowInsets with the system window insets fully consumed.
+ *
+ * @return A modified copy of this WindowInsets
+ */
+ public WindowInsets consumeSystemWindowInsets() {
final WindowInsets result = new WindowInsets(this);
result.mSystemWindowInsets = new Rect(0, 0, 0, 0);
return result;
}
- public WindowInsets cloneWithSystemWindowInsetsConsumed(boolean left, boolean top,
+ /**
+ * Returns a copy of this WindowInsets with selected system window insets fully consumed.
+ *
+ * @param left true to consume the left system window inset
+ * @param top true to consume the top system window inset
+ * @param right true to consume the right system window inset
+ * @param bottom true to consume the bottom system window inset
+ * @return A modified copy of this WindowInsets
+ * @hide pending API
+ */
+ public WindowInsets consumeSystemWindowInsets(boolean left, boolean top,
boolean right, boolean bottom) {
if (left || top || right || bottom) {
final WindowInsets result = new WindowInsets(this);
@@ -265,19 +285,36 @@ public class WindowInsets {
return this;
}
- public WindowInsets cloneWithSystemWindowInsets(int left, int top, int right, int bottom) {
+ /**
+ * Returns a copy of this WindowInsets with selected system window insets replaced
+ * with new values.
+ *
+ * @param left New left inset in pixels
+ * @param top New top inset in pixels
+ * @param right New right inset in pixels
+ * @param bottom New bottom inset in pixels
+ * @return A modified copy of this WindowInsets
+ */
+ public WindowInsets replaceSystemWindowInsets(int left, int top,
+ int right, int bottom) {
final WindowInsets result = new WindowInsets(this);
result.mSystemWindowInsets = new Rect(left, top, right, bottom);
return result;
}
- public WindowInsets cloneWithWindowDecorInsetsConsumed() {
+ /**
+ * @hide
+ */
+ public WindowInsets consumeWindowDecorInsets() {
final WindowInsets result = new WindowInsets(this);
result.mWindowDecorInsets.set(0, 0, 0, 0);
return result;
}
- public WindowInsets cloneWithWindowDecorInsetsConsumed(boolean left, boolean top,
+ /**
+ * @hide
+ */
+ public WindowInsets consumeWindowDecorInsets(boolean left, boolean top,
boolean right, boolean bottom) {
if (left || top || right || bottom) {
final WindowInsets result = new WindowInsets(this);
@@ -290,7 +327,10 @@ public class WindowInsets {
return this;
}
- public WindowInsets cloneWithWindowDecorInsets(int left, int top, int right, int bottom) {
+ /**
+ * @hide
+ */
+ public WindowInsets replaceWindowDecorInsets(int left, int top, int right, int bottom) {
final WindowInsets result = new WindowInsets(this);
result.mWindowDecorInsets = new Rect(left, top, right, bottom);
return result;
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index c9be54d..4fde1e4 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -76,14 +76,7 @@ import java.lang.annotation.RetentionPolicy;
public interface WindowManagerPolicy {
// Policy flags. These flags are also defined in frameworks/base/include/ui/Input.h.
public final static int FLAG_WAKE = 0x00000001;
- public final static int FLAG_WAKE_DROPPED = 0x00000002;
- public final static int FLAG_SHIFT = 0x00000004;
- public final static int FLAG_CAPS_LOCK = 0x00000008;
- public final static int FLAG_ALT = 0x00000010;
- public final static int FLAG_ALT_GR = 0x00000020;
- public final static int FLAG_MENU = 0x00000040;
- public final static int FLAG_LAUNCHER = 0x00000080;
- public final static int FLAG_VIRTUAL = 0x00000100;
+ public final static int FLAG_VIRTUAL = 0x00000002;
public final static int FLAG_INJECTED = 0x01000000;
public final static int FLAG_TRUSTED = 0x02000000;
@@ -450,8 +443,6 @@ public interface WindowManagerPolicy {
public final int OFF_BECAUSE_OF_USER = 2;
/** Screen turned off because of timeout */
public final int OFF_BECAUSE_OF_TIMEOUT = 3;
- /** Screen turned off because of proximity sensor */
- public final int OFF_BECAUSE_OF_PROX_SENSOR = 4;
/** @hide */
@IntDef({USER_ROTATION_FREE, USER_ROTATION_LOCKED})
@@ -907,23 +898,23 @@ public interface WindowManagerPolicy {
public int focusChangedLw(WindowState lastFocus, WindowState newFocus);
/**
- * Called after the screen turns off.
+ * Called when the device is going to sleep.
*
* @param why {@link #OFF_BECAUSE_OF_USER} or
* {@link #OFF_BECAUSE_OF_TIMEOUT}.
*/
- public void screenTurnedOff(int why);
+ public void goingToSleep(int why);
public interface ScreenOnListener {
void onScreenOn();
}
/**
- * Called when the power manager would like to turn the screen on.
+ * Called when the device is waking up.
* Must call back on the listener to tell it when the higher-level system
* is ready for the screen to go on (i.e. the lock screen is shown).
*/
- public void screenTurningOn(ScreenOnListener screenOnListener);
+ public void wakingUp(ScreenOnListener screenOnListener);
/**
* Return whether the screen is about to turn on or is currently on.
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index f8160c8..bc2d7ec 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -259,7 +259,7 @@ public final class InputMethodInfo implements Parcelable {
public InputMethodInfo(String packageName, String className,
CharSequence label, String settingsActivity) {
this(buildDummyResolveInfo(packageName, className, label), false, settingsActivity, null,
- 0, false);
+ 0, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */);
}
/**
@@ -269,6 +269,17 @@ public final class InputMethodInfo implements Parcelable {
public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
boolean forceDefault) {
+ this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId,
+ forceDefault, true /* supportsSwitchingToNextInputMethod */);
+ }
+
+ /**
+ * Temporary API for creating a built-in input method for test.
+ * @hide
+ */
+ public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
+ String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
+ boolean forceDefault, boolean supportsSwitchingToNextInputMethod) {
final ServiceInfo si = ri.serviceInfo;
mService = ri;
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -277,7 +288,7 @@ public final class InputMethodInfo implements Parcelable {
mIsAuxIme = isAuxIme;
mSubtypes = new InputMethodSubtypeArray(subtypes);
mForceDefault = forceDefault;
- mSupportsSwitchingToNextInputMethod = true;
+ mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
}
private static ResolveInfo buildDummyResolveInfo(String packageName, String className,
diff --git a/core/java/android/webkit/ClientCertRequest.java b/core/java/android/webkit/ClientCertRequest.java
index 8951786..588b868 100644
--- a/core/java/android/webkit/ClientCertRequest.java
+++ b/core/java/android/webkit/ClientCertRequest.java
@@ -36,8 +36,6 @@ import java.security.cert.X509Certificate;
* host/port pair. The user can clear the cached data using
* {@link WebView#clearClientCertPreferences}.
*
- * TODO(sgurun) unhide
- * @hide
*/
public interface ClientCertRequest {
/**
diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java
index 2f8850b..3e33498 100644
--- a/core/java/android/webkit/PermissionRequest.java
+++ b/core/java/android/webkit/PermissionRequest.java
@@ -19,14 +19,11 @@ package android.webkit;
import android.net.Uri;
/**
- * This class wraps a permission request, and is used to request permission for
- * the web content to access the resources.
+ * This interface defines a permission request and is used when web content
+ * requests access to protected resources.
*
- * Either {@link #grant(long) grant()} or {@link #deny()} must be called to response the
- * request, otherwise, {@link WebChromeClient#onPermissionRequest(PermissionRequest)} will
- * not be invoked again if there is other permission request in this WebView.
- *
- * @hide
+ * Either {@link #grant(long) grant()} or {@link #deny()} must be called in UI
+ * thread to respond to the request.
*/
public interface PermissionRequest {
/**
@@ -62,8 +59,6 @@ public interface PermissionRequest {
* must be equals or a subset of granted resources.
* This parameter is designed to avoid granting permission by accident
* especially when new resources are requested by web content.
- * Calling grant(getResources()) has security issue, the new permission
- * will be granted without being noticed.
*/
public void grant(long resources);
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 60cba86..d630a9a 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -304,7 +304,6 @@ public class WebChromeClient {
* If this method isn't overridden, the permission is denied.
*
* @param request the PermissionRequest from current web content.
- * @hide
*/
public void onPermissionRequest(PermissionRequest request) {
request.deny();
@@ -314,8 +313,7 @@ public class WebChromeClient {
* Notify the host application that the given permission request
* has been canceled. Any related UI should therefore be hidden.
*
- * @param request the PermissionRequest need be canceled.
- * @hide
+ * @param request the PermissionRequest that needs be canceled.
*/
public void onPermissionRequestCanceled(PermissionRequest request) {}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 98ef66e..7c32c5b 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -171,6 +171,38 @@ public abstract class WebSettings {
}
/**
+ * Used with {@link #setMixedContentMode}
+ *
+ * In this mode, the WebView will allow a secure origin to load content from any other origin,
+ * even if that origin is insecure. This is the least secure mode of operation for the WebView,
+ * and where possible apps should not set this mode.
+ */
+ public static final int MIXED_CONTENT_ALWAYS_ALLOW = 0;
+
+ /**
+ * Used with {@link #setMixedContentMode}
+ *
+ * In this mode, the WebView will not allow a secure origin to load content from an insecure
+ * origin. This is the preferred and most secure mode of operation for the WebView and apps are
+ * strongly advised to use this mode.
+ */
+ public static final int MIXED_CONTENT_NEVER_ALLOW = 1;
+
+ /**
+ * Used with {@link #setMixedContentMode}
+ *
+ * In this mode, the WebView will attempt to be compatible with the approach of a modern web
+ * browser with regard to mixed content. Some insecure content may be allowed to be loaded by
+ * a secure origin and other types of content will be blocked. The types of content are allowed
+ * or blocked may change release to release and are not explicitly defined.
+ *
+ * This mode is intended to be used by apps that are not in control of the content that they
+ * render but desire to operate in a reasonably secure environment. For highest security, apps
+ * are recommended to use {@link #MIXED_CONTENT_NEVER_ALLOW}.
+ */
+ public static final int MIXED_CONTENT_COMPATIBILITY_MODE = 2;
+
+ /**
* Hidden constructor to prevent clients from creating a new settings
* instance or deriving the class.
*
@@ -1403,4 +1435,29 @@ public abstract class WebSettings {
public int getCacheMode() {
throw new MustOverrideException();
}
+
+ /**
+ * Configures the WebView's behavior when a secure origin attempts to load a resource from an
+ * insecure origin.
+ *
+ * By default, apps that target {@link android.os.Build.VERSION_CODES#KITKAT} or below default
+ * to {@link #MIXED_CONTENT_ALWAYS_ALLOW}. Apps targeting
+ * {@link android.os.Build.VERSION_CODES#L} default to {@link #MIXED_CONTENT_NEVER_ALLOW}.
+ *
+ * The preferred and most secure mode of operation for the WebView is
+ * {@link #MIXED_CONTENT_NEVER_ALLOW} and use of {@link #MIXED_CONTENT_ALWAYS_ALLOW} is
+ * strongly discouraged.
+ *
+ * @param mode The mixed content mode to use. One of {@link #MIXED_CONTENT_NEVER_ALLOW},
+ * {@link #MIXED_CONTENT_NEVER_ALLOW} or {@link #MIXED_CONTENT_COMPATIBILITY_MODE}.
+ */
+ public abstract void setMixedContentMode(int mode);
+
+ /**
+ * Gets the current behavior of the WebView with regard to loading insecure content from a
+ * secure origin.
+ * @return The current setting, one of {@link #MIXED_CONTENT_NEVER_ALLOW},
+ * {@link #MIXED_CONTENT_NEVER_ALLOW} or {@link #MIXED_CONTENT_COMPATIBILITY_MODE}.
+ */
+ public abstract int getMixedContentMode();
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index c914e52..91ca7b4 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -33,6 +33,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.StrictMode;
import android.print.PrintDocumentAdapter;
+import android.security.KeyChain;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
@@ -250,6 +251,15 @@ public class WebView extends AbsoluteLayout
implements ViewTreeObserver.OnGlobalFocusChangeListener,
ViewGroup.OnHierarchyChangeListener, ViewDebug.HierarchyHandler {
+ /**
+ * Broadcast Action: Indicates the data reduction proxy setting changed.
+ * Sent by the settings app when user changes the data reduction proxy value. This intent will
+ * always stay as a hidden API.
+ * @hide
+ */
+ public static final String DATA_REDUCTION_PROXY_SETTING_CHANGED =
+ "android.webkit.DATA_REDUCTION_PROXY_SETTING_CHANGED";
+
private static final String LOGTAG = "WebView";
// Throwing an exception for incorrect thread usage if the
@@ -701,7 +711,7 @@ public class WebView extends AbsoluteLayout
*/
@Deprecated
public static void enablePlatformNotifications() {
- getFactory().getStatics().setPlatformNotificationsEnabled(true);
+ // noop
}
/**
@@ -713,7 +723,7 @@ public class WebView extends AbsoluteLayout
*/
@Deprecated
public static void disablePlatformNotifications() {
- getFactory().getStatics().setPlatformNotificationsEnabled(false);
+ // noop
}
/**
@@ -1479,18 +1489,16 @@ public class WebView extends AbsoluteLayout
* Clears the client certificate preferences table stored in response
* to proceeding/cancelling client cert requests. Note that webview
* automatically clears these preferences when it receives a
- * {@link KeyChain.ACTION_STORAGE_CHANGED}
- *
- * @param resultCallback A callback to be invoked when client certs are cleared.
- * The embedder can pass null if not interested in the callback.
+ * {@link KeyChain#ACTION_STORAGE_CHANGED} intent. The client certificate
+ * preferences are global for all Webviews.
*
- * TODO(sgurun) unhide
- * @hide
+ * @param onCleared A runnable to be invoked when client certs are cleared.
+ * The embedder can pass null if not interested in the
+ * callback. The runnable will be called in UI thread.
*/
- public void clearClientCertPreferences(ValueCallback<Void> resultCallback) {
- checkThread();
+ public static void clearClientCertPreferences(Runnable onCleared) {
if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearClientCertPreferences");
- mProvider.clearClientCertPreferences(resultCallback);
+ getFactory().getStatics().clearClientCertPreferences(onCleared);
}
/**
@@ -1610,6 +1618,8 @@ public class WebView extends AbsoluteLayout
* @return the address, or if no address is found, null
*/
public static String findAddress(String addr) {
+ // TODO: Rewrite this in Java so it is not needed to start up chromium
+ // Could also be deprecated
return getFactory().getStatics().findAddress(addr);
}
@@ -1672,13 +1682,15 @@ public class WebView extends AbsoluteLayout
/**
* Preauthorize the given origin to access resources.
- * This authorization only valid for this WebView instance life cycle and
+ * The authorization only valid for this WebView instance's life cycle and
* will not retained.
*
+ * In the case that an origin has had resources preauthorized, calls to
+ * {@link WebChromeClient#onPermissionRequest(PermissionRequest)} will not be
+ * made for those resources from that origin.
+ *
* @param origin the origin authorized to access resources
* @param resources the resource authorized to be accessed by origin.
- *
- * @hide
*/
public void preauthorizePermission(Uri origin, long resources) {
checkThread();
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 688c251..62b80c4 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -19,6 +19,7 @@ package android.webkit;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.os.Message;
+import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.ViewRootImpl;
@@ -223,8 +224,6 @@ public class WebViewClient {
* @param view The WebView that is initiating the callback
* @param request An instance of a {@link ClientCertRequest}
*
- * TODO(sgurun) unhide
- * @hide
*/
public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
request.cancel();
@@ -272,11 +271,43 @@ public class WebViewClient {
*
* @param view The WebView that is initiating the callback.
* @param event The key event.
+ * @deprecated This method is subsumed by the more generic onUnhandledInputEvent.
*/
+ @Deprecated
public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
+ onUnhandledInputEventInternal(view, event);
+ }
+
+ /**
+ * Notify the host application that a input event was not handled by the WebView.
+ * Except system keys, WebView always consumes input events in the normal flow
+ * or if shouldOverrideKeyEvent returns true. This is called asynchronously
+ * from where the event is dispatched. It gives the host application a chance
+ * to handle the unhandled input events.
+ *
+ * Note that if the event is a {@link android.view.MotionEvent}, then it's lifetime is only
+ * that of the function call. If the WebViewClient wishes to use the event beyond that, then it
+ * <i>must</i> create a copy of the event.
+ *
+ * It is the responsibility of overriders of this method to call
+ * {@link #onUnhandledKeyEvent(WebView, KeyEvent)}
+ * when appropriate if they wish to continue receiving events through it.
+ *
+ * @param view The WebView that is initiating the callback.
+ * @param event The input event.
+ */
+ public void onUnhandledInputEvent(WebView view, InputEvent event) {
+ if (event instanceof KeyEvent) {
+ onUnhandledKeyEvent(view, (KeyEvent) event);
+ return;
+ }
+ onUnhandledInputEventInternal(view, event);
+ }
+
+ private void onUnhandledInputEventInternal(WebView view, InputEvent event) {
ViewRootImpl root = view.getViewRootImpl();
if (root != null) {
- root.dispatchUnhandledKey(event);
+ root.dispatchUnhandledInputEvent(event);
}
}
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index e391aaf..945e0e3 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -59,6 +59,13 @@ public interface WebViewFactoryProvider {
* {@link android.webkit.WebView#setWebContentsDebuggingEnabled(boolean) }
*/
void setWebContentsDebuggingEnabled(boolean enable);
+
+ /**
+ * Implements the API method:
+ * {@link android.webkit.WebView#clearClientCertPreferences(Runnable) }
+ */
+ void clearClientCertPreferences(Runnable onCleared);
+
}
Statics getStatics();
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index efa5497..5081ff5 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -198,8 +198,6 @@ public interface WebViewProvider {
public void clearSslPreferences();
- public void clearClientCertPreferences(ValueCallback<Void> resultCallback);
-
public WebBackForwardList copyBackForwardList();
public void setFindListener(WebView.FindListener listener);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index becda67..f4cd5fc 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -110,6 +110,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
* @see #setTranscriptMode(int)
*/
public static final int TRANSCRIPT_MODE_DISABLED = 0;
+
/**
* The list will automatically scroll to the bottom when a data set change
* notification is received and only if the last item is already visible
@@ -118,6 +119,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
* @see #setTranscriptMode(int)
*/
public static final int TRANSCRIPT_MODE_NORMAL = 1;
+
/**
* The list will automatically scroll to the bottom, no matter what items
* are currently visible.
@@ -609,6 +611,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
final boolean[] mIsScrap = new boolean[1];
+ private final int[] mScrollOffset = new int[2];
+ private final int[] mScrollConsumed = new int[2];
+
// True when the popup should be hidden because of a call to
// dispatchDisplayHint()
private boolean mPopupHidden;
@@ -2489,8 +2494,30 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
+ /**
+ * Positions the selector in a way that mimics keyboard focus. If the
+ * selector drawable supports hotspots, this manages the focus hotspot.
+ */
+ void positionSelectorLikeFocus(int position, View sel) {
+ positionSelector(position, sel);
+
+ final Drawable selector = mSelector;
+ if (selector != null && selector.supportsHotspots() && position != INVALID_POSITION) {
+ final Rect bounds = mSelectorRect;
+ final float x = bounds.exactCenterX();
+ final float y = bounds.exactCenterY();
+ selector.setHotspot(R.attr.state_focused, x, y);
+ }
+ }
+
void positionSelector(int position, View sel) {
if (position != INVALID_POSITION) {
+ if (mSelectorPosition != position) {
+ final Drawable selector = mSelector;
+ if (selector != null && selector.supportsHotspots()) {
+ selector.clearHotspots();
+ }
+ }
mSelectorPosition = position;
}
@@ -3240,13 +3267,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
- private boolean startScrollIfNeeded(int y) {
+ private boolean startScrollIfNeeded(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;
final int distance = Math.abs(deltaY);
final boolean overscroll = mScrollY != 0;
- if (overscroll || distance > mTouchSlop) {
+ if ((overscroll || distance > mTouchSlop) &&
+ (getNestedScrollAxes() & SCROLL_AXIS_VERTICAL) == 0) {
createScrollingCache();
if (overscroll) {
mTouchMode = TOUCH_MODE_OVERSCROLL;
@@ -3268,17 +3296,28 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
- scrollIfNeeded(y);
+ scrollIfNeeded(y, vtev);
return true;
}
return false;
}
- private void scrollIfNeeded(int y) {
- final int rawDeltaY = y - mMotionY;
+ private void scrollIfNeeded(int y, MotionEvent vtev) {
+ int rawDeltaY = y - mMotionY;
+ if (dispatchNestedPreScroll(0, rawDeltaY, mScrollConsumed, mScrollOffset)) {
+ rawDeltaY -= mScrollConsumed[1];
+ mMotionCorrection -= mScrollOffset[1];
+ if (mLastY != Integer.MIN_VALUE) {
+ mLastY -= mScrollOffset[1] + mScrollConsumed[1];
+ }
+ if (vtev != null) {
+ vtev.offsetLocation(0, mScrollOffset[1]);
+ }
+ }
final int deltaY = rawDeltaY - mMotionCorrection;
int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
+ int lastYCorrection = 0;
if (mTouchMode == TOUCH_MODE_SCROLL) {
if (PROFILE_SCROLLING) {
@@ -3337,39 +3376,48 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
int overscroll = -incrementalDeltaY -
(motionViewRealTop - motionViewPrevTop);
- 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();
+ if (dispatchNestedScroll(0, overscroll - incrementalDeltaY, 0, overscroll,
+ mScrollOffset)) {
+ mMotionCorrection -= mScrollOffset[1];
+ lastYCorrection -= mScrollOffset[1];
+ if (vtev != null) {
+ vtev.offsetLocation(0, mScrollOffset[1]);
}
- }
-
- 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 (rawDeltaY > 0) {
- mEdgeGlowTop.onPull((float) overscroll / getHeight());
- if (!mEdgeGlowBottom.isFinished()) {
- mEdgeGlowBottom.onRelease();
+ } 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();
}
- invalidate(mEdgeGlowTop.getBounds(false));
- } else if (rawDeltaY < 0) {
- mEdgeGlowBottom.onPull((float) overscroll / getHeight());
- if (!mEdgeGlowTop.isFinished()) {
- mEdgeGlowTop.onRelease();
+ }
+
+ 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 (!mEdgeGlowBottom.isFinished()) {
+ mEdgeGlowBottom.onRelease();
+ }
+ invalidate(mEdgeGlowTop.getBounds(false));
+ } else if (deltaY < 0) {
+ mEdgeGlowBottom.onPull((float) overscroll / getHeight());
+ if (!mEdgeGlowTop.isFinished()) {
+ mEdgeGlowTop.onRelease();
+ }
+ invalidate(mEdgeGlowBottom.getBounds(true));
}
- invalidate(mEdgeGlowBottom.getBounds(true));
}
}
}
mMotionY = y;
}
- mLastY = y;
+ mLastY = y + lastYCorrection;
}
} else if (mTouchMode == TOUCH_MODE_OVERSCROLL) {
if (y != mLastY) {
@@ -3493,6 +3541,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
return false;
}
+ startNestedScroll(SCROLL_AXIS_VERTICAL);
+
if (mFastScroll != null) {
boolean intercepted = mFastScroll.onTouchEvent(ev);
if (intercepted) {
@@ -3501,7 +3551,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
initVelocityTrackerIfNotExists();
- mVelocityTracker.addMovement(ev);
+ final MotionEvent vtev = MotionEvent.obtain(ev);
final int actionMasked = ev.getActionMasked();
switch (actionMasked) {
@@ -3511,7 +3561,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
case MotionEvent.ACTION_MOVE: {
- onTouchMove(ev);
+ onTouchMove(ev, vtev);
break;
}
@@ -3562,6 +3612,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
+ if (mVelocityTracker != null) {
+ mVelocityTracker.addMovement(vtev);
+ }
+ vtev.recycle();
return true;
}
@@ -3628,7 +3682,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
- private void onTouchMove(MotionEvent ev) {
+ private void onTouchMove(MotionEvent ev, MotionEvent vtev) {
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
pointerIndex = 0;
@@ -3649,7 +3703,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)) {
+ if (startScrollIfNeeded(y, vtev)) {
break;
}
// Otherwise, check containment within list bounds. If we're
@@ -3669,7 +3723,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
break;
case TOUCH_MODE_SCROLL:
case TOUCH_MODE_OVERSCROLL:
- scrollIfNeeded(y);
+ scrollIfNeeded(y, vtev);
break;
}
}
@@ -3911,6 +3965,49 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
@Override
+ public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
+ return ((nestedScrollAxes & SCROLL_AXIS_VERTICAL) != 0);
+ }
+
+ @Override
+ public void onNestedScrollAccepted(View child, View target, int axes) {
+ super.onNestedScrollAccepted(child, target, axes);
+ startNestedScroll(SCROLL_AXIS_VERTICAL);
+ }
+
+ @Override
+ public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
+ int dxUnconsumed, int dyUnconsumed) {
+ final int motionIndex = getChildCount() / 2;
+ final View motionView = getChildAt(motionIndex);
+ final int oldTop = motionView != null ? motionView.getTop() : 0;
+ if (motionView == null || trackMotionScroll(-dyUnconsumed, -dyUnconsumed)) {
+ int myUnconsumed = dyUnconsumed;
+ int myConsumed = 0;
+ if (motionView != null) {
+ myConsumed = motionView.getTop() - oldTop;
+ myUnconsumed -= myConsumed;
+ }
+ dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null);
+ }
+ }
+
+ @Override
+ public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
+ final int childCount = getChildCount();
+ if (!consumed && childCount > 0 && canScrollList((int) velocityY) &&
+ Math.abs(velocityY) > mMinimumVelocity) {
+ reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
+ if (mFlingRunnable == null) {
+ mFlingRunnable = new FlingRunnable();
+ }
+ mFlingRunnable.start((int) velocityY);
+ return true;
+ }
+ return dispatchNestedFling(velocityX, velocityY, consumed);
+ }
+
+ @Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (mEdgeGlowTop != null) {
@@ -4046,6 +4143,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mLastY = Integer.MIN_VALUE;
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
+ startNestedScroll(SCROLL_AXIS_VERTICAL);
if (touchMode == TOUCH_MODE_FLING) {
return true;
}
@@ -4063,7 +4161,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
final int y = (int) ev.getY(pointerIndex);
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
- if (startScrollIfNeeded(y)) {
+ if (startScrollIfNeeded(y, null)) {
return true;
}
break;
@@ -4077,6 +4175,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mActivePointerId = INVALID_POINTER;
recycleVelocityTracker();
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
+ stopNestedScroll();
break;
}
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index e4575e5..51759c5 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -649,7 +649,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter
private class OverflowPopup extends MenuPopupHelper {
public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
boolean overflowOnly) {
- super(context, menu, anchorView, overflowOnly);
+ super(context, menu, anchorView, overflowOnly,
+ com.android.internal.R.attr.actionOverflowMenuStyle);
setGravity(Gravity.END);
setCallback(mPopupPresenterCallback);
}
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index b47177a..f91865b 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -622,8 +622,8 @@ public class ListPopupWindow {
// only set this if the dropdown is not always visible
mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
mPopup.setTouchInterceptor(mTouchInterceptor);
- mPopup.showAsDropDown(getAnchorView(),
- mDropDownHorizontalOffset, mDropDownVerticalOffset, mDropDownGravity);
+ mPopup.showAsDropDown(getAnchorView(), mDropDownHorizontalOffset,
+ mDropDownVerticalOffset, mDropDownGravity);
mDropDownList.setSelection(ListView.INVALID_POSITION);
if (!mModal || mDropDownList.isInTouchMode()) {
@@ -1565,7 +1565,7 @@ public class ListPopupWindow {
// Ensure that keyboard focus starts from the last touched position.
setSelectedPositionInt(position);
- positionSelector(position, child);
+ positionSelectorLikeFocus(position, child);
// Refresh the drawable state to reflect the new pressed state,
// which will also update the selector state.
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 5de67c8..eeb8015 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -2564,7 +2564,7 @@ public class ListView extends AbsListView {
if (needToRedraw) {
if (selectedView != null) {
- positionSelector(selectedPos, selectedView);
+ positionSelectorLikeFocus(selectedPos, selectedView);
mSelectedTop = selectedView.getTop();
}
if (!awakenScrollBars()) {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 6e71a5c..01632ae 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -18,6 +18,7 @@ package android.widget;
import com.android.internal.R;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -116,6 +117,10 @@ public class PopupWindow {
private Drawable mAboveAnchorBackgroundDrawable;
private Drawable mBelowAnchorBackgroundDrawable;
+ // Temporary animation centers. Should be moved into window params?
+ private int mAnchorRelativeX;
+ private int mAnchorRelativeY;
+
private boolean mAboveAnchor;
private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
@@ -129,12 +134,14 @@ public class PopupWindow {
};
private WeakReference<View> mAnchor;
- private OnScrollChangedListener mOnScrollChangedListener =
+
+ private final OnScrollChangedListener mOnScrollChangedListener =
new OnScrollChangedListener() {
+ @Override
public void onScrollChanged() {
- View anchor = mAnchor != null ? mAnchor.get() : null;
+ final View anchor = mAnchor != null ? mAnchor.get() : null;
if (anchor != null && mPopupView != null) {
- WindowManager.LayoutParams p = (WindowManager.LayoutParams)
+ final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
mPopupView.getLayoutParams();
updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
@@ -143,7 +150,9 @@ public class PopupWindow {
}
}
};
+
private int mAnchorXoff, mAnchorYoff, mAnchoredGravity;
+ private boolean mOverlapAnchor;
private boolean mPopupViewInitialLayoutDirectionInherited;
@@ -187,6 +196,7 @@ public class PopupWindow {
attrs, com.android.internal.R.styleable.PopupWindow, defStyleAttr, defStyleRes);
mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground);
+ mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, -1);
mAnimationStyle = animStyle == com.android.internal.R.style.Animation_PopupWindow ? -1 :
@@ -934,9 +944,9 @@ public class PopupWindow {
// do the job.
if (mAboveAnchorBackgroundDrawable != null) {
if (mAboveAnchor) {
- mPopupView.setBackgroundDrawable(mAboveAnchorBackgroundDrawable);
+ mPopupView.setBackground(mAboveAnchorBackgroundDrawable);
} else {
- mPopupView.setBackgroundDrawable(mBelowAnchorBackgroundDrawable);
+ mPopupView.setBackground(mBelowAnchorBackgroundDrawable);
}
} else {
mPopupView.refreshDrawableState();
@@ -1114,36 +1124,43 @@ public class PopupWindow {
}
return mAnimationStyle;
}
-
+
/**
- * <p>Positions the popup window on screen. When the popup window is too
- * tall to fit under the anchor, a parent scroll view is seeked and scrolled
- * up to reclaim space. If scrolling is not possible or not enough, the
- * popup window gets moved on top of the anchor.</p>
- *
- * <p>The height must have been set on the layout parameters prior to
- * calling this method.</p>
- *
+ * Positions the popup window on screen. When the popup window is too tall
+ * to fit under the anchor, a parent scroll view is seeked and scrolled up
+ * to reclaim space. If scrolling is not possible or not enough, the popup
+ * window gets moved on top of the anchor.
+ * <p>
+ * The height must have been set on the layout parameters prior to calling
+ * this method.
+ *
* @param anchor the view on which the popup window must be anchored
* @param p the layout parameters used to display the drop down
- *
+ * @param xoff horizontal offset used to adjust for background padding
+ * @param yoff vertical offset used to adjust for background padding
+ * @param gravity horizontal gravity specifying popup alignment
* @return true if the popup is translated upwards to fit on screen
*/
- private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p,
- int xoff, int yoff, int gravity) {
-
+ private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, int xoff,
+ int yoff, int gravity) {
final int anchorHeight = anchor.getHeight();
+ final int anchorWidth = anchor.getWidth();
+ if (mOverlapAnchor) {
+ yoff -= anchorHeight;
+ }
+
anchor.getLocationInWindow(mDrawingLocation);
p.x = mDrawingLocation[0] + xoff;
p.y = mDrawingLocation[1] + anchorHeight + yoff;
- final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection()) &
- Gravity.HORIZONTAL_GRAVITY_MASK;
+ final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection())
+ & Gravity.HORIZONTAL_GRAVITY_MASK;
if (hgrav == Gravity.RIGHT) {
- // Flip the location to align the right sides of the popup and anchor instead of left
- p.x -= mPopupWidth - anchor.getWidth();
+ // Flip the location to align the right sides of the popup and
+ // anchor instead of left.
+ p.x -= mPopupWidth - anchorWidth;
}
-
+
boolean onTop = false;
p.gravity = Gravity.LEFT | Gravity.TOP;
@@ -1152,60 +1169,58 @@ public class PopupWindow {
final Rect displayFrame = new Rect();
anchor.getWindowVisibleDisplayFrame(displayFrame);
- int screenY = mScreenLocation[1] + anchorHeight + yoff;
-
+ final int screenY = mScreenLocation[1] + anchorHeight + yoff;
final View root = anchor.getRootView();
- if (screenY + mPopupHeight > displayFrame.bottom ||
- p.x + mPopupWidth - root.getWidth() > 0) {
- // if the drop down disappears at the bottom of the screen. we try to
- // scroll a parent scrollview or move the drop down back up on top of
- // the edit box
+ if (screenY + mPopupHeight > displayFrame.bottom
+ || p.x + mPopupWidth - root.getWidth() > 0) {
+ // If the drop down disappears at the bottom of the screen, we try
+ // to scroll a parent scrollview or move the drop down back up on
+ // top of the edit box.
if (mAllowScrollingAnchorParent) {
- int scrollX = anchor.getScrollX();
- int scrollY = anchor.getScrollY();
- Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff,
- scrollY + mPopupHeight + anchor.getHeight() + yoff);
+ final int scrollX = anchor.getScrollX();
+ final int scrollY = anchor.getScrollY();
+ final Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff,
+ scrollY + mPopupHeight + anchorHeight + yoff);
anchor.requestRectangleOnScreen(r, true);
}
- // now we re-evaluate the space available, and decide from that
+ // Now we re-evaluate the space available, and decide from that
// whether the pop-up will go above or below the anchor.
anchor.getLocationInWindow(mDrawingLocation);
p.x = mDrawingLocation[0] + xoff;
- p.y = mDrawingLocation[1] + anchor.getHeight() + yoff;
+ p.y = mDrawingLocation[1] + anchorHeight + yoff;
- // Preserve the gravity adjustment
+ // Preserve the gravity adjustment.
if (hgrav == Gravity.RIGHT) {
- p.x -= mPopupWidth - anchor.getWidth();
+ p.x -= mPopupWidth - anchorWidth;
}
-
- // determine whether there is more space above or below the anchor
+
+ // Determine whether there is more space above or below the anchor.
anchor.getLocationOnScreen(mScreenLocation);
-
- onTop = (displayFrame.bottom - mScreenLocation[1] - anchor.getHeight() - yoff) <
+ onTop = (displayFrame.bottom - mScreenLocation[1] - anchorHeight - yoff) <
(mScreenLocation[1] - yoff - displayFrame.top);
if (onTop) {
p.gravity = Gravity.LEFT | Gravity.BOTTOM;
p.y = root.getHeight() - mDrawingLocation[1] + yoff;
} else {
- p.y = mDrawingLocation[1] + anchor.getHeight() + yoff;
+ p.y = mDrawingLocation[1] + anchorHeight + yoff;
}
}
if (mClipToScreen) {
final int displayFrameWidth = displayFrame.right - displayFrame.left;
-
- int right = p.x + p.width;
+ final int right = p.x + p.width;
if (right > displayFrameWidth) {
p.x -= right - displayFrameWidth;
}
+
if (p.x < displayFrame.left) {
p.x = displayFrame.left;
p.width = Math.min(p.width, displayFrameWidth);
}
if (onTop) {
- int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
+ final int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
if (popupTop < 0) {
p.y += popupTop;
}
@@ -1215,7 +1230,11 @@ public class PopupWindow {
}
p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
-
+
+ // Compute the position of the anchor relative to the popup.
+ mAnchorRelativeX = mDrawingLocation[0] - p.x + anchorHeight / 2;
+ mAnchorRelativeY = mDrawingLocation[1] - p.y + anchorWidth / 2;
+
return onTop;
}
@@ -1503,7 +1522,8 @@ public class PopupWindow {
}
WeakReference<View> oldAnchor = mAnchor;
- final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
+ final boolean needsUpdate = updateLocation
+ && (mAnchorXoff != xoff || mAnchorYoff != yoff);
if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
registerForScrollChanged(anchor, xoff, yoff, gravity);
} else if (needsUpdate) {
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 7e8f6b4..0fa75a6 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -583,7 +583,8 @@ public class ScrollView extends FrameLayout {
@Override
public boolean onTouchEvent(MotionEvent ev) {
initVelocityTrackerIfNotExists();
- mVelocityTracker.addMovement(ev);
+
+ MotionEvent vtev = MotionEvent.obtain(ev);
final int action = ev.getAction();
@@ -627,7 +628,8 @@ public class ScrollView extends FrameLayout {
final int y = (int) ev.getY(activePointerIndex);
int deltaY = mLastMotionY - y;
if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
- deltaY -= mScrollConsumed[1] + mScrollOffset[1];
+ deltaY -= mScrollConsumed[1];
+ vtev.offsetLocation(0, mScrollOffset[1]);
}
if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
final ViewParent parent = getParent();
@@ -643,7 +645,7 @@ public class ScrollView extends FrameLayout {
}
if (mIsBeingDragged) {
// Scroll to follow the motion event
- mLastMotionY = y;
+ mLastMotionY = y - mScrollOffset[1];
final int oldY = mScrollY;
final int range = getScrollRange();
@@ -663,6 +665,7 @@ public class ScrollView extends FrameLayout {
final int unconsumedY = deltaY - scrolledDeltaY;
if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) {
mLastMotionY -= mScrollOffset[1];
+ vtev.offsetLocation(0, mScrollOffset[1]);
} else if (canOverscroll) {
final int pulledToY = oldY + deltaY;
if (pulledToY < 0) {
@@ -720,6 +723,11 @@ public class ScrollView extends FrameLayout {
mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
break;
}
+
+ if (mVelocityTracker != null) {
+ mVelocityTracker.addMovement(vtev);
+ }
+ vtev.recycle();
return true;
}
@@ -1565,10 +1573,10 @@ public class ScrollView extends FrameLayout {
}
private void flingWithNestedDispatch(int velocityY) {
- if (mScrollY == 0 && velocityY < 0 ||
- mScrollY == getScrollRange() && velocityY > 0) {
- dispatchNestedFling(0, velocityY);
- } else {
+ final boolean canFling = (mScrollY > 0 || velocityY > 0) &&
+ (mScrollY < getScrollRange() || velocityY < 0);
+ dispatchNestedFling(0, velocityY, canFling);
+ if (canFling) {
fling(velocityY);
}
}
@@ -1627,6 +1635,12 @@ public class ScrollView extends FrameLayout {
return (nestedScrollAxes & SCROLL_AXIS_VERTICAL) != 0;
}
+ @Override
+ public void onNestedScrollAccepted(View child, View target, int axes) {
+ super.onNestedScrollAccepted(child, target, axes);
+ startNestedScroll(SCROLL_AXIS_VERTICAL);
+ }
+
/**
* @inheritDoc
*/
@@ -1638,16 +1652,23 @@ public class ScrollView extends FrameLayout {
@Override
public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed) {
+ final int oldScrollY = mScrollY;
scrollBy(0, dyUnconsumed);
+ final int myConsumed = mScrollY - oldScrollY;
+ final int myUnconsumed = dyUnconsumed - myConsumed;
+ dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null);
}
/**
* @inheritDoc
*/
@Override
- public boolean onNestedFling(View target, float velocityX, float velocityY) {
- flingWithNestedDispatch((int) velocityY);
- return true;
+ public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
+ if (!consumed) {
+ flingWithNestedDispatch((int) velocityY);
+ return true;
+ }
+ return false;
}
@Override