summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/animation/Animator.java16
-rw-r--r--core/java/android/animation/PropertyValuesHolder.java2
-rw-r--r--core/java/android/bluetooth/BluetoothGatt.java8
-rw-r--r--core/java/android/content/Intent.java6
-rw-r--r--core/java/android/content/UndoManager.java2
-rw-r--r--core/java/android/content/UndoOperation.java2
-rw-r--r--core/java/android/content/UndoOwner.java2
-rw-r--r--core/java/android/content/pm/ServiceInfo.java2
-rw-r--r--core/java/android/hardware/FlushCompleteListener.java35
-rw-r--r--core/java/android/hardware/Sensor.java22
-rw-r--r--core/java/android/hardware/SensorManager.java162
-rw-r--r--core/java/android/hardware/SystemSensorManager.java116
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java37
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java37
-rw-r--r--core/java/android/hardware/camera2/Rational.java83
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDevice.java4
-rw-r--r--core/java/android/hardware/camera2/utils/CameraBinderDecorator.java4
-rw-r--r--core/java/android/nfc/IAppCallback.aidl (renamed from core/java/android/nfc/INdefPushCallback.aidl)4
-rw-r--r--core/java/android/nfc/INfcAdapter.aidl7
-rw-r--r--core/java/android/nfc/NfcActivityManager.java47
-rw-r--r--core/java/android/nfc/NfcAdapter.java49
-rw-r--r--core/java/android/nfc/cardemulation/ApduServiceInfo.java14
-rw-r--r--core/java/android/nfc/cardemulation/CardEmulation.java343
-rw-r--r--core/java/android/nfc/cardemulation/CardEmulationManager.java4
-rw-r--r--core/java/android/nfc/cardemulation/HostApduService.java31
-rw-r--r--core/java/android/nfc/cardemulation/OffHostApduService.java5
-rw-r--r--core/java/android/os/BatteryStats.java25
-rw-r--r--core/java/android/os/UserManager.java13
-rw-r--r--core/java/android/print/PrintDocumentAdapter.java10
-rw-r--r--core/java/android/print/PrintFileDocumentAdapter.java9
-rw-r--r--core/java/android/printservice/PrinterDiscoverySession.java2
-rw-r--r--core/java/android/provider/Settings.java33
-rw-r--r--core/java/android/security/IKeystoreService.java16
-rw-r--r--core/java/android/transition/AutoTransition.java (renamed from core/java/android/view/transition/AutoTransition.java)16
-rw-r--r--core/java/android/transition/ChangeBounds.java (renamed from core/java/android/view/transition/Move.java)37
-rw-r--r--core/java/android/transition/Crossfade.java (renamed from core/java/android/view/transition/Crossfade.java)49
-rw-r--r--core/java/android/transition/Fade.java (renamed from core/java/android/view/transition/Fade.java)50
-rw-r--r--core/java/android/transition/Recolor.java (renamed from core/java/android/view/transition/Recolor.java)29
-rw-r--r--core/java/android/transition/Rotate.java (renamed from core/java/android/view/transition/Rotate.java)15
-rw-r--r--core/java/android/transition/Scene.java (renamed from core/java/android/view/transition/Scene.java)85
-rw-r--r--core/java/android/transition/Slide.java (renamed from core/java/android/view/transition/Slide.java)8
-rw-r--r--core/java/android/transition/TextChange.java (renamed from core/java/android/view/transition/TextChange.java)45
-rw-r--r--core/java/android/transition/Transition.java (renamed from core/java/android/view/transition/Transition.java)377
-rw-r--r--core/java/android/transition/TransitionInflater.java (renamed from core/java/android/view/transition/TransitionInflater.java)156
-rw-r--r--core/java/android/transition/TransitionManager.java (renamed from core/java/android/view/transition/TransitionManager.java)112
-rw-r--r--core/java/android/transition/TransitionSet.java (renamed from core/java/android/view/transition/TransitionGroup.java)218
-rw-r--r--core/java/android/transition/TransitionValues.java (renamed from core/java/android/view/transition/TransitionValues.java)6
-rw-r--r--core/java/android/transition/TransitionValuesMaps.java (renamed from core/java/android/view/transition/TransitionValuesMaps.java)2
-rw-r--r--core/java/android/transition/Visibility.java (renamed from core/java/android/view/transition/Visibility.java)80
-rw-r--r--core/java/android/transition/package.html (renamed from core/java/android/view/transition/package.html)11
-rw-r--r--core/java/android/view/GestureDetector.java15
-rw-r--r--core/java/android/view/ScaleGestureDetector.java132
-rw-r--r--core/java/android/view/View.java41
-rw-r--r--core/java/android/view/ViewGroup.java2
-rw-r--r--core/java/android/view/ViewParent.java6
-rw-r--r--core/java/android/view/ViewRootImpl.java2
-rw-r--r--core/java/android/webkit/CallbackProxy.java43
-rw-r--r--core/java/android/webkit/DebugFlags.java18
-rw-r--r--core/java/android/webkit/HTML5VideoFullScreen.java5
-rw-r--r--core/java/android/webkit/HTML5VideoViewProxy.java5
-rw-r--r--core/java/android/webkit/WebView.java55
-rw-r--r--core/java/android/webkit/WebViewClassic.java2
-rw-r--r--core/java/android/widget/AbsListView.java34
-rw-r--r--core/java/android/widget/ActivityChooserView.java30
-rw-r--r--core/java/android/widget/ListPopupWindow.java6
-rw-r--r--core/java/android/widget/PopupMenu.java6
-rw-r--r--core/java/android/widget/RelativeLayout.java19
-rw-r--r--core/java/android/widget/TextView.java4
-rw-r--r--core/java/android/widget/VideoView.java209
69 files changed, 2299 insertions, 783 deletions
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 89accbb..129e52c 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -280,19 +280,9 @@ public abstract class Animator implements Cloneable {
}
/**
- * Gets the set of {@link AnimatorPauseListener} objects that are currently
- * listening for pause/resume events on this animator.
- *
- * @return ArrayList<AnimatorListener> The set of pause listeners.
- */
- public ArrayList<AnimatorPauseListener> getPauseListeners() {
- return mPauseListeners;
- }
-
- /**
- * Removes all listeners from this object. This is equivalent to calling
- * {@link #getListeners()} and {@link #getPauseListeners()} followed by calling
- * {@link ArrayList#clear()} on the returned lists of listeners.
+ * Removes all {@link #addListener(android.animation.Animator.AnimatorListener) listeners}
+ * and {@link #addPauseListener(android.animation.Animator.AnimatorPauseListener)
+ * pauseListeners} from this object.
*/
public void removeAllListeners() {
if (mListeners != null) {
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index 5b1a7cf..43014ad 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -636,7 +636,7 @@ public class PropertyValuesHolder implements Cloneable {
}
/**
- * The TypeEvaluator will the automatically determined based on the type of values
+ * The TypeEvaluator will be automatically determined based on the type of values
* supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
* desired. This may be important in cases where either the type of the values supplied
* do not match the way that they should be interpolated between, or if the values
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index b390aa1..a2bb78c 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -702,6 +702,10 @@ public final class BluetoothGatt implements BluetoothProfile {
* @param start Start or stop advertising
*/
/*package*/ void listen(boolean start) {
+ if (mContext == null || !mContext.getResources().
+ getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) {
+ throw new UnsupportedOperationException("BluetoothGatt#listen is blocked");
+ }
if (DBG) Log.d(TAG, "listen() - start: " + start);
if (mService == null || mClientIf == 0) return;
@@ -728,6 +732,10 @@ public final class BluetoothGatt implements BluetoothProfile {
/*package*/ void setAdvData(boolean advData, boolean includeName, boolean includeTxPower,
Integer minInterval, Integer maxInterval,
Integer appearance, Byte[] manufacturerData) {
+ if (mContext == null || !mContext.getResources().
+ getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) {
+ throw new UnsupportedOperationException("BluetoothGatt#setAdvData is blocked");
+ }
if (DBG) Log.d(TAG, "setAdvData()");
if (mService == null || mClientIf == 0) return;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d7ca915..dfc0412 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3300,8 +3300,10 @@ public class Intent implements Parcelable, Cloneable {
/**
* Optional extra for {@link #ACTION_SHUTDOWN} that allows the sender to qualify that
* this shutdown is only for the user space of the system, not a complete shutdown.
- * Hardware should not be shut down when this is true. The default if not supplied
- * is false.
+ * When this is true, hardware devices can use this information to determine that
+ * they shouldn't do a complete shutdown of their device since this is not a
+ * complete shutdown down to the kernel, but only user space restarting.
+ * The default if not supplied is false.
*/
public static final String EXTRA_SHUTDOWN_USERSPACE_ONLY
= "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
diff --git a/core/java/android/content/UndoManager.java b/core/java/android/content/UndoManager.java
index 1c2db47..e9ec5a4 100644
--- a/core/java/android/content/UndoManager.java
+++ b/core/java/android/content/UndoManager.java
@@ -50,6 +50,8 @@ import java.util.HashMap;
* undo/redo them without needing to impact edits in other objects; while
* within the larger document, all edits can be seen and the user must
* undo/redo them as a single stream.</p>
+ *
+ * @hide
*/
public class UndoManager {
private final HashMap<String, UndoOwner> mOwners = new HashMap<String, UndoOwner>();
diff --git a/core/java/android/content/UndoOperation.java b/core/java/android/content/UndoOperation.java
index 8084b1f..1ff32d4 100644
--- a/core/java/android/content/UndoOperation.java
+++ b/core/java/android/content/UndoOperation.java
@@ -23,6 +23,8 @@ import android.os.Parcelable;
* A single undoable operation. You must subclass this to implement the state
* and behavior for your operation. Instances of this class are placed and
* managed in an {@link UndoManager}.
+ *
+ * @hide
*/
public abstract class UndoOperation<DATA> implements Parcelable {
UndoOwner mOwner;
diff --git a/core/java/android/content/UndoOwner.java b/core/java/android/content/UndoOwner.java
index a279de6..d0cdc95 100644
--- a/core/java/android/content/UndoOwner.java
+++ b/core/java/android/content/UndoOwner.java
@@ -18,6 +18,8 @@ package android.content;
/**
* Representation of an owner of {@link UndoOperation} objects in an {@link UndoManager}.
+ *
+ * @hide
*/
public class UndoOwner {
final String mTag;
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 3f17dc4..3dc8717 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -52,7 +52,7 @@ public class ServiceInfo extends ComponentInfo
* Bit in {@link #flags}: If set,
* {@link android.app.Service#onProvideAssistData(android.os.Bundle)} will
* be called on the service when it is running in the foreground. Set from
- * the android.R.attr#provideAssistData attribute.
+ * the {@link android.R.attr#provideAssistData} attribute.
*/
public static final int FLAG_PROVIDE_ASSIST_DATA = 0x0004;
diff --git a/core/java/android/hardware/FlushCompleteListener.java b/core/java/android/hardware/FlushCompleteListener.java
new file mode 100644
index 0000000..cb5b9e3
--- /dev/null
+++ b/core/java/android/hardware/FlushCompleteListener.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 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;
+
+/**
+ * Used for receiving a notification when a flush() has been successfully completed.
+ * @hide
+ */
+public interface FlushCompleteListener {
+ /**
+ * Called after flush() is completed. This flush() could have been initiated by this application
+ * or some other application. All the events in the batch at the point when the flush was called
+ * have been delivered to the applications registered for those sensor events.
+ * <p>
+ *
+ * @param sensor The {@link android.hardware.Sensor Sensor} on which flush was called.
+ *
+ * @see android.hardware.SensorManager#flush(Sensor)
+ */
+ public void onFlushCompleted(Sensor sensor);
+}
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 9bffdbe..bbede57 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -319,6 +319,8 @@ public final class Sensor {
private float mResolution;
private float mPower;
private int mMinDelay;
+ private int mFifoReservedEventCount;
+ private int mFifoMaxEventCount;
Sensor() {
}
@@ -381,6 +383,26 @@ public final class Sensor {
return mMinDelay;
}
+ /**
+ * @return Number of events reserved for this sensor in the batch mode FIFO. This gives a
+ * guarantee on the minimum number of events that can be batched
+ * @hide
+ */
+ public int getFifoReservedEventCount() {
+ return mFifoReservedEventCount;
+ }
+
+ /**
+ * @return Maximum number of events of this sensor that could be batched. If this value is zero
+ * it indicates that batch mode is not supported for this sensor. If other applications
+ * registered to batched sensors, the actual number of events that can be batched might be
+ * smaller because the hardware FiFo will be partially used to batch the other sensors.
+ * @hide
+ */
+ public int getFifoMaxEventCount() {
+ return mFifoMaxEventCount;
+ }
+
/** @hide */
public int getHandle() {
return mHandle;
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 30118f9..b6ca62a 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -608,8 +608,72 @@ public abstract class SensorManager {
}
/**
- * Registers a {@link android.hardware.SensorEventListener
- * SensorEventListener} for the given sensor.
+ * Enables batch mode for a sensor with the given rate and maxBatchReportLatency. If the
+ * underlying hardware does not support batch mode, this defaults to
+ * {@link #registerListener(SensorEventListener, Sensor, int)} and other parameters are are
+ * ignored. In non-batch mode, all sensor events must be reported as soon as they are detected.
+ * While in batch mode, sensor events do not need to be reported as soon as they are detected.
+ * They can be temporarily stored in batches and reported in batches, as long as no event is
+ * delayed by more than "maxBatchReportLatency" microseconds. That is, all events since the
+ * previous batch are recorded and returned all at once. This allows to reduce the amount of
+ * interrupts sent to the SoC, and allows the SoC to switch to a lower power state (Idle) while
+ * the sensor is capturing and batching data.
+ * <p>
+ * Registering to a sensor in batch mode will not prevent the SoC from going to suspend mode. In
+ * this case, the sensor will continue to gather events and store it in a hardware FIFO. If the
+ * FIFO gets full before the AP wakes up again, some events will be lost, as the older events
+ * get overwritten by new events in the hardware FIFO. This can be avoided by holding a wake
+ * lock. If the application holds a wake lock, the SoC will not go to suspend mode, so no events
+ * will be lost, as the events will be reported before the FIFO gets full.
+ * </p>
+ * <p>
+ * Batching is always best effort. If a different application requests updates in continuous
+ * mode, this application will also get events in continuous mode. Batch mode updates can be
+ * unregistered by calling {@link #unregisterListener(SensorEventListener)}.
+ * </p>
+ * <p class="note">
+ * </p>
+ * Note: Don't use this method with a one shot trigger sensor such as
+ * {@link Sensor#TYPE_SIGNIFICANT_MOTION}. Use
+ * {@link #requestTriggerSensor(TriggerEventListener, Sensor)} instead. </p>
+ *
+ * @param listener A {@link android.hardware.SensorEventListener SensorEventListener} object
+ * that will receive the sensor events.
+ * @param sensor The {@link android.hardware.Sensor Sensor} to register to.
+ * @param rate The desired delay between two consecutive events in microseconds. This is only a
+ * hint to the system. Events may be received faster or slower than the specified
+ * rate. Usually events are received faster. Can be one of
+ * {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
+ * {@link #SENSOR_DELAY_GAME}, {@link #SENSOR_DELAY_FASTEST} or the delay in
+ * microseconds.
+ * @param maxBatchReportLatency An event in the batch can be delayed by at most
+ * maxBatchReportLatency microseconds. More events can be batched if this value is
+ * large. If this is set to zero, batch mode is disabled and events are delivered in
+ * continuous mode as soon as they are available which is equivalent to calling
+ * {@link #registerListener(SensorEventListener, Sensor, int)}.
+ * @param reservedFlags Always set to Zero.
+ * @param flushCompleteListener A {@link android.hardware.FlushCompleteListener
+ * FlushCompleteListener} object which is called when any application calls flush()
+ * on this sensor and all the events in the batch at the time of calling flush() are
+ * successfully delivered to the listeners.
+ * @return true if batch mode is successfully enabled for this sensor, false otherwise.
+ * @see #registerListener(SensorEventListener, Sensor, int)
+ * @see #unregisterListener(SensorEventListener)
+ * @see #flush(Sensor)
+ * @throws IllegalArgumentException when sensor or listener is null or a trigger sensor.
+ * @hide
+ */
+ public boolean registerListener(SensorEventListener listener, Sensor sensor, int rateUs,
+ int maxBatchReportLatencyUs, int reservedFlags,
+ FlushCompleteListener flushCompleteListener) {
+ int delay = getDelay(rateUs);
+ return registerListenerImpl(listener, sensor, delay, null, maxBatchReportLatencyUs,
+ reservedFlags, flushCompleteListener);
+ }
+
+ /**
+ * Registers a {@link android.hardware.SensorEventListener SensorEventListener} for the given
+ * sensor. Events are delivered in continuous mode as soon as they are available.
*
* <p class="note"></p>
* Note: Don't use this method with a one shot trigger sensor such as
@@ -655,31 +719,55 @@ public abstract class SensorManager {
return false;
}
- int delay = -1;
- switch (rate) {
- case SENSOR_DELAY_FASTEST:
- delay = 0;
- break;
- case SENSOR_DELAY_GAME:
- delay = 20000;
- break;
- case SENSOR_DELAY_UI:
- delay = 66667;
- break;
- case SENSOR_DELAY_NORMAL:
- delay = 200000;
- break;
- default:
- delay = rate;
- break;
- }
+ int delay = getDelay(rate);
+ return registerListenerImpl(listener, sensor, delay, handler, 0, 0, null);
+ }
- return registerListenerImpl(listener, sensor, delay, handler);
+ /**
+ * Enables batch mode for a sensor with the given rate and maxBatchReportLatency.
+ * @param handler
+ * The {@link android.os.Handler Handler} the
+ * {@link android.hardware.SensorEvent sensor events} will be
+ * delivered to.
+ *
+ * @see #registerListener(SensorEventListener, Sensor, int, int, int, FlushCompleteListener)
+ * @hide
+ */
+ public boolean registerListener(SensorEventListener listener, Sensor sensor, int rateUs,
+ int maxBatchReportLatencyUs, int reservedFlags, Handler handler,
+ FlushCompleteListener flushCompleteListener) {
+ int delayUs = getDelay(rateUs);
+ return registerListenerImpl(listener, sensor, delayUs, handler, maxBatchReportLatencyUs,
+ reservedFlags, flushCompleteListener);
}
/** @hide */
protected abstract boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
- int delay, Handler handler);
+ int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags,
+ FlushCompleteListener flushCompleteListener);
+
+
+ /**
+ * Flushes the batch FIFO of the given sensor. If there are events in the FIFO of this sensor,
+ * they are returned as if the batch timeout has expired. Events are returned in the
+ * usual way through the SensorEventListener. This call doesn't effect the batch timeout for
+ * this sensor. This call is asynchronous and returns immediately. FlushCompleteListener is
+ * called after all the events in the batch at the time of calling this method have been
+ * delivered successfully.
+ * @param sensor
+ * The {@link android.hardware.Sensor Sensor} to flush.
+ * @return true if the flush is initiated successfully. false if the sensor isn't active
+ * i.e no application is registered for updates from this sensor.
+ * @see #registerListener(SensorEventListener, Sensor, int, int, int, FlushCompleteListener)
+ * @throws IllegalArgumentException when sensor is null or a trigger sensor.
+ * @hide
+ */
+ public boolean flush(Sensor sensor) {
+ return flushImpl(sensor);
+ }
+
+ /** @hide */
+ protected abstract boolean flushImpl(Sensor sensor);
/**
* <p>
@@ -1079,15 +1167,15 @@ public abstract class SensorManager {
* <p>
* All three angles above are in <b>radians</b> and <b>positive</b> in the
* <b>counter-clockwise</b> direction.
- *
+ *
* @param R
* rotation matrix see {@link #getRotationMatrix}.
- *
+ *
* @param values
* an array of 3 floats to hold the result.
- *
+ *
* @return The array values passed as argument.
- *
+ *
* @see #getRotationMatrix(float[], float[], float[], float[])
* @see GeomagneticField
*/
@@ -1407,4 +1495,26 @@ public abstract class SensorManager {
return mLegacySensorManager;
}
}
+
+ private static int getDelay(int rate) {
+ int delay = -1;
+ switch (rate) {
+ case SENSOR_DELAY_FASTEST:
+ delay = 0;
+ break;
+ case SENSOR_DELAY_GAME:
+ delay = 20000;
+ break;
+ case SENSOR_DELAY_UI:
+ delay = 66667;
+ break;
+ case SENSOR_DELAY_NORMAL:
+ delay = 200000;
+ break;
+ default:
+ delay = rate;
+ break;
+ }
+ return delay;
+ }
}
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 852cf4a..9747f0d 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -93,30 +93,35 @@ public class SystemSensorManager extends SensorManager {
/** @hide */
@Override
protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
- int delay, Handler handler)
- {
+ int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags,
+ FlushCompleteListener flushCompleteListener) {
+ if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
+ if (listener == null) throw new IllegalArgumentException("listener cannot be null");
+ if (reservedFlags != 0) throw new IllegalArgumentException("reservedFlags should be zero");
+ if (delayUs < 0) throw new IllegalArgumentException("rateUs should be positive");
+ if (maxBatchReportLatencyUs < 0)
+ throw new IllegalArgumentException("maxBatchReportLatencyUs should be positive");
+ // Trigger Sensors should use the requestTriggerSensor call.
+ if (Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT)
+ throw new IllegalArgumentException("Trigger Sensors cannot use registerListener");
+
// Invariants to preserve:
// - one Looper per SensorEventListener
// - one Looper per SensorEventQueue
// We map SensorEventListener to a SensorEventQueue, which holds the looper
- if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
-
- // Trigger Sensors should use the requestTriggerSensor call.
- if (Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) return false;
-
synchronized (mSensorListeners) {
SensorEventQueue queue = mSensorListeners.get(listener);
if (queue == null) {
Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
- queue = new SensorEventQueue(listener, looper, this);
- if (!queue.addSensor(sensor, delay)) {
+ queue = new SensorEventQueue(listener, looper, this, flushCompleteListener);
+ if (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags)) {
queue.dispose();
return false;
}
mSensorListeners.put(listener, queue);
return true;
} else {
- return queue.addSensor(sensor, delay);
+ return queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags);
}
}
}
@@ -157,14 +162,14 @@ public class SystemSensorManager extends SensorManager {
TriggerEventQueue queue = mTriggerListeners.get(listener);
if (queue == null) {
queue = new TriggerEventQueue(listener, mMainLooper, this);
- if (!queue.addSensor(sensor, 0)) {
+ if (!queue.addSensor(sensor, 0, 0, 0)) {
queue.dispose();
return false;
}
mTriggerListeners.put(listener, queue);
return true;
} else {
- return queue.addSensor(sensor, 0);
+ return queue.addSensor(sensor, 0, 0, 0);
}
}
}
@@ -195,6 +200,18 @@ public class SystemSensorManager extends SensorManager {
}
}
+ protected boolean flushImpl(Sensor sensor) {
+ if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
+ if(Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT)
+ throw new IllegalArgumentException("Trigger Sensors cannot call flush");
+
+ FlushEventQueue queue = new FlushEventQueue(mMainLooper, this);
+ if (queue.flushSensor(sensor) != 0) {
+ return false;
+ }
+ return true;
+ }
+
/*
* BaseEventQueue is the communication channel with the sensor service,
* SensorEventQueue, TriggerEventQueue are subclases and there is one-to-one mapping between
@@ -202,11 +219,12 @@ public class SystemSensorManager extends SensorManager {
*/
private static abstract class BaseEventQueue {
private native int nativeInitBaseEventQueue(BaseEventQueue eventQ, MessageQueue msgQ,
-
float[] scratch);
- private static native int nativeEnableSensor(int eventQ, int handle, int us);
+ private static native int nativeEnableSensor(int eventQ, int handle, int rateUs,
+ int maxBatchReportLatencyUs, int reservedFlags);
private static native int nativeDisableSensor(int eventQ, int handle);
private static native void nativeDestroySensorEventQueue(int eventQ);
+ private static native int nativeFlushSensor(int eventQ, int handle);
private int nSensorEventQueue;
private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
@@ -225,7 +243,8 @@ public class SystemSensorManager extends SensorManager {
dispose(false);
}
- public boolean addSensor(Sensor sensor, int delay) {
+ public boolean addSensor(
+ Sensor sensor, int delayUs, int maxBatchReportLatencyUs, int reservedFlags) {
// Check if already present.
int handle = sensor.getHandle();
if (mActiveSensors.get(handle)) return false;
@@ -233,9 +252,13 @@ public class SystemSensorManager extends SensorManager {
// Get ready to receive events before calling enable.
mActiveSensors.put(handle, true);
addSensorEvent(sensor);
- if (enableSensor(sensor, delay) != 0) {
- removeSensor(sensor, false);
- return false;
+ if (enableSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags) != 0) {
+ // Try continuous mode if batching fails.
+ if (maxBatchReportLatencyUs == 0 ||
+ maxBatchReportLatencyUs > 0 && enableSensor(sensor, delayUs, 0, 0) != 0) {
+ removeSensor(sensor, false);
+ return false;
+ }
}
return true;
}
@@ -268,6 +291,12 @@ public class SystemSensorManager extends SensorManager {
return false;
}
+ public int flushSensor(Sensor sensor) {
+ if (nSensorEventQueue == 0) throw new NullPointerException();
+ if (sensor == null) throw new NullPointerException();
+ return nativeFlushSensor(nSensorEventQueue, sensor.getHandle());
+ }
+
public boolean hasSensors() {
// no more sensors are set
return mActiveSensors.indexOfValue(true) >= 0;
@@ -295,11 +324,14 @@ public class SystemSensorManager extends SensorManager {
}
}
- private int enableSensor(Sensor sensor, int us) {
+ private int enableSensor(
+ Sensor sensor, int rateUs, int maxBatchReportLatencyUs, int reservedFlags) {
if (nSensorEventQueue == 0) throw new NullPointerException();
if (sensor == null) throw new NullPointerException();
- return nativeEnableSensor(nSensorEventQueue, sensor.getHandle(), us);
+ return nativeEnableSensor(nSensorEventQueue, sensor.getHandle(), rateUs,
+ maxBatchReportLatencyUs, reservedFlags);
}
+
private int disableSensor(Sensor sensor) {
if (nSensorEventQueue == 0) throw new NullPointerException();
if (sensor == null) throw new NullPointerException();
@@ -307,6 +339,7 @@ public class SystemSensorManager extends SensorManager {
}
protected abstract void dispatchSensorEvent(int handle, float[] values, int accuracy,
long timestamp);
+ protected abstract void dispatchFlushCompleteEvent(int handle);
protected abstract void addSensorEvent(Sensor sensor);
protected abstract void removeSensorEvent(Sensor sensor);
@@ -314,12 +347,14 @@ public class SystemSensorManager extends SensorManager {
static final class SensorEventQueue extends BaseEventQueue {
private final SensorEventListener mListener;
+ private final FlushCompleteListener mFlushCompleteListener;
private final SparseArray<SensorEvent> mSensorsEvents = new SparseArray<SensorEvent>();
public SensorEventQueue(SensorEventListener listener, Looper looper,
- SystemSensorManager manager) {
+ SystemSensorManager manager, FlushCompleteListener flushCompleteListener) {
super(looper, manager);
mListener = listener;
+ mFlushCompleteListener = flushCompleteListener;
}
public void addSensorEvent(Sensor sensor) {
@@ -370,6 +405,15 @@ public class SystemSensorManager extends SensorManager {
}
mListener.onSensorChanged(t);
}
+
+ @SuppressWarnings("unused")
+ protected void dispatchFlushCompleteEvent(int handle) {
+ final Sensor sensor = sHandleToSensor.get(handle);
+ if (mFlushCompleteListener != null) {
+ mFlushCompleteListener.onFlushCompleted(sensor);
+ }
+ return;
+ }
}
static final class TriggerEventQueue extends BaseEventQueue {
@@ -415,5 +459,35 @@ public class SystemSensorManager extends SensorManager {
mListener.onTrigger(t);
}
+
+ @SuppressWarnings("unused")
+ protected void dispatchFlushCompleteEvent(int handle) {
+ }
+ }
+
+ static final class FlushEventQueue extends BaseEventQueue {
+ public FlushEventQueue(Looper looper, SystemSensorManager manager) {
+ super(looper, manager);
+ }
+
+ @SuppressWarnings("unused")
+ @Override
+ protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
+ long timestamp) {
+ }
+
+ @Override
+ @SuppressWarnings("unused")
+ protected void addSensorEvent(Sensor sensor) {
+ }
+
+ @Override
+ @SuppressWarnings("unused")
+ protected void removeSensorEvent(Sensor sensor) {
+ }
+
+ @SuppressWarnings("unused")
+ protected void dispatchFlushCompleteEvent(int handle) {
+ }
}
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 8b5bf4a..3f9b9e9 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -303,13 +303,14 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable {
* </p>
* <p>
* Each area is a rectangle plus weight: xmin, ymin,
- * xmax, ymax, weight.
+ * xmax, ymax, weight. The rectangle is defined inclusive of the
+ * specified coordinates.
* </p><p>
* The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left of the active pixel array, and
- * (android.sensor.info.activeArraySize.width,
- * android.sensor.info.activeArraySize.height) being the
- * bottom-right point of the active pixel array. The weight
+ * with (0,0) being the top-left pixel in the active pixel array, and
+ * (android.sensor.info.activeArraySize.width - 1,
+ * android.sensor.info.activeArraySize.height - 1) being the
+ * bottom-right pixel in the active pixel array. The weight
* should be nonnegative.
* </p><p>
* If all regions have 0 weight, then no specific metering area
@@ -378,13 +379,14 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable {
* </p>
* <p>
* Each area is a rectangle plus weight: xmin, ymin,
- * xmax, ymax, weight.
+ * xmax, ymax, weight. The rectangle is defined inclusive of the
+ * specified coordinates.
* </p><p>
* The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left of the active pixel array, and
- * (android.sensor.info.activeArraySize.width,
- * android.sensor.info.activeArraySize.height) being the
- * bottom-right point of the active pixel array. The weight
+ * with (0,0) being the top-left pixel in the active pixel array, and
+ * (android.sensor.info.activeArraySize.width - 1,
+ * android.sensor.info.activeArraySize.height - 1) being the
+ * bottom-right pixel in the active pixel array. The weight
* should be nonnegative.
* </p><p>
* If all regions have 0 weight, then no specific focus area
@@ -462,12 +464,15 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable {
* <p>
* Only used in AUTO mode.
* </p><p>
- * Each area is a rectangle plus weight: xmin, ymin, xmax,
- * ymax, weight. The coordinate system is based on the active
- * pixel array, with (0,0) being the top-left of the active
- * pixel array, and (android.sensor.info.activeArraySize.width,
- * android.sensor.info.activeArraySize.height) being the
- * bottom-right point of the active pixel array. The weight
+ * Each area is a rectangle plus weight: xmin, ymin,
+ * xmax, ymax, weight. The rectangle is defined inclusive of the
+ * specified coordinates.
+ * </p><p>
+ * The coordinate system is based on the active pixel array,
+ * with (0,0) being the top-left pixel in the active pixel array, and
+ * (android.sensor.info.activeArraySize.width - 1,
+ * android.sensor.info.activeArraySize.height - 1) being the
+ * bottom-right pixel in the active pixel array. The weight
* should be nonnegative.
* </p><p>
* If all regions have 0 weight, then no specific metering area
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index ef6aaa0..bd151a2 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -198,13 +198,14 @@ public final class CaptureResult extends CameraMetadata {
* </p>
* <p>
* Each area is a rectangle plus weight: xmin, ymin,
- * xmax, ymax, weight.
+ * xmax, ymax, weight. The rectangle is defined inclusive of the
+ * specified coordinates.
* </p><p>
* The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left of the active pixel array, and
- * (android.sensor.info.activeArraySize.width,
- * android.sensor.info.activeArraySize.height) being the
- * bottom-right point of the active pixel array. The weight
+ * with (0,0) being the top-left pixel in the active pixel array, and
+ * (android.sensor.info.activeArraySize.width - 1,
+ * android.sensor.info.activeArraySize.height - 1) being the
+ * bottom-right pixel in the active pixel array. The weight
* should be nonnegative.
* </p><p>
* If all regions have 0 weight, then no specific metering area
@@ -258,13 +259,14 @@ public final class CaptureResult extends CameraMetadata {
* </p>
* <p>
* Each area is a rectangle plus weight: xmin, ymin,
- * xmax, ymax, weight.
+ * xmax, ymax, weight. The rectangle is defined inclusive of the
+ * specified coordinates.
* </p><p>
* The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left of the active pixel array, and
- * (android.sensor.info.activeArraySize.width,
- * android.sensor.info.activeArraySize.height) being the
- * bottom-right point of the active pixel array. The weight
+ * with (0,0) being the top-left pixel in the active pixel array, and
+ * (android.sensor.info.activeArraySize.width - 1,
+ * android.sensor.info.activeArraySize.height - 1) being the
+ * bottom-right pixel in the active pixel array. The weight
* should be nonnegative.
* </p><p>
* If all regions have 0 weight, then no specific focus area
@@ -342,12 +344,15 @@ public final class CaptureResult extends CameraMetadata {
* <p>
* Only used in AUTO mode.
* </p><p>
- * Each area is a rectangle plus weight: xmin, ymin, xmax,
- * ymax, weight. The coordinate system is based on the active
- * pixel array, with (0,0) being the top-left of the active
- * pixel array, and (android.sensor.info.activeArraySize.width,
- * android.sensor.info.activeArraySize.height) being the
- * bottom-right point of the active pixel array. The weight
+ * Each area is a rectangle plus weight: xmin, ymin,
+ * xmax, ymax, weight. The rectangle is defined inclusive of the
+ * specified coordinates.
+ * </p><p>
+ * The coordinate system is based on the active pixel array,
+ * with (0,0) being the top-left pixel in the active pixel array, and
+ * (android.sensor.info.activeArraySize.width - 1,
+ * android.sensor.info.activeArraySize.height - 1) being the
+ * bottom-right pixel in the active pixel array. The weight
* should be nonnegative.
* </p><p>
* If all regions have 0 weight, then no specific metering area
diff --git a/core/java/android/hardware/camera2/Rational.java b/core/java/android/hardware/camera2/Rational.java
index 0260e02..77b8c26 100644
--- a/core/java/android/hardware/camera2/Rational.java
+++ b/core/java/android/hardware/camera2/Rational.java
@@ -26,22 +26,17 @@ public final class Rational {
/**
* <p>Create a Rational with a given numerator and denominator.</p>
*
- * <p>
- * The signs of the numerator and the denominator may be flipped such that the denominator
- * is always 0.
- * </p>
+ * <p>The signs of the numerator and the denominator may be flipped such that the denominator
+ * is always positive.</p>
+ *
+ * <p>A rational value with a 0-denominator may be constructed, but will have similar semantics
+ * as float NaN and INF values. The int getter functions return 0 in this case.</p>
*
* @param numerator the numerator of the rational
* @param denominator the denominator of the rational
- *
- * @throws IllegalArgumentException if the denominator is 0
*/
public Rational(int numerator, int denominator) {
- if (denominator == 0) {
- throw new IllegalArgumentException("Argument 'denominator' is 0");
- }
-
if (denominator < 0) {
numerator = -numerator;
denominator = -denominator;
@@ -55,6 +50,9 @@ public final class Rational {
* Gets the numerator of the rational.
*/
public int getNumerator() {
+ if (mDenominator == 0) {
+ return 0;
+ }
return mNumerator;
}
@@ -65,22 +63,41 @@ public final class Rational {
return mDenominator;
}
+ private boolean isNaN() {
+ return mDenominator == 0 && mNumerator == 0;
+ }
+
+ private boolean isInf() {
+ return mDenominator == 0 && mNumerator > 0;
+ }
+
+ private boolean isNegInf() {
+ return mDenominator == 0 && mNumerator < 0;
+ }
+
/**
* <p>Compare this Rational to another object and see if they are equal.</p>
*
* <p>A Rational object can only be equal to another Rational object (comparing against any other
* type will return false).</p>
*
- * <p>A Rational object is considered equal to another Rational object if and only if their
- * reduced forms have the same numerator and denominator.</p>
+ * <p>A Rational object is considered equal to another Rational object if and only if one of
+ * the following holds</p>:
+ * <ul><li>Both are NaN</li>
+ * <li>Both are infinities of the same sign</li>
+ * <li>Both have the same numerator and denominator in their reduced form</li>
+ * </ul>
*
* <p>A reduced form of a Rational is calculated by dividing both the numerator and the
* denominator by their greatest common divisor.</p>
*
* <pre>
- * (new Rational(1, 2)).equals(new Rational(1, 2)) == true // trivially true
- * (new Rational(2, 3)).equals(new Rational(1, 2)) == false // trivially false
- * (new Rational(1, 2)).equals(new Rational(2, 4)) == true // true after reduction
+ * (new Rational(1, 2)).equals(new Rational(1, 2)) == true // trivially true
+ * (new Rational(2, 3)).equals(new Rational(1, 2)) == false // trivially false
+ * (new Rational(1, 2)).equals(new Rational(2, 4)) == true // true after reduction
+ * (new Rational(0, 0)).equals(new Rational(0, 0)) == true // NaN.equals(NaN)
+ * (new Rational(1, 0)).equals(new Rational(5, 0)) == true // both are +infinity
+ * (new Rational(1, 0)).equals(new Rational(-1, 0)) == false // +infinity != -infinity
* </pre>
*
* @param obj a reference to another object
@@ -91,13 +108,17 @@ public final class Rational {
public boolean equals(Object obj) {
if (obj == null) {
return false;
- }
- if (this == obj) {
- return true;
- }
- if (obj instanceof Rational) {
+ } else if (obj instanceof Rational) {
Rational other = (Rational) obj;
- if(mNumerator == other.mNumerator && mDenominator == other.mDenominator) {
+ if (mDenominator == 0 || other.mDenominator == 0) {
+ if (isNaN() && other.isNaN()) {
+ return true;
+ } else if (isInf() && other.isInf() || isNegInf() && other.isNegInf()) {
+ return true;
+ } else {
+ return false;
+ }
+ } else if (mNumerator == other.mNumerator && mDenominator == other.mDenominator) {
return true;
} else {
int thisGcd = gcd();
@@ -117,7 +138,25 @@ public final class Rational {
@Override
public String toString() {
- return mNumerator + "/" + mDenominator;
+ if (isNaN()) {
+ return "NaN";
+ } else if (isInf()) {
+ return "Infinity";
+ } else if (isNegInf()) {
+ return "-Infinity";
+ } else {
+ return mNumerator + "/" + mDenominator;
+ }
+ }
+
+ /**
+ * <p>Convert to a floating point representation.</p>
+ *
+ * @return The floating point representation of this rational number.
+ * @hide
+ */
+ public float toFloat() {
+ return (float) mNumerator / (float) mDenominator;
}
@Override
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index 64e4dc9..86a073f 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -301,7 +301,9 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
synchronized (mLock) {
try {
- mRemoteDevice.disconnect();
+ if (mRemoteDevice != null) {
+ mRemoteDevice.disconnect();
+ }
} catch (CameraRuntimeException e) {
throw e.asChecked();
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
index fbe7ff4..2c05c58 100644
--- a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
+++ b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
@@ -49,7 +49,7 @@ public class CameraBinderDecorator {
public static final int EACCES = -13;
public static final int EBUSY = -16;
public static final int ENODEV = -19;
- public static final int ENOTSUP = -129;
+ public static final int EOPNOTSUPP = -95;
private static class CameraBinderDecoratorListener implements Decorator.DecoratorListener {
@@ -86,7 +86,7 @@ public class CameraBinderDecorator {
case ENODEV:
UncheckedThrow.throwAnyException(new CameraRuntimeException(
CAMERA_DISCONNECTED));
- case ENOTSUP:
+ case EOPNOTSUPP:
UncheckedThrow.throwAnyException(new CameraRuntimeException(
CAMERA_DEPRECATED_HAL));
}
diff --git a/core/java/android/nfc/INdefPushCallback.aidl b/core/java/android/nfc/IAppCallback.aidl
index 16771dc..9599308 100644
--- a/core/java/android/nfc/INdefPushCallback.aidl
+++ b/core/java/android/nfc/IAppCallback.aidl
@@ -17,12 +17,14 @@
package android.nfc;
import android.nfc.BeamShareData;
+import android.nfc.Tag;
/**
* @hide
*/
-interface INdefPushCallback
+interface IAppCallback
{
BeamShareData createBeamShareData();
void onNdefPushComplete();
+ void onTagDiscovered(in Tag tag);
}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 15d0475..8414738 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -21,10 +21,11 @@ import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.Tag;
import android.nfc.TechListParcel;
-import android.nfc.INdefPushCallback;
+import android.nfc.IAppCallback;
import android.nfc.INfcAdapterExtras;
import android.nfc.INfcTag;
import android.nfc.INfcCardEmulation;
+import android.os.Bundle;
/**
* @hide
@@ -44,10 +45,10 @@ interface INfcAdapter
void setForegroundDispatch(in PendingIntent intent,
in IntentFilter[] filters, in TechListParcel techLists);
- void setNdefPushCallback(in INdefPushCallback callback);
+ void setAppCallback(in IAppCallback callback);
void dispatch(in Tag tag);
- void setReaderMode (IBinder b, int flags);
+ void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras);
void setP2pModes(int initatorModes, int targetModes);
}
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index d0d943c..77c0234 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -19,6 +19,7 @@ package android.nfc;
import android.app.Activity;
import android.app.Application;
import android.net.Uri;
+import android.nfc.NfcAdapter.ReaderCallback;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
@@ -36,7 +37,7 @@ import java.util.List;
*
* @hide
*/
-public final class NfcActivityManager extends INdefPushCallback.Stub
+public final class NfcActivityManager extends IAppCallback.Stub
implements Application.ActivityLifecycleCallbacks {
static final String TAG = NfcAdapter.TAG;
static final Boolean DBG = false;
@@ -113,6 +114,8 @@ public final class NfcActivityManager extends INdefPushCallback.Stub
Uri[] uris = null;
int flags = 0;
int readerModeFlags = 0;
+ NfcAdapter.ReaderCallback readerCallback = null;
+ Bundle readerModeExtras = null;
Binder token;
public NfcActivityState(Activity activity) {
@@ -197,17 +200,20 @@ public final class NfcActivityManager extends INdefPushCallback.Stub
mDefaultEvent = new NfcEvent(mAdapter);
}
- public void enableReaderMode(Activity activity, int flags) {
+ public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,
+ Bundle extras) {
boolean isResumed;
Binder token;
synchronized (NfcActivityManager.this) {
NfcActivityState state = getActivityState(activity);
+ state.readerCallback = callback;
state.readerModeFlags = flags;
+ state.readerModeExtras = extras;
token = state.token;
isResumed = state.resumed;
}
if (isResumed) {
- setReaderMode(token, flags);
+ setReaderMode(token, flags, extras);
}
}
@@ -216,20 +222,22 @@ public final class NfcActivityManager extends INdefPushCallback.Stub
Binder token;
synchronized (NfcActivityManager.this) {
NfcActivityState state = getActivityState(activity);
+ state.readerCallback = null;
state.readerModeFlags = 0;
+ state.readerModeExtras = null;
token = state.token;
isResumed = state.resumed;
}
if (isResumed) {
- setReaderMode(token, 0);
+ setReaderMode(token, 0, null);
}
}
- public void setReaderMode(Binder token, int flags) {
+ public void setReaderMode(Binder token, int flags, Bundle extras) {
if (DBG) Log.d(TAG, "Setting reader mode");
try {
- NfcAdapter.sService.setReaderMode(token, flags);
+ NfcAdapter.sService.setReaderMode(token, this, flags, extras);
} catch (RemoteException e) {
mAdapter.attemptDeadServiceRecovery(e);
}
@@ -302,12 +310,12 @@ public final class NfcActivityManager extends INdefPushCallback.Stub
}
/**
- * Request or unrequest NFC service callbacks for NDEF push.
+ * Request or unrequest NFC service callbacks.
* Makes IPC call - do not hold lock.
*/
void requestNfcServiceCallback() {
try {
- NfcAdapter.sService.setNdefPushCallback(this);
+ NfcAdapter.sService.setAppCallback(this);
} catch (RemoteException e) {
mAdapter.attemptDeadServiceRecovery(e);
}
@@ -375,6 +383,22 @@ public final class NfcActivityManager extends INdefPushCallback.Stub
}
}
+ @Override
+ public void onTagDiscovered(Tag tag) throws RemoteException {
+ NfcAdapter.ReaderCallback callback;
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = findResumedActivityState();
+ if (state == null) return;
+
+ callback = state.readerCallback;
+ }
+
+ // Make callback without lock
+ if (callback != null) {
+ callback.onTagDiscovered(tag);
+ }
+
+ }
/** Callback from Activity life-cycle, on main thread */
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) { /* NO-OP */ }
@@ -387,6 +411,7 @@ public final class NfcActivityManager extends INdefPushCallback.Stub
@Override
public void onActivityResumed(Activity activity) {
int readerModeFlags = 0;
+ Bundle readerModeExtras = null;
Binder token;
synchronized (NfcActivityManager.this) {
NfcActivityState state = findActivityState(activity);
@@ -395,9 +420,10 @@ public final class NfcActivityManager extends INdefPushCallback.Stub
state.resumed = true;
token = state.token;
readerModeFlags = state.readerModeFlags;
+ readerModeExtras = state.readerModeExtras;
}
if (readerModeFlags != 0) {
- setReaderMode(token, readerModeFlags);
+ setReaderMode(token, readerModeFlags, readerModeExtras);
}
requestNfcServiceCallback();
}
@@ -417,7 +443,7 @@ public final class NfcActivityManager extends INdefPushCallback.Stub
}
if (readerModeFlagsSet) {
// Restore default p2p modes
- setReaderMode(token, 0);
+ setReaderMode(token, 0, null);
}
}
@@ -441,4 +467,5 @@ public final class NfcActivityManager extends INdefPushCallback.Stub
}
}
}
+
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index fa0c1f6..2a18900 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -33,6 +33,7 @@ import android.nfc.tech.MifareClassic;
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcA;
import android.nfc.tech.NfcF;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -196,42 +197,42 @@ public final class NfcAdapter {
public static final int STATE_TURNING_OFF = 4;
/**
- * Flag for use with {@link #enableReaderMode(Activity, int)}.
+ * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
* <p>
* Setting this flag enables polling for Nfc-A technology.
*/
public static final int FLAG_READER_NFC_A = 0x1;
/**
- * Flag for use with {@link #enableReaderMode(Activity, int)}.
+ * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
* <p>
* Setting this flag enables polling for Nfc-B technology.
*/
public static final int FLAG_READER_NFC_B = 0x2;
/**
- * Flag for use with {@link #enableReaderMode(Activity, int)}.
+ * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
* <p>
* Setting this flag enables polling for Nfc-F technology.
*/
public static final int FLAG_READER_NFC_F = 0x4;
/**
- * Flag for use with {@link #enableReaderMode(Activity, int)}.
+ * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
* <p>
* Setting this flag enables polling for Nfc-V (ISO15693) technology.
*/
public static final int FLAG_READER_NFC_V = 0x8;
/**
- * Flag for use with {@link #enableReaderMode(Activity, int)}.
+ * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
* <p>
* Setting this flag enables polling for Kovio technology.
*/
public static final int FLAG_READER_KOVIO = 0x10;
/**
- * Flag for use with {@link #enableReaderMode(Activity, int)}.
+ * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
* <p>
* Setting this flag allows the caller to prevent the
* platform from performing an NDEF check on the tags it
@@ -239,6 +240,23 @@ public final class NfcAdapter {
*/
public static final int FLAG_READER_SKIP_NDEF_CHECK = 0x80;
+ /**
+ * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
+ * <p>
+ * Setting this flag allows the caller to prevent the
+ * platform from playing sounds when it discovers a tag.
+ */
+ public static final int FLAG_READER_NO_PLATFORM_SOUNDS = 0x100;
+
+ /**
+ * Int Extra for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
+ * <p>
+ * Setting this integer extra allows the calling application to specify
+ * the delay that the platform will use for performing presence checks
+ * on any discovered tag.
+ */
+ public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
+
/** @hide */
public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 0x1;
@@ -291,6 +309,14 @@ public final class NfcAdapter {
final Context mContext;
/**
+ * A callback to be invoked when the system has found a tag in
+ * reader mode.
+ */
+ public interface ReaderCallback {
+ public void onTagDiscovered(Tag tag);
+ }
+
+ /**
* A callback to be invoked when the system successfully delivers your {@link NdefMessage}
* to another device.
* @see #setOnNdefPushCompleteCallback
@@ -1167,19 +1193,18 @@ public final class NfcAdapter {
* {@link Ndef} tag technology from being enumerated on the tag, and that
* NDEF-based tag dispatch will not be functional.
*
- * <p>It is recommended to combine this method with
- * {@link #enableForegroundDispatch(Activity, PendingIntent, IntentFilter[], String[][])
- * to ensure that tags are delivered to this activity.
- *
* <p>For interacting with tags that are emulated on another Android device
* using Android's host-based card-emulation, the recommended flags are
* {@link #FLAG_READER_NFC_A} and {@link #FLAG_READER_SKIP_NDEF_CHECK}.
*
* @param activity the Activity that requests the adapter to be in reader mode
+ * @param callback the callback to be called when a tag is discovered
* @param flags Flags indicating poll technologies and other optional parameters
+ * @param extras Additional extras for configuring reader mode.
*/
- public void enableReaderMode(Activity activity, int flags) {
- mNfcActivityManager.enableReaderMode(activity, flags);
+ public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,
+ Bundle extras) {
+ mNfcActivityManager.enableReaderMode(activity, callback, flags, extras);
}
/**
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index b83911a..41c6603 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -105,8 +105,12 @@ public final class ApduServiceInfo implements Parcelable {
if (onHost) {
parser = si.loadXmlMetaData(pm, HostApduService.SERVICE_META_DATA);
if (parser == null) {
- throw new XmlPullParserException("No " + HostApduService.SERVICE_META_DATA +
- " meta-data");
+ Log.d(TAG, "Didn't find service meta-data, trying legacy.");
+ parser = si.loadXmlMetaData(pm, HostApduService.OLD_SERVICE_META_DATA);
+ if (parser == null) {
+ throw new XmlPullParserException("No " + HostApduService.SERVICE_META_DATA +
+ " meta-data");
+ }
}
} else {
parser = si.loadXmlMetaData(pm, OffHostApduService.SERVICE_META_DATA);
@@ -170,12 +174,12 @@ public final class ApduServiceInfo implements Parcelable {
com.android.internal.R.styleable.AidGroup_description);
String groupCategory = groupAttrs.getString(
com.android.internal.R.styleable.AidGroup_category);
- if (!CardEmulationManager.CATEGORY_PAYMENT.equals(groupCategory)) {
- groupCategory = CardEmulationManager.CATEGORY_OTHER;
+ if (!CardEmulation.CATEGORY_PAYMENT.equals(groupCategory)) {
+ groupCategory = CardEmulation.CATEGORY_OTHER;
}
currentGroup = mCategoryToGroup.get(groupCategory);
if (currentGroup != null) {
- if (!CardEmulationManager.CATEGORY_OTHER.equals(groupCategory)) {
+ if (!CardEmulation.CATEGORY_OTHER.equals(groupCategory)) {
Log.e(TAG, "Not allowing multiple aid-groups in the " +
groupCategory + " category");
currentGroup = null;
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
new file mode 100644
index 0000000..3cd7863
--- /dev/null
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -0,0 +1,343 @@
+/*
+ * 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.nfc.cardemulation;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.app.ActivityThread;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.nfc.INfcCardEmulation;
+import android.nfc.NfcAdapter;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.List;
+
+public final class CardEmulation {
+ static final String TAG = "CardEmulation";
+
+ /**
+ * Activity action: ask the user to change the default
+ * card emulation service for a certain category. This will
+ * show a dialog that asks the user whether he wants to
+ * replace the current default service with the service
+ * identified with the ComponentName specified in
+ * {@link #EXTRA_SERVICE_COMPONENT}, for the category
+ * specified in {@link #EXTRA_CATEGORY}
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CHANGE_DEFAULT =
+ "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
+
+ /**
+ * The category extra for {@link #ACTION_CHANGE_DEFAULT}
+ *
+ * @see #ACTION_CHANGE_DEFAULT
+ */
+ public static final String EXTRA_CATEGORY = "category";
+
+ /**
+ * The ComponentName object passed in as a parcelable
+ * extra for {@link #ACTION_CHANGE_DEFAULT}
+ *
+ * @see #ACTION_CHANGE_DEFAULT
+ */
+ public static final String EXTRA_SERVICE_COMPONENT = "component";
+
+ /**
+ * The payment category can be used to indicate that an AID
+ * represents a payment application.
+ */
+ public static final String CATEGORY_PAYMENT = "payment";
+
+ /**
+ * If an AID group does not contain a category, or the
+ * specified category is not defined by the platform version
+ * that is parsing the AID group, all AIDs in the group will
+ * automatically be categorized under the {@link #CATEGORY_OTHER}
+ * category.
+ */
+ public static final String CATEGORY_OTHER = "other";
+
+ /**
+ * Return value for {@link #getSelectionModeForCategory(String)}.
+ *
+ * <p>In this mode, the user has set a default service for this
+ * AID category. If a remote reader selects any of the AIDs
+ * that the default service has registered in this category,
+ * that service will automatically be bound to to handle
+ * the transaction.
+ *
+ * <p>There are still cases where a service that is
+ * not the default for a category can selected:
+ * <p>
+ * If a remote reader selects an AID in this category
+ * that is not handled by the default service, and there is a set
+ * of other services {S} that do handle this AID, the
+ * user is asked if he wants to use any of the services in
+ * {S} instead.
+ * <p>
+ * As a special case, if the size of {S} is one, containing a single service X,
+ * and all AIDs X has registered in this category are not
+ * registered by any other service, then X will be
+ * selected automatically without asking the user.
+ * <p>Example:
+ * <ul>
+ * <li>Service A registers AIDs "1", "2" and "3" in the category
+ * <li>Service B registers AIDs "3" and "4" in the category
+ * <li>Service C registers AIDs "5" and "6" in the category
+ * </ul>
+ * In this case, the following will happen when service A
+ * is the default:
+ * <ul>
+ * <li>Reader selects AID "1", "2" or "3": service A is invoked automatically
+ * <li>Reader selects AID "4": the user is asked to confirm he
+ * wants to use service B, because its AIDs overlap with service A.
+ * <li>Reader selects AID "5" or "6": service C is invoked automatically,
+ * because all AIDs it has asked for are only registered by C,
+ * and there is no overlap.
+ * </ul>
+ *
+ */
+ public static final int SELECTION_MODE_PREFER_DEFAULT = 0;
+
+ /**
+ * Return value for {@link #getSelectionModeForCategory(String)}.
+ *
+ * <p>In this mode, whenever an AID of this category is selected,
+ * the user is asked which service he wants to use to handle
+ * the transaction, even if there is only one matching service.
+ */
+ public static final int SELECTION_MODE_ALWAYS_ASK = 1;
+
+ /**
+ * Return value for {@link #getSelectionModeForCategory(String)}.
+ *
+ * <p>In this mode, the user will only be asked to select a service
+ * if the selected AID has been registered by multiple applications.
+ */
+ public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2;
+
+ static boolean sIsInitialized = false;
+ static HashMap<Context, CardEmulation> sCardEmus = new HashMap();
+ static INfcCardEmulation sService;
+
+ final Context mContext;
+
+ private CardEmulation(Context context, INfcCardEmulation service) {
+ mContext = context.getApplicationContext();
+ sService = service;
+ }
+
+ public static synchronized CardEmulation getInstance(NfcAdapter adapter) {
+ if (adapter == null) throw new NullPointerException("NfcAdapter is null");
+ Context context = adapter.getContext();
+ if (context == null) {
+ Log.e(TAG, "NfcAdapter context is null.");
+ throw new UnsupportedOperationException();
+ }
+ if (!sIsInitialized) {
+ IPackageManager pm = ActivityThread.getPackageManager();
+ if (pm == null) {
+ Log.e(TAG, "Cannot get PackageManager");
+ throw new UnsupportedOperationException();
+ }
+ try {
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
+ Log.e(TAG, "This device does not support card emulation");
+ throw new UnsupportedOperationException();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "PackageManager query failed.");
+ throw new UnsupportedOperationException();
+ }
+ sIsInitialized = true;
+ }
+ CardEmulation manager = sCardEmus.get(context);
+ if (manager == null) {
+ // Get card emu service
+ INfcCardEmulation service = adapter.getCardEmulationService();
+ manager = new CardEmulation(context, service);
+ sCardEmus.put(context, manager);
+ }
+ return manager;
+ }
+
+ /**
+ * Allows an application to query whether a service is currently
+ * the default service to handle a card emulation category.
+ *
+ * <p>Note that if {@link #getSelectionModeForCategory(String)}
+ * returns {@link #SELECTION_MODE_ALWAYS_ASK}, this method will always
+ * return false.
+ *
+ * @param service The ComponentName of the service
+ * @param category The category
+ * @return whether service is currently the default service for the category.
+ */
+ public boolean isDefaultServiceForCategory(ComponentName service, String category) {
+ try {
+ return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service, category);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service,
+ category);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ *
+ * Allows an application to query whether a service is currently
+ * the default handler for a specified ISO7816-4 Application ID.
+ *
+ * @param service The ComponentName of the service
+ * @param aid The ISO7816-4 Application ID
+ * @return
+ */
+ public boolean isDefaultServiceForAid(ComponentName service, String aid) {
+ try {
+ return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Returns the application selection mode for the passed in category.
+ * Valid return values are:
+ * <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default
+ * application for this category, which will be preferred.
+ * <p>{@link #SELECTION_MODE_ALWAYS_ASK} the user has requested to be asked
+ * every time what app he would like to use in this category.
+ * <p>{@link #SELECTION_MODE_ASK_IF_CONFLICT} the user will only be asked
+ * to pick a service if there is a conflict.
+ * @param category The category, for example {@link #CATEGORY_PAYMENT}
+ * @return
+ */
+ public int getSelectionModeForCategory(String category) {
+ if (CATEGORY_PAYMENT.equals(category)) {
+ String defaultComponent = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
+ if (defaultComponent != null) {
+ return SELECTION_MODE_PREFER_DEFAULT;
+ } else {
+ return SELECTION_MODE_ALWAYS_ASK;
+ }
+ } else {
+ // All other categories are in "only ask if conflict" mode
+ return SELECTION_MODE_ASK_IF_CONFLICT;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public boolean setDefaultServiceForCategory(ComponentName service, String category) {
+ try {
+ return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service, category);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service,
+ category);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public boolean setDefaultForNextTap(ComponentName service) {
+ try {
+ return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+ /**
+ * @hide
+ */
+ public List<ApduServiceInfo> getServices(String category) {
+ try {
+ return sService.getServices(UserHandle.myUserId(), category);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return null;
+ }
+ try {
+ return sService.getServices(UserHandle.myUserId(), category);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return null;
+ }
+ }
+ }
+
+ void recoverService() {
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+ sService = adapter.getCardEmulationService();
+ }
+}
diff --git a/core/java/android/nfc/cardemulation/CardEmulationManager.java b/core/java/android/nfc/cardemulation/CardEmulationManager.java
index 9d60c73..124ea1c 100644
--- a/core/java/android/nfc/cardemulation/CardEmulationManager.java
+++ b/core/java/android/nfc/cardemulation/CardEmulationManager.java
@@ -33,6 +33,10 @@ import android.util.Log;
import java.util.HashMap;
import java.util.List;
+/**
+ * TODO Remove when calling .apks are upgraded
+ * @hide
+ */
public final class CardEmulationManager {
static final String TAG = "CardEmulationManager";
diff --git a/core/java/android/nfc/cardemulation/HostApduService.java b/core/java/android/nfc/cardemulation/HostApduService.java
index ae94b2f..174acc0 100644
--- a/core/java/android/nfc/cardemulation/HostApduService.java
+++ b/core/java/android/nfc/cardemulation/HostApduService.java
@@ -40,13 +40,31 @@ public abstract class HostApduService extends Service {
*/
@SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE =
+ "android.nfc.cardemulation.action.HOST_APDU_SERVICE";
+
+ /**
+ * The name of the meta-data element that contains
+ * more information about this service.
+ */
+ public static final String SERVICE_META_DATA =
+ "android.nfc.cardemulation.host_apdu_service";
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ * TODO Remove
+ * @hide
+ */
+ public static final String OLD_SERVICE_INTERFACE =
"android.nfc.HostApduService";
/**
* The name of the meta-data element that contains
* more information about this service.
+ *
+ * TODO Remove
+ * @hide
*/
- public static final String SERVICE_META_DATA = "android.nfc.HostApduService";
+ public static final String OLD_SERVICE_META_DATA = "android.nfc.HostApduService";
/**
* Reason for {@link #onDeactivated(int)}.
@@ -63,7 +81,7 @@ public abstract class HostApduService extends Service {
* currently active on the logical channel).
*
* <p>Note that this next AID may still be resolved to this
- * service, in which case {@link #processCommandApdu(byte[], int)}
+ * service, in which case {@link #processCommandApdu(byte[], Bundle)}
* will be called again.
*/
public static final int DEACTIVATION_DESELECTED = 1;
@@ -131,7 +149,7 @@ public abstract class HostApduService extends Service {
byte[] apdu = dataBundle.getByteArray(KEY_DATA);
if (apdu != null) {
- byte[] responseApdu = processCommandApdu(apdu, 0);
+ byte[] responseApdu = processCommandApdu(apdu, null);
if (responseApdu != null) {
if (mNfcService == null) {
Log.e(TAG, "Response not sent; service was deactivated.");
@@ -230,7 +248,7 @@ public abstract class HostApduService extends Service {
* transaction.
*
* <p>Note: this method may be called anywhere between
- * the first {@link #processCommandApdu(byte[], int)}
+ * the first {@link #processCommandApdu(byte[], Bundle)}
* call and a {@link #onDeactivated(int)} call.
*/
public final void notifyUnhandled() {
@@ -290,8 +308,11 @@ public abstract class HostApduService extends Service {
* @param flags
* @return a byte-array containing the response APDU, or null if no
* response APDU can be sent at this point.
+ * @hide
*/
- public abstract byte[] processCommandApdu(byte[] commandApdu, int flags);
+ public byte[] processCommandApdu(byte[] commandApdu, int flags) {
+ return null;
+ }
/**
* This method will be called in two possible scenarios:
diff --git a/core/java/android/nfc/cardemulation/OffHostApduService.java b/core/java/android/nfc/cardemulation/OffHostApduService.java
index 79599db..15f63f9 100644
--- a/core/java/android/nfc/cardemulation/OffHostApduService.java
+++ b/core/java/android/nfc/cardemulation/OffHostApduService.java
@@ -42,13 +42,14 @@ public abstract class OffHostApduService extends Service {
*/
@SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE =
- "android.nfc.OffHostApduService";
+ "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE";
/**
* The name of the meta-data element that contains
* more information about this service.
*/
- public static final String SERVICE_META_DATA = "android.nfc.OffHostApduService";
+ public static final String SERVICE_META_DATA =
+ "android.nfc.cardemulation.off_host_apdu_service";
/**
* The Android platform itself will not bind to this service,
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 38ffb96..dbaa325 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2380,22 +2380,25 @@ public abstract class BatteryStats implements Parcelable {
@SuppressWarnings("unused")
public void dumpCheckinLocked(
- PrintWriter pw, List<ApplicationInfo> apps, boolean isUnpluggedOnly) {
+ PrintWriter pw, List<ApplicationInfo> apps, boolean isUnpluggedOnly,
+ boolean includeHistory) {
prepareForDumpLocked();
long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
- final HistoryItem rec = new HistoryItem();
- if (startIteratingHistoryLocked()) {
- HistoryPrinter hprinter = new HistoryPrinter();
- while (getNextHistoryLocked(rec)) {
- pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
- pw.print(0); pw.print(',');
- pw.print(HISTORY_DATA); pw.print(',');
- hprinter.printNextItemCheckin(pw, rec, now);
- pw.println();
+ if (includeHistory) {
+ final HistoryItem rec = new HistoryItem();
+ if (startIteratingHistoryLocked()) {
+ HistoryPrinter hprinter = new HistoryPrinter();
+ while (getNextHistoryLocked(rec)) {
+ pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+ pw.print(0); pw.print(',');
+ pw.print(HISTORY_DATA); pw.print(',');
+ hprinter.printNextItemCheckin(pw, rec, now);
+ pw.println();
+ }
+ finishIteratingHistoryLocked();
}
- finishIteratingHistoryLocked();
}
if (apps != null) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 83426ae..10b9765 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -357,7 +357,18 @@ public class UserManager {
* @param restrictionKey the string key representing the restriction
*/
public boolean hasUserRestriction(String restrictionKey) {
- return getUserRestrictions().getBoolean(restrictionKey, false);
+ return hasUserRestriction(restrictionKey, Process.myUserHandle());
+ }
+
+ /**
+ * @hide
+ * Returns whether the given user has been disallowed from performing certain actions
+ * or setting certain settings.
+ * @param restrictionKey the string key representing the restriction
+ * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+ */
+ public boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) {
+ return getUserRestrictions(userHandle).getBoolean(restrictionKey, false);
}
/**
diff --git a/core/java/android/print/PrintDocumentAdapter.java b/core/java/android/print/PrintDocumentAdapter.java
index 33b4aad..8a59b6b 100644
--- a/core/java/android/print/PrintDocumentAdapter.java
+++ b/core/java/android/print/PrintDocumentAdapter.java
@@ -55,7 +55,7 @@ import java.util.List;
* The APIs defined in this class are designed to enable doing part or all
* of the work on an arbitrary thread. For example, if the printed content
* does not depend on the UI state, i.e. on what is shown on the screen, then
- * you can off load the entire work on a dedicated thread, thus making your
+ * you can offload the entire work on a dedicated thread, thus making your
* application interactive while the print work is being performed.
* </p>
* <p>
@@ -99,6 +99,7 @@ public abstract class PrintDocumentAdapter {
* the last argument <code>true</code> or <code>false</code> depending on
* whether the layout changed the content or not, respectively; and {@link
* LayoutResultCallback#onLayoutFailed(CharSequence)}, if an error occurred.
+ * Note that you must call one of the methods of the given callback.
* </p>
* <p>
* When doing a layout you may satisfy some of the constraints in the print
@@ -147,13 +148,14 @@ public abstract class PrintDocumentAdapter {
/**
* Called when specific pages of the content should be written in the
- * from of a PDF file to the given file descriptor. This method is invoked
+ * form of a PDF file to the given file descriptor. This method is invoked
* on the main thread.
*<p>
* After you are done writing, you should close the file descriptor and
- * invoke {@link WriteResultCallback #onWriteFinished(List)}, if writing
+ * invoke {@link WriteResultCallback #onWriteFinished(PageRange[]), if writing
* completed successfully; or {@link WriteResultCallback#onWriteFailed(
- * CharSequence)}, if an error occurred.
+ * CharSequence)}, if an error occurred. Note that you must call one of
+ * the methods of the given callback.
* </p>
* <p>
* <strong>Note:</strong> If the printed content is large, it is a good
diff --git a/core/java/android/print/PrintFileDocumentAdapter.java b/core/java/android/print/PrintFileDocumentAdapter.java
index b905396..c3a23a5 100644
--- a/core/java/android/print/PrintFileDocumentAdapter.java
+++ b/core/java/android/print/PrintFileDocumentAdapter.java
@@ -36,10 +36,11 @@ import java.io.InputStream;
import java.io.OutputStream;
/**
- * Adapter for printing files. This class could be useful if you
+ * Adapter for printing PDF files. This class could be useful if you
* want to print a file and intercept when the system is ready
- * spooling the data, so you can deleted the file if it is a
- * temporary one.
+ * spooling the data, so you can delete the file if it is a
+ * temporary one. To achieve this one must override {@link #onFinish()}
+ * and delete the file yourself.
*/
public class PrintFileDocumentAdapter extends PrintDocumentAdapter {
@@ -57,7 +58,7 @@ public class PrintFileDocumentAdapter extends PrintDocumentAdapter {
* Constructor.
*
* @param context Context for accessing resources.
- * @param file The file to print.
+ * @param file The PDF file to print.
* @param documentInfo The information about the printed file.
*/
public PrintFileDocumentAdapter(Context context, File file,
diff --git a/core/java/android/printservice/PrinterDiscoverySession.java b/core/java/android/printservice/PrinterDiscoverySession.java
index 1f86ecc..6464cc1 100644
--- a/core/java/android/printservice/PrinterDiscoverySession.java
+++ b/core/java/android/printservice/PrinterDiscoverySession.java
@@ -80,7 +80,7 @@ import java.util.List;
public abstract class PrinterDiscoverySession {
private static final String LOG_TAG = "PrinterDiscoverySession";
- private static final int MAX_ITEMS_PER_CALLBACK = 100;
+ private static final int MAX_ITEMS_PER_CALLBACK = 50;
private static int sIdCounter = 0;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 31817cc..eb5430c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3708,13 +3708,22 @@ public final class Settings {
public static final String LONG_PRESS_TIMEOUT = "long_press_timeout";
/**
- * List of the enabled print providers.
+ * List of the enabled print services.
* @hide
*/
public static final String ENABLED_PRINT_SERVICES =
"enabled_print_services";
/**
+ * List of the system print services we enabled on first boot. On
+ * first boot we enable all system, i.e. bundled print services,
+ * once, so they work out-of-the-box.
+ * @hide
+ */
+ public static final String ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES =
+ "enabled_on_first_boot_system_print_services";
+
+ /**
* Setting to always use the default text-to-speech settings regardless
* of the application settings.
* 1 = override application settings,
@@ -5098,6 +5107,21 @@ public final class Settings {
"wifi_display_certification_on";
/**
+ * WPS Configuration method used by Wifi display, this setting only
+ * takes effect when WIFI_DISPLAY_CERTIFICATION_ON is 1 (enabled).
+ *
+ * Possible values are:
+ *
+ * WpsInfo.INVALID: use default WPS method chosen by framework
+ * WpsInfo.PBC : use Push button
+ * WpsInfo.KEYPAD : use Keypad
+ * WpsInfo.DISPLAY: use Display
+ * @hide
+ */
+ public static final String WIFI_DISPLAY_WPS_CONFIG =
+ "wifi_display_wps_config";
+
+ /**
* Whether to notify the user of open networks.
* <p>
* If not connected and the scan results have an open network, we will
@@ -5259,6 +5283,13 @@ public final class Settings {
"data_stall_alarm_aggressive_delay_in_ms";
/**
+ * The number of milliseconds to allow the provisioning apn to remain active
+ * @hide
+ */
+ public static final String PROVISIONING_APN_ALARM_DELAY_IN_MS =
+ "provisioning_apn_alarm_delay_in_ms";
+
+ /**
* The interval in milliseconds at which to check gprs registration
* after the first registration mismatch of gprs and voice service,
* to detect possible data network registration problems.
diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java
index 3d75dc8..bf8d4e5 100644
--- a/core/java/android/security/IKeystoreService.java
+++ b/core/java/android/security/IKeystoreService.java
@@ -244,7 +244,8 @@ public interface IKeystoreService extends IInterface {
return _result;
}
- public int generate(String name, int uid, int flags) throws RemoteException {
+ public int generate(String name, int uid, int keyType, int keySize, int flags,
+ byte[][] args) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
@@ -252,7 +253,17 @@ public interface IKeystoreService extends IInterface {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
_data.writeInt(uid);
+ _data.writeInt(keyType);
+ _data.writeInt(keySize);
_data.writeInt(flags);
+ if (args == null) {
+ _data.writeInt(0);
+ } else {
+ _data.writeInt(args.length);
+ for (int i = 0; i < args.length; i++) {
+ _data.writeByteArray(args[i]);
+ }
+ }
mRemote.transact(Stub.TRANSACTION_generate, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
@@ -560,7 +571,8 @@ public interface IKeystoreService extends IInterface {
public int zero() throws RemoteException;
- public int generate(String name, int uid, int flags) throws RemoteException;
+ public int generate(String name, int uid, int keyType, int keySize, int flags, byte[][] args)
+ throws RemoteException;
public int import_key(String name, byte[] data, int uid, int flags) throws RemoteException;
diff --git a/core/java/android/view/transition/AutoTransition.java b/core/java/android/transition/AutoTransition.java
index 7ddac7e..6e46021 100644
--- a/core/java/android/view/transition/AutoTransition.java
+++ b/core/java/android/transition/AutoTransition.java
@@ -14,22 +14,28 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
/**
* Utility class for creating a default transition that automatically fades,
* moves, and resizes views during a scene change.
+ *
+ * <p>An AutoTransition can be described in a resource file by using the
+ * tag <code>autoTransition</code>, along with the other standard
+ * attributes of {@link android.R.styleable#Transition}.</p>
*/
-public class AutoTransition extends TransitionGroup {
+public class AutoTransition extends TransitionSet {
/**
- * Constructs an AutoTransition object, which is a TransitionGroup which
+ * Constructs an AutoTransition object, which is a TransitionSet which
* first fades out disappearing targets, then moves and resizes existing
* targets, and finally fades in appearing targets.
*
*/
public AutoTransition() {
- setOrdering(SEQUENTIALLY);
- addTransitions(new Fade(Fade.OUT), new Move(), new Fade(Fade.IN));
+ setOrdering(ORDERING_SEQUENTIAL);
+ addTransition(new Fade(Fade.OUT)).
+ addTransition(new ChangeBounds()).
+ addTransition(new Fade(Fade.IN));
}
}
diff --git a/core/java/android/view/transition/Move.java b/core/java/android/transition/ChangeBounds.java
index fda0cd2..8053bff 100644
--- a/core/java/android/view/transition/Move.java
+++ b/core/java/android/transition/ChangeBounds.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -33,13 +33,17 @@ import java.util.Map;
/**
* This transition captures the layout bounds of target views before and after
* the scene change and animates those changes during the transition.
+ *
+ * <p>A ChangeBounds transition can be described in a resource file by using the
+ * tag <code>changeBounds</code>, along with the other standard
+ * attributes of {@link android.R.styleable#Transition}.</p>
*/
-public class Move extends Transition {
+public class ChangeBounds extends Transition {
- private static final String PROPNAME_BOUNDS = "android:move:bounds";
- private static final String PROPNAME_PARENT = "android:move:parent";
- private static final String PROPNAME_WINDOW_X = "android:move:windowX";
- private static final String PROPNAME_WINDOW_Y = "android:move:windowY";
+ private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds";
+ private static final String PROPNAME_PARENT = "android:changeBounds:parent";
+ private static final String PROPNAME_WINDOW_X = "android:changeBounds:windowX";
+ private static final String PROPNAME_WINDOW_Y = "android:changeBounds:windowY";
private static final String[] sTransitionProperties = {
PROPNAME_BOUNDS,
PROPNAME_PARENT,
@@ -50,7 +54,7 @@ public class Move extends Transition {
int[] tempLocation = new int[2];
boolean mResizeClip = false;
boolean mReparent = false;
- private static final String LOG_TAG = "Move";
+ private static final String LOG_TAG = "ChangeBounds";
private static RectEvaluator sRectEvaluator = new RectEvaluator();
@@ -64,7 +68,7 @@ public class Move extends Transition {
}
/**
- * Setting this flag tells Move to track the before/after parent
+ * Setting this flag tells ChangeBounds to track the before/after parent
* of every view using this transition. The flag is not enabled by
* default because it requires the parent instances to be the same
* in the two scenes or else all parents must use ids to allow
@@ -77,8 +81,7 @@ public class Move extends Transition {
mReparent = reparent;
}
- @Override
- protected void captureValues(TransitionValues values, boolean start) {
+ private void captureValues(TransitionValues values) {
View view = values.view;
values.values.put(PROPNAME_BOUNDS, new Rect(view.getLeft(), view.getTop(),
view.getRight(), view.getBottom()));
@@ -89,7 +92,17 @@ public class Move extends Transition {
}
@Override
- protected Animator play(final ViewGroup sceneRoot, TransitionValues startValues,
+ 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) {
return null;
@@ -105,7 +118,7 @@ public class Move extends Transition {
boolean parentsEqual = (startParent == endParent) ||
(startParent.getId() == endParent.getId());
// TODO: Might want reparenting to be separate/subclass transition, or at least
- // triggered by a property on Move. Otherwise, we're forcing the requirement that
+ // triggered by a property on ChangeBounds. Otherwise, we're forcing the requirement that
// all parents in layouts have IDs to avoid layout-inflation resulting in a side-effect
// of reparenting the views.
if (!mReparent || parentsEqual) {
diff --git a/core/java/android/view/transition/Crossfade.java b/core/java/android/transition/Crossfade.java
index 18bb57f..69ce872 100644
--- a/core/java/android/view/transition/Crossfade.java
+++ b/core/java/android/transition/Crossfade.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -24,10 +24,8 @@ import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.SurfaceView;
import android.view.TextureView;
@@ -43,6 +41,8 @@ import java.util.Map;
*
* <p>Note: This transition is not compatible with {@link TextureView}
* or {@link SurfaceView}.</p>
+ *
+ * @hide
*/
public class Crossfade extends Transition {
// TODO: Add a hook that lets a Transition call user code to query whether it should run on
@@ -121,12 +121,19 @@ public class Crossfade extends Transition {
* @param fadeBehavior The type of fading animation to use when this
* transition is run.
*/
- public void setFadeBehavior(int fadeBehavior) {
+ public Crossfade setFadeBehavior(int fadeBehavior) {
if (fadeBehavior >= FADE_BEHAVIOR_CROSSFADE && fadeBehavior <= FADE_BEHAVIOR_OUT_IN) {
mFadeBehavior = fadeBehavior;
}
+ return this;
}
+ /**
+ * Returns the fading behavior of the animation.
+ *
+ * @return This crossfade object.
+ * @see #setFadeBehavior(int)
+ */
public int getFadeBehavior() {
return mFadeBehavior;
}
@@ -139,18 +146,25 @@ public class Crossfade extends Transition {
* @param resizeBehavior The type of resizing behavior to use when this
* transition is run.
*/
- public void setResizeBehavior(int resizeBehavior) {
+ public Crossfade setResizeBehavior(int resizeBehavior) {
if (resizeBehavior >= RESIZE_BEHAVIOR_NONE && resizeBehavior <= RESIZE_BEHAVIOR_SCALE) {
mResizeBehavior = resizeBehavior;
}
+ return this;
}
+ /**
+ * Returns the resizing behavior of the animation.
+ *
+ * @return This crossfade object.
+ * @see #setResizeBehavior(int)
+ */
public int getResizeBehavior() {
return mResizeBehavior;
}
@Override
- protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
@@ -243,18 +257,16 @@ public class Crossfade extends Transition {
}
}
- @Override
- protected void captureValues(TransitionValues values, boolean start) {
- View view = values.view;
+ private void captureValues(TransitionValues transitionValues) {
+ View view = transitionValues.view;
Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
if (mFadeBehavior != FADE_BEHAVIOR_REVEAL) {
bounds.offset(view.getLeft(), view.getTop());
}
- values.values.put(PROPNAME_BOUNDS, bounds);
+ transitionValues.values.put(PROPNAME_BOUNDS, bounds);
if (Transition.DBG) {
- Log.d(LOG_TAG, "Captured bounds " + values.values.get(PROPNAME_BOUNDS) + ": start = " +
- start);
+ Log.d(LOG_TAG, "Captured bounds " + transitionValues.values.get(PROPNAME_BOUNDS));
}
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
Bitmap.Config.ARGB_8888);
@@ -264,12 +276,21 @@ public class Crossfade extends Transition {
Canvas c = new Canvas(bitmap);
view.draw(c);
}
- values.values.put(PROPNAME_BITMAP, bitmap);
+ transitionValues.values.put(PROPNAME_BITMAP, bitmap);
// TODO: I don't have resources, can't call the non-deprecated method?
BitmapDrawable drawable = new BitmapDrawable(bitmap);
// TODO: lrtb will be wrong if the view has transXY set
drawable.setBounds(bounds);
- values.values.put(PROPNAME_DRAWABLE, drawable);
+ transitionValues.values.put(PROPNAME_DRAWABLE, drawable);
}
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
}
diff --git a/core/java/android/view/transition/Fade.java b/core/java/android/transition/Fade.java
index 45c21d8..12e0d73 100644
--- a/core/java/android/view/transition/Fade.java
+++ b/core/java/android/transition/Fade.java
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -30,6 +29,12 @@ import android.view.ViewGroup;
* or non-visible. Visibility is determined by both the
* {@link View#setVisibility(int)} state of the view as well as whether it
* is parented in the current view hierarchy.
+ *
+ * <p>A Fade transition can be described in a resource file by using the
+ * tag <code>fade</code>, along with the standard
+ * attributes of {@link android.R.styleable#Fade} and
+ * {@link android.R.styleable#Transition}.</p>
+
*/
public class Fade extends Visibility {
@@ -93,21 +98,31 @@ public class Fade extends Visibility {
return anim;
}
- @Override
- protected void captureValues(TransitionValues values, boolean start) {
- super.captureValues(values, start);
- float alpha = values.view.getAlpha();
- values.values.put(PROPNAME_ALPHA, alpha);
+ private void captureValues(TransitionValues transitionValues) {
+ float alpha = transitionValues.view.getAlpha();
+ transitionValues.values.put(PROPNAME_ALPHA, alpha);
int[] loc = new int[2];
- values.view.getLocationOnScreen(loc);
- values.values.put(PROPNAME_SCREEN_X, loc[0]);
- values.values.put(PROPNAME_SCREEN_Y, loc[1]);
+ transitionValues.view.getLocationOnScreen(loc);
+ transitionValues.values.put(PROPNAME_SCREEN_X, loc[0]);
+ transitionValues.values.put(PROPNAME_SCREEN_Y, loc[1]);
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ captureValues(transitionValues);
+ }
+
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
}
@Override
- protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
TransitionValues endValues) {
- Animator animator = super.play(sceneRoot, startValues, endValues);
+ Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
if (animator == null && startValues != null && endValues != null) {
boolean endVisible = isVisible(endValues);
final View endView = endValues.view;
@@ -122,13 +137,18 @@ public class Fade extends Visibility {
}
@Override
- protected Animator appear(ViewGroup sceneRoot,
+ public Animator onAppear(ViewGroup sceneRoot,
TransitionValues startValues, int startVisibility,
TransitionValues endValues, int endVisibility) {
if ((mFadingMode & IN) != IN || endValues == null) {
return null;
}
final View endView = endValues.view;
+ if (DBG) {
+ View startView = (startValues != null) ? startValues.view : null;
+ Log.d(LOG_TAG, "Fade.onDisappear: startView, startVis, endView, endVis = " +
+ startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
+ }
// if alpha < 1, just fade it in from the current value
if (endView.getAlpha() == 1.0f) {
endView.setAlpha(0);
@@ -137,7 +157,7 @@ public class Fade extends Visibility {
}
@Override
- protected Animator disappear(ViewGroup sceneRoot,
+ public Animator onDisappear(ViewGroup sceneRoot,
TransitionValues startValues, int startVisibility,
TransitionValues endValues, int endVisibility) {
if ((mFadingMode & OUT) != OUT) {
@@ -147,7 +167,7 @@ public class Fade extends Visibility {
View startView = (startValues != null) ? startValues.view : null;
View endView = (endValues != null) ? endValues.view : null;
if (DBG) {
- Log.d(LOG_TAG, "Fade.predisappear: startView, startVis, endView, endVis = " +
+ Log.d(LOG_TAG, "Fade.onDisappear: startView, startVis, endView, endVis = " +
startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
}
View overlayView = null;
diff --git a/core/java/android/view/transition/Recolor.java b/core/java/android/transition/Recolor.java
index e4858c4..70111d1 100644
--- a/core/java/android/view/transition/Recolor.java
+++ b/core/java/android/transition/Recolor.java
@@ -14,20 +14,17 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
import android.animation.Animator;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.util.ArrayMap;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-import java.util.Map;
-
/**
* This transition tracks changes during scene changes to the
* {@link View#setBackground(android.graphics.drawable.Drawable) background}
@@ -36,22 +33,34 @@ import java.util.Map;
* {@link TextView#setTextColor(android.content.res.ColorStateList)
* color} of the text for target TextViews. If the color changes between
* scenes, the color change is animated.
+ *
+ * @hide
*/
public class Recolor extends Transition {
private static final String PROPNAME_BACKGROUND = "android:recolor:background";
private static final String PROPNAME_TEXT_COLOR = "android:recolor:textColor";
- @Override
- protected void captureValues(TransitionValues values, boolean start) {
- values.values.put(PROPNAME_BACKGROUND, values.view.getBackground());
- if (values.view instanceof TextView) {
- values.values.put(PROPNAME_TEXT_COLOR, ((TextView)values.view).getCurrentTextColor());
+ private void captureValues(TransitionValues transitionValues) {
+ transitionValues.values.put(PROPNAME_BACKGROUND, transitionValues.view.getBackground());
+ if (transitionValues.view instanceof TextView) {
+ transitionValues.values.put(PROPNAME_TEXT_COLOR,
+ ((TextView)transitionValues.view).getCurrentTextColor());
}
}
@Override
- protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ public void captureStartValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
diff --git a/core/java/android/view/transition/Rotate.java b/core/java/android/transition/Rotate.java
index d35a6dc7..ad1720c 100644
--- a/core/java/android/view/transition/Rotate.java
+++ b/core/java/android/transition/Rotate.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
import android.animation.Animator;
import android.animation.ObjectAnimator;
@@ -24,18 +24,25 @@ import android.view.ViewGroup;
/**
* This transition captures the rotation property of targets before and after
* the scene change and animates any changes.
+ *
+ * @hide
*/
public class Rotate extends Transition {
private static final String PROPNAME_ROTATION = "android:rotate:rotation";
@Override
- protected void captureValues(TransitionValues values, boolean start) {
- values.values.put(PROPNAME_ROTATION, values.view.getRotation());
+ public void captureStartValues(TransitionValues transitionValues) {
+ transitionValues.values.put(PROPNAME_ROTATION, transitionValues.view.getRotation());
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ transitionValues.values.put(PROPNAME_ROTATION, transitionValues.view.getRotation());
}
@Override
- protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
diff --git a/core/java/android/view/transition/Scene.java b/core/java/android/transition/Scene.java
index cf3eadb..f81eeef 100644
--- a/core/java/android/view/transition/Scene.java
+++ b/core/java/android/transition/Scene.java
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
import android.content.Context;
+import android.util.SparseArray;
import android.view.LayoutInflater;
+import android.view.View;
import android.view.ViewGroup;
/**
@@ -34,6 +36,37 @@ public final class Scene {
private ViewGroup mSceneRoot;
private ViewGroup mLayout; // alternative to layoutId
Runnable mEnterAction, mExitAction;
+ private static ThreadLocal<SparseArray<Scene>> sScenes = new ThreadLocal<SparseArray<Scene>>();
+
+ /**
+ * Returns a Scene described by the resource file associated with the given
+ * <code>layoutId</code> parameter. If such a Scene has already been created,
+ * that same Scene will be returned. This caching of layoutId-based scenes enables
+ * sharing of common scenes between those created in code and those referenced
+ * by {@link TransitionManager} XML resource files.
+ *
+ * @param sceneRoot The root of the hierarchy in which scene changes
+ * and transitions will take place.
+ * @param layoutId The id of a standard layout resource file.
+ * @param context The context used in the process of inflating
+ * the layout resource.
+ * @return
+ */
+ public static Scene getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context) {
+ SparseArray<Scene> scenes = sScenes.get();
+ if (scenes == null) {
+ scenes = new SparseArray<Scene>();
+ sScenes.set(scenes);
+ }
+ Scene scene = scenes.get(layoutId);
+ if (scene != null) {
+ return scene;
+ } else {
+ scene = new Scene(sceneRoot, layoutId, context);
+ scenes.put(layoutId, scene);
+ return scene;
+ }
+ }
/**
* Constructs a Scene with no information about how values will change
@@ -54,6 +87,9 @@ public final class Scene {
* children from the sceneRoot container and will inflate and add
* the hierarchy specified by the layoutId resource file.
*
+ * <p>This method is hidden because layoutId-based scenes should be
+ * created by the caching factory method {@link Scene#getCurrentScene(View)}.</p>
+ *
* @param sceneRoot The root of the hierarchy in which scene changes
* and transitions will take place.
* @param layoutId The id of a resource file that defines the view
@@ -61,7 +97,7 @@ public final class Scene {
* @param context The context used in the process of inflating
* the layout resource.
*/
- public Scene(ViewGroup sceneRoot, int layoutId, Context context) {
+ private Scene(ViewGroup sceneRoot, int layoutId, Context context) {
mContext = context;
mSceneRoot = sceneRoot;
mLayoutId = layoutId;
@@ -74,7 +110,7 @@ public final class Scene {
*
* @param sceneRoot The root of the hierarchy in which scene changes
* and transitions will take place.
- * @param layout The view hiearrchy of this scene, added as a child
+ * @param layout The view hierarchy of this scene, added as a child
* of sceneRoot when this scene is entered.
*/
public Scene(ViewGroup sceneRoot, ViewGroup layout) {
@@ -94,19 +130,14 @@ public final class Scene {
}
/**
- * Exits this scene, if it is the {@link ViewGroup#getCurrentScene()
- * currentScene} on the scene's {@link #getSceneRoot() scene root}.
- * Exiting a scene involves removing the layout added if the scene
- * has either a layoutId or layout view group (set at construction
- * time) and running the {@link #setExitAction(Runnable) exit action}
+ * Exits this scene, if it is the current scene
+ * on the scene's {@link #getSceneRoot() scene root}. The current scene is
+ * set when {@link #enter() entering} a scene.
+ * Exiting a scene runs the {@link #setExitAction(Runnable) exit action}
* if there is one.
*/
public void exit() {
- if (mSceneRoot.getCurrentScene() == this) {
- if (mLayoutId >= 0 || mLayout != null) {
- // Undo layout change caused by entering this scene
- getSceneRoot().removeAllViews();
- }
+ if (getCurrentScene(mSceneRoot) == this) {
if (mExitAction != null) {
mExitAction.run();
}
@@ -127,8 +158,7 @@ public final class Scene {
// Apply layout change, if any
if (mLayoutId >= 0 || mLayout != null) {
- // redundant with exit() action of previous scene, but must
- // empty out that parent container before adding to it
+ // empty out parent container before adding to it
getSceneRoot().removeAllViews();
if (mLayoutId >= 0) {
@@ -143,7 +173,30 @@ public final class Scene {
mEnterAction.run();
}
- mSceneRoot.setCurrentScene(this );
+ setCurrentScene(mSceneRoot, this);
+ }
+
+ /**
+ * Set the scene that the given view is in. The current scene is set only
+ * on the root view of a scene, not for every view in that hierarchy. This
+ * information is used by Scene to determine whether there is a previous
+ * scene which should be exited before the new scene is entered.
+ *
+ * @param view The view on which the current scene is being set
+ */
+ static void setCurrentScene(View view, Scene scene) {
+ view.setTagInternal(com.android.internal.R.id.current_scene, scene);
+ }
+
+ /**
+ * Gets the current {@link Scene} set on the given view. A scene is set on a view
+ * only if that view is the scene root.
+ *
+ * @return The current Scene set on this view. A value of null indicates that
+ * no Scene is currently set.
+ */
+ static Scene getCurrentScene(View view) {
+ return (Scene) view.getTag(com.android.internal.R.id.current_scene);
}
/**
diff --git a/core/java/android/view/transition/Slide.java b/core/java/android/transition/Slide.java
index b2f5db5..b38973c 100644
--- a/core/java/android/view/transition/Slide.java
+++ b/core/java/android/transition/Slide.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
import android.animation.Animator;
import android.animation.ObjectAnimator;
@@ -28,6 +28,8 @@ import android.view.animation.DecelerateInterpolator;
* This transition captures the visibility of target objects before and
* after a scene change and animates any changes by sliding the target
* objects into or out of place.
+ *
+ * @hide
*/
public class Slide extends Visibility {
@@ -37,7 +39,7 @@ public class Slide extends Visibility {
private static final TimeInterpolator sDecelerator = new DecelerateInterpolator();
@Override
- protected Animator appear(ViewGroup sceneRoot,
+ public Animator onAppear(ViewGroup sceneRoot,
TransitionValues startValues, int startVisibility,
TransitionValues endValues, int endVisibility) {
View endView = (endValues != null) ? endValues.view : null;
@@ -49,7 +51,7 @@ public class Slide extends Visibility {
}
@Override
- protected Animator disappear(ViewGroup sceneRoot,
+ public Animator onDisappear(ViewGroup sceneRoot,
TransitionValues startValues, int startVisibility,
TransitionValues endValues, int endVisibility) {
View startView = (startValues != null) ? startValues.view : null;
diff --git a/core/java/android/view/transition/TextChange.java b/core/java/android/transition/TextChange.java
index 7973c97..1b26942 100644
--- a/core/java/android/view/transition/TextChange.java
+++ b/core/java/android/transition/TextChange.java
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.graphics.Color;
-import android.util.ArrayMap;
import android.view.ViewGroup;
import android.widget.TextView;
@@ -33,6 +32,8 @@ import java.util.Map;
* starting text stays until the transition ends, at which point it changes
* to the end text. This is useful in situations where you want to resize a
* text view to its new size before displaying the text that goes there.
+ *
+ * @hide
*/
public class TextChange extends Transition {
private static final String PROPNAME_TEXT = "android:textchange:text";
@@ -84,15 +85,18 @@ public class TextChange extends Transition {
/**
* Sets the type of changing animation that will be run, one of
- * {@link #CHANGE_BEHAVIOR_KEEP} and {@link #CHANGE_BEHAVIOR_OUT_IN}.
+ * {@link #CHANGE_BEHAVIOR_KEEP}, {@link #CHANGE_BEHAVIOR_OUT},
+ * {@link #CHANGE_BEHAVIOR_IN}, and {@link #CHANGE_BEHAVIOR_OUT_IN}.
*
* @param changeBehavior The type of fading animation to use when this
* transition is run.
+ * @return this textChange object.
*/
- public void setChangeBehavior(int changeBehavior) {
+ public TextChange setChangeBehavior(int changeBehavior) {
if (changeBehavior >= CHANGE_BEHAVIOR_KEEP && changeBehavior <= CHANGE_BEHAVIOR_OUT_IN) {
mChangeBehavior = changeBehavior;
}
+ return this;
}
@Override
@@ -100,19 +104,38 @@ public class TextChange extends Transition {
return sTransitionProperties;
}
- @Override
- protected void captureValues(TransitionValues values, boolean start) {
- if (values.view instanceof TextView) {
- TextView textview = (TextView) values.view;
- values.values.put(PROPNAME_TEXT, textview.getText());
+ /**
+ * Returns the type of changing animation that will be run.
+ *
+ * @return either {@link #CHANGE_BEHAVIOR_KEEP}, {@link #CHANGE_BEHAVIOR_OUT},
+ * {@link #CHANGE_BEHAVIOR_IN}, or {@link #CHANGE_BEHAVIOR_OUT_IN}.
+ */
+ public int getChangeBehavior() {
+ return mChangeBehavior;
+ }
+
+ private void captureValues(TransitionValues transitionValues) {
+ if (transitionValues.view instanceof TextView) {
+ TextView textview = (TextView) transitionValues.view;
+ transitionValues.values.put(PROPNAME_TEXT, textview.getText());
if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) {
- values.values.put(PROPNAME_TEXT_COLOR, textview.getCurrentTextColor());
+ transitionValues.values.put(PROPNAME_TEXT_COLOR, textview.getCurrentTextColor());
}
}
}
@Override
- protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ public void captureStartValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
TransitionValues endValues) {
if (startValues == null || endValues == null || !(endValues.view instanceof TextView)) {
return null;
diff --git a/core/java/android/view/transition/Transition.java b/core/java/android/transition/Transition.java
index a66fa52..59df183 100644
--- a/core/java/android/view/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -31,11 +31,12 @@ import android.view.ViewOverlay;
import android.widget.ListView;
import java.util.ArrayList;
+import java.util.List;
/**
* A Transition holds information about animations that will be run on its
* targets during a scene change. Subclasses of this abstract class may
- * choreograph several child transitions ({@link TransitionGroup} or they may
+ * choreograph several child transitions ({@link TransitionSet} or they may
* perform custom animations themselves. Any Transition has two main jobs:
* (1) capture property values, and (2) play animations based on changes to
* captured property values. A custom transition knows what property values
@@ -50,9 +51,42 @@ import java.util.ArrayList;
* a non-UI thread, so changes to the view due to transitions (such as moving
* and resizing the view) may be out of sync with the display inside those bounds.
* TextureView is more compatible with transitions in general, but some
- * specific transitions (such as {@link Crossfade}) may not be compatible
+ * specific transitions (such as {@link Fade}) may not be compatible
* with TextureView because they rely on {@link ViewOverlay} functionality,
* which does not currently work with TextureView.</p>
+ *
+ * <p>Transitions can be declared in XML resource files inside the <code>res/transition</code>
+ * directory. Transition resources consist of a tag name for one of the Transition
+ * subclasses along with attributes to define some of the attributes of that transition.
+ * For example, here is a minimal resource file that declares a {@link ChangeBounds} transition:</p>
+ *
+ * {@sample development/samples/ApiDemos/res/transition/changebounds.xml ChangeBounds}
+ *
+ * <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
+ * the same defaults as their code-created equivalents. Here is a slightly more
+ * elaborate example which declares a {@link TransitionSet} transition with
+ * {@link ChangeBounds} and {@link Fade} child transitions:</p>
+ *
+ * {@sample
+ * development/samples/ApiDemos/res/transition/changebounds_fadeout_sequential.xml TransitionSet}
+ *
+ * <p>In this example, the transitionOrdering attribute is used on the TransitionSet
+ * object to change from the default {@link TransitionSet#ORDERING_TOGETHER} behavior
+ * to be {@link TransitionSet#ORDERING_SEQUENTIAL} instead. Also, the {@link Fade}
+ * 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.
+ * 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
+ * disappearing, so we choose to limit the {@link Fade} transition to only that view.</p>
+ *
+ * Further information on XML resource descriptions for transitions can be found for
+ * {@link android.R.styleable#Transition}, {@link android.R.styleable#TransitionSet},
+ * {@link android.R.styleable#TransitionTarget}, and {@link android.R.styleable#Fade}.
+ *
*/
public abstract class Transition implements Cloneable {
@@ -64,17 +98,17 @@ public abstract class Transition implements Cloneable {
long mStartDelay = -1;
long mDuration = -1;
TimeInterpolator mInterpolator = null;
- int[] mTargetIds;
- View[] mTargets;
+ ArrayList<Integer> mTargetIds = new ArrayList<Integer>();
+ ArrayList<View> mTargets = new ArrayList<View>();
private TransitionValuesMaps mStartValues = new TransitionValuesMaps();
private TransitionValuesMaps mEndValues = new TransitionValuesMaps();
- TransitionGroup mParent = null;
+ TransitionSet mParent = null;
// Per-animator information used for later canceling when future transitions overlap
private static ThreadLocal<ArrayMap<Animator, AnimationInfo>> sRunningAnimators =
new ThreadLocal<ArrayMap<Animator, AnimationInfo>>();
- // Scene Root is set at play() time in the cloned Transition
+ // Scene Root is set at createAnimator() time in the cloned Transition
ViewGroup mSceneRoot = null;
// Track all animators in use in case the transition gets canceled and needs to
@@ -91,14 +125,15 @@ public abstract class Transition implements Cloneable {
// The set of listeners to be sent transition lifecycle events.
ArrayList<TransitionListener> mListeners = null;
- // The set of animators collected from calls to play(), to be run in runAnimations()
+ // The set of animators collected from calls to createAnimator(),
+ // to be run in runAnimators()
ArrayList<Animator> mAnimators = new ArrayList<Animator>();
/**
* Constructs a Transition object with no target objects. A transition with
* no targets defaults to running on all target objects in the scene hierarchy
- * (if the transition is not contained in a TransitionGroup), or all target
- * objects passed down from its parent (if it is in a TransitionGroup).
+ * (if the transition is not contained in a TransitionSet), or all target
+ * objects passed down from its parent (if it is in a TransitionSet).
*/
public Transition() {}
@@ -110,6 +145,7 @@ public abstract class Transition implements Cloneable {
*
* @param duration The length of the animation, in milliseconds.
* @return This transition object.
+ * @attr ref android.R.styleable#Transition_duration
*/
public Transition setDuration(long duration) {
mDuration = duration;
@@ -121,8 +157,8 @@ public abstract class Transition implements Cloneable {
* the returned value will be negative, indicating that resulting animators will
* retain their own durations.
*
- * @return The duration set on this transition, if one has been set, otherwise
- * returns a negative number.
+ * @return The duration set on this transition, in milliseconds, if one has been
+ * set, otherwise returns a negative number.
*/
public long getDuration() {
return mDuration;
@@ -135,9 +171,12 @@ public abstract class Transition implements Cloneable {
* Transition is set, that delay will override the Animator delay.
*
* @param startDelay The length of the delay, in milliseconds.
+ * @return This transition object.
+ * @attr ref android.R.styleable#Transition_startDelay
*/
- public void setStartDelay(long startDelay) {
+ public Transition setStartDelay(long startDelay) {
mStartDelay = startDelay;
+ return this;
}
/**
@@ -145,8 +184,8 @@ public abstract class Transition implements Cloneable {
* the returned value will be negative, indicating that resulting animators will
* retain their own startDelays.
*
- * @return The startDealy set on this transition, if one has been set, otherwise
- * returns a negative number.
+ * @return The startDelay set on this transition, in milliseconds, if one has
+ * been set, otherwise returns a negative number.
*/
public long getStartDelay() {
return mStartDelay;
@@ -159,9 +198,12 @@ public abstract class Transition implements Cloneable {
* Transition is set, that interpolator will override the Animator interpolator.
*
* @param interpolator The time interpolator used by the transition
+ * @return This transition object.
+ * @attr ref android.R.styleable#Transition_interpolator
*/
- public void setInterpolator(TimeInterpolator interpolator) {
+ public Transition setInterpolator(TimeInterpolator interpolator) {
mInterpolator = interpolator;
+ return this;
}
/**
@@ -178,7 +220,7 @@ public abstract class Transition implements Cloneable {
/**
* Returns the set of property names used stored in the {@link TransitionValues}
- * object passed into {@link #captureValues(TransitionValues, boolean)} that
+ * object passed into {@link #captureStartValues(TransitionValues)} that
* this transition cares about for the purposes of canceling overlapping animations.
* When any transition is started on a given scene root, all transitions
* currently running on that same scene root are checked to see whether the
@@ -202,11 +244,17 @@ public abstract class Transition implements Cloneable {
}
/**
- * This method is called by the transition's parent (all the way up to the
+ * This method creates an animation that will be run for this transition
+ * given the information in the startValues and endValues structures captured
+ * earlier for the start and end scenes. Subclasses of Transition should override
+ * this method. The method should only be called by the transition system; it is
+ * not intended to be called from external classes.
+ *
+ * <p>This method is called by the transition's parent (all the way up to the
* topmost Transition in the hierarchy) with the sceneRoot and start/end
* values that the transition may need to set up initial target values
* and construct an appropriate animation. For example, if an overall
- * Transition is a {@link TransitionGroup} consisting of several
+ * Transition is a {@link TransitionSet} consisting of several
* child transitions in sequence, then some of the child transitions may
* want to set initial values on target views prior to the overall
* Transition commencing, to put them in an appropriate state for the
@@ -216,14 +264,13 @@ public abstract class Transition implements Cloneable {
* actually starting the animation. This is necessary because the scene
* change that triggers the Transition will automatically set the end-scene
* on all target views, so a Transition that wants to animate from a
- * different value should set that value prior to returning from this method.
+ * different value should set that value prior to returning from this method.</p>
*
* <p>Additionally, a Transition can perform logic to determine whether
* the transition needs to run on the given target and start/end values.
* For example, a transition that resizes objects on the screen may wish
* to avoid running for views which are not present in either the start
- * or end scenes. A return value of <code>null</code> indicates that
- * no animation should run. The default implementation returns null.</p>
+ * or end scenes.</p>
*
* <p>If there is an animator created and returned from this method, the
* transition mechanism will apply any applicable duration, startDelay,
@@ -234,31 +281,34 @@ public abstract class Transition implements Cloneable {
* <p>The method is called for every applicable target object, which is
* stored in the {@link TransitionValues#view} field.</p>
*
- * @param sceneRoot
- * @param startValues
- * @param endValues
- * @return A non-null Animator to be started at the appropriate time in the
- * overall transition for this scene change, null otherwise.
+ *
+ * @param sceneRoot The root of the transition hierarchy.
+ * @param startValues The values for a specific target in the start scene.
+ * @param endValues The values for the target in the end scene.
+ * @return A Animator to be started at the appropriate time in the
+ * overall transition for this scene change. A null value means no animation
+ * should be run.
*/
- protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
TransitionValues endValues) {
return null;
}
/**
- * This version of play() is called with the entire set of start/end
+ * This method, essentially a wrapper around all calls to createAnimator for all
+ * possible target views, is called with the entire set of start/end
* values. The implementation in Transition iterates through these lists
- * and calls {@link #play(ViewGroup, TransitionValues, TransitionValues)}
+ * and calls {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}
* with each set of start/end values on this transition. The
- * TransitionGroup subclass overrides this method and delegates it to
+ * TransitionSet subclass overrides this method and delegates it to
* each of its children in succession.
*
* @hide
*/
- protected void play(ViewGroup sceneRoot, TransitionValuesMaps startValues,
+ protected void createAnimators(ViewGroup sceneRoot, TransitionValuesMaps startValues,
TransitionValuesMaps endValues) {
if (DBG) {
- Log.d(LOG_TAG, "play() for " + this);
+ Log.d(LOG_TAG, "createAnimators() for " + this);
}
ArrayMap<View, TransitionValues> endCopy =
new ArrayMap<View, TransitionValues>(endValues.viewValues);
@@ -392,7 +442,7 @@ public abstract class Transition implements Cloneable {
}
}
// TODO: what to do about targetIds and itemIds?
- Animator animator = play(sceneRoot, start, end);
+ Animator animator = createAnimator(sceneRoot, start, end);
if (animator != null) {
// Save animation info for future cancellation purposes
View view = null;
@@ -450,19 +500,19 @@ public abstract class Transition implements Cloneable {
* views are ignored and only the ids are used).
*/
boolean isValidTarget(View target, long targetId) {
- if (mTargetIds == null && mTargets == null) {
+ if (mTargetIds.size() == 0 && mTargets.size() == 0) {
return true;
}
- if (mTargetIds != null) {
- for (int i = 0; i < mTargetIds.length; ++i) {
- if (mTargetIds[i] == targetId) {
+ if (mTargetIds.size() > 0) {
+ for (int i = 0; i < mTargetIds.size(); ++i) {
+ if (mTargetIds.get(i) == targetId) {
return true;
}
}
}
- if (target != null && mTargets != null) {
- for (int i = 0; i < mTargets.length; ++i) {
- if (mTargets[i] == target) {
+ if (target != null && mTargets.size() > 0) {
+ for (int i = 0; i < mTargets.size(); ++i) {
+ if (mTargets.get(i) == target) {
return true;
}
}
@@ -485,13 +535,13 @@ public abstract class Transition implements Cloneable {
*
* @hide
*/
- protected void runAnimations() {
+ protected void runAnimators() {
if (DBG) {
- Log.d(LOG_TAG, "runAnimations() on " + this);
+ Log.d(LOG_TAG, "runAnimators() on " + this);
}
start();
ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
- // Now start every Animator that was previously created for this transition in play()
+ // Now start every Animator that was previously created for this transition
for (Animator anim : mAnimators) {
if (DBG) {
Log.d(LOG_TAG, " anim: " + anim);
@@ -525,17 +575,49 @@ public abstract class Transition implements Cloneable {
}
/**
- * Captures the current scene of values for the properties that this
- * transition monitors. These values can be either the start or end
- * values used in a subsequent call to
- * {@link #play(ViewGroup, TransitionValues, TransitionValues)}, as indicated by
- * <code>start</code>. The main concern for an implementation is what the
+ * Captures the values in the start scene for the properties that this
+ * transition monitors. These values are then passed as the startValues
+ * structure in a later call to
+ * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}.
+ * The main concern for an implementation is what the
+ * properties are that the transition cares about and what the values are
+ * for all of those properties. The start and end values will be compared
+ * later during the
+ * {@link #createAnimator(android.view.ViewGroup, TransitionValues, TransitionValues)}
+ * method to determine what, if any, animations, should be run.
+ *
+ * <p>Subclasses must implement this method. The method should only be called by the
+ * transition system; it is not intended to be called from external classes.</p>
+ *
+ * @param transitionValues The holder for any values that the Transition
+ * wishes to store. Values are stored in the <code>values</code> field
+ * of this TransitionValues object and are keyed from
+ * a String value. For example, to store a view's rotation value,
+ * a transition might call
+ * <code>transitionValues.values.put("appname:transitionname:rotation",
+ * view.getRotation())</code>. The target view will already be stored in
+ * the transitionValues structure when this method is called.
+ *
+ * @see #captureEndValues(TransitionValues)
+ * @see #createAnimator(ViewGroup, TransitionValues, TransitionValues)
+ */
+ public abstract void captureStartValues(TransitionValues transitionValues);
+
+ /**
+ * Captures the values in the end scene for the properties that this
+ * transition monitors. These values are then passed as the endValues
+ * structure in a later call to
+ * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}.
+ * The main concern for an implementation is what the
* properties are that the transition cares about and what the values are
* for all of those properties. The start and end values will be compared
* later during the
- * {@link #play(android.view.ViewGroup, TransitionValues, TransitionValues)}
+ * {@link #createAnimator(android.view.ViewGroup, TransitionValues, TransitionValues)}
* method to determine what, if any, animations, should be run.
*
+ * <p>Subclasses must implement this method. The method should only be called by the
+ * transition system; it is not intended to be called from external classes.</p>
+ *
* @param transitionValues The holder for any values that the Transition
* wishes to store. Values are stored in the <code>values</code> field
* of this TransitionValues object and are keyed from
@@ -544,29 +626,51 @@ public abstract class Transition implements Cloneable {
* <code>transitionValues.values.put("appname:transitionname:rotation",
* view.getRotation())</code>. The target view will already be stored in
* the transitionValues structure when this method is called.
+ *
+ * @see #captureStartValues(TransitionValues)
+ * @see #createAnimator(ViewGroup, TransitionValues, TransitionValues)
*/
- protected abstract void captureValues(TransitionValues transitionValues, boolean start);
+ public abstract void captureEndValues(TransitionValues transitionValues);
/**
- * Sets the ids of target views that this Transition is interested in
+ * Adds the id of a target view that this Transition is interested in
* animating. By default, there are no targetIds, and a Transition will
* listen for changes on every view in the hierarchy below the sceneRoot
- * of the Scene being transitioned into. Setting targetIDs constrains
+ * of the Scene being transitioned into. Setting targetIds constrains
* the Transition to only listen for, and act on, views with these IDs.
* Views with different IDs, or no IDs whatsoever, will be ignored.
*
+ * <p>Note that using ids to specify targets implies that ids should be unique
+ * within the view hierarchy underneat the scene root.</p>
+ *
* @see View#getId()
- * @param targetIds A list of IDs which specify the set of Views on which
- * the Transition will act.
- * @return Transition The Transition on which the targetIds have been set.
+ * @param targetId The id of a target view, must be a positive number.
+ * @return The Transition to which the targetId is added.
+ * Returning the same object makes it easier to chain calls during
+ * construction, such as
+ * <code>transitionSet.addTransitions(new Fade()).addTargetId(someId);</code>
+ */
+ public Transition addTargetId(int targetId) {
+ if (targetId > 0) {
+ mTargetIds.add(targetId);
+ }
+ return this;
+ }
+
+ /**
+ * Removes the given targetId from the list of ids that this Transition
+ * is interested in animating.
+ *
+ * @param targetId The id of a target view, must be a positive number.
+ * @return The Transition from which the targetId is removed.
* Returning the same object makes it easier to chain calls during
* construction, such as
- * <code>transitionGroup.addTransitions(new Fade()).setTargetIds(someId);</code>
+ * <code>transitionSet.addTransitions(new Fade()).removeTargetId(someId);</code>
*/
- public Transition setTargetIds(int... targetIds) {
- int numTargets = targetIds.length;
- mTargetIds = new int[numTargets];
- System.arraycopy(targetIds, 0, mTargetIds, 0, numTargets);
+ public Transition removeTargetId(int targetId) {
+ if (targetId > 0) {
+ mTargetIds.remove(targetId);
+ }
return this;
}
@@ -578,28 +682,43 @@ public abstract class Transition implements Cloneable {
* the Transition to only listen for, and act on, these views.
* All other views will be ignored.
*
- * <p>The target list is like the {@link #setTargetIds(int...) targetId}
+ * <p>The target list is like the {@link #addTargetId(int) targetId}
* list except this list specifies the actual View instances, not the ids
* of the views. This is an important distinction when scene changes involve
* view hierarchies which have been inflated separately; different views may
* share the same id but not actually be the same instance. If the transition
- * should treat those views as the same, then seTargetIds() should be used
- * instead of setTargets(). If, on the other hand, scene changes involve
+ * should treat those views as the same, then {@link #addTargetId(int)} should be used
+ * instead of {@link #addTarget(View)}. If, on the other hand, scene changes involve
* changes all within the same view hierarchy, among views which do not
- * necessary have ids set on them, then the target list may be more
+ * necessarily have ids set on them, then the target list of views may be more
* convenient.</p>
*
- * @see #setTargetIds(int...)
- * @param targets A list of Views on which the Transition will act.
- * @return Transition The Transition on which the targets have been set.
+ * @see #addTargetId(int)
+ * @param target A View on which the Transition will act, must be non-null.
+ * @return The Transition to which the target is added.
+ * Returning the same object makes it easier to chain calls during
+ * construction, such as
+ * <code>transitionSet.addTransitions(new Fade()).addTarget(someView);</code>
+ */
+ public Transition addTarget(View target) {
+ mTargets.add(target);
+ return this;
+ }
+
+ /**
+ * Removes the given target from the list of targets that this Transition
+ * is interested in animating.
+ *
+ * @param target The target view, must be non-null.
+ * @return Transition The Transition from which the target is removed.
* Returning the same object makes it easier to chain calls during
* construction, such as
- * <code>transitionGroup.addTransitions(new Fade()).setTargets(someView);</code>
+ * <code>transitionSet.addTransitions(new Fade()).removeTarget(someView);</code>
*/
- public Transition setTargets(View... targets) {
- int numTargets = targets.length;
- mTargets = new View[numTargets];
- System.arraycopy(targets, 0, mTargets, 0, numTargets);
+ public Transition removeTarget(View target) {
+ if (target != null) {
+ mTargets.remove(target);
+ }
return this;
}
@@ -612,7 +731,7 @@ public abstract class Transition implements Cloneable {
*
* @return the list of target IDs
*/
- public int[] getTargetIds() {
+ public List<Integer> getTargetIds() {
return mTargetIds;
}
@@ -625,7 +744,7 @@ public abstract class Transition implements Cloneable {
*
* @return the list of target views
*/
- public View[] getTargets() {
+ public List<View> getTargets() {
return mTargets;
}
@@ -646,16 +765,19 @@ public abstract class Transition implements Cloneable {
mEndValues.idValues.clear();
mEndValues.itemIdValues.clear();
}
- if (mTargetIds != null && mTargetIds.length > 0 ||
- mTargets != null && mTargets.length > 0) {
- if (mTargetIds != null) {
- for (int i = 0; i < mTargetIds.length; ++i) {
- int id = mTargetIds[i];
+ if (mTargetIds.size() > 0 || mTargets.size() > 0) {
+ if (mTargetIds.size() > 0) {
+ for (int i = 0; i < mTargetIds.size(); ++i) {
+ int id = mTargetIds.get(i);
View view = sceneRoot.findViewById(id);
if (view != null) {
TransitionValues values = new TransitionValues();
values.view = view;
- captureValues(values, start);
+ if (start) {
+ captureStartValues(values);
+ } else {
+ captureEndValues(values);
+ }
if (start) {
mStartValues.viewValues.put(view, values);
if (id >= 0) {
@@ -670,13 +792,17 @@ public abstract class Transition implements Cloneable {
}
}
}
- if (mTargets != null) {
- for (int i = 0; i < mTargets.length; ++i) {
- View view = mTargets[i];
+ if (mTargets.size() > 0) {
+ for (int i = 0; i < mTargets.size(); ++i) {
+ View view = mTargets.get(i);
if (view != null) {
TransitionValues values = new TransitionValues();
values.view = view;
- captureValues(values, start);
+ if (start) {
+ captureStartValues(values);
+ } else {
+ captureEndValues(values);
+ }
if (start) {
mStartValues.viewValues.put(view, values);
} else {
@@ -723,7 +849,7 @@ public abstract class Transition implements Cloneable {
}
TransitionValues values = new TransitionValues();
values.view = view;
- captureValues(values, start);
+ captureStartValues(values);
if (start) {
if (!isListViewItem) {
mStartValues.viewValues.put(view, values);
@@ -757,7 +883,7 @@ public abstract class Transition implements Cloneable {
* necessary, for example, to query the before/after state of related views
* for a given transition.
*/
- protected TransitionValues getTransitionValues(View view, boolean start) {
+ public TransitionValues getTransitionValues(View view, boolean start) {
if (mParent != null) {
return mParent.getTransitionValues(view, start);
}
@@ -834,7 +960,7 @@ public abstract class Transition implements Cloneable {
/**
* Called by TransitionManager to play the transition. This calls
- * play() to set things up and create all of the animations and then
+ * createAnimators() to set things up and create all of the animations and then
* runAnimations() to actually start the animations.
*/
void playTransition(ViewGroup sceneRoot) {
@@ -889,11 +1015,8 @@ public abstract class Transition implements Cloneable {
}
}
- // setup() must be called on entire transition hierarchy and set of views
- // before calling play() on anything; every transition needs a chance to set up
- // target views appropriately before transitions begin running
- play(sceneRoot, mStartValues, mEndValues);
- runAnimations();
+ createAnimators(sceneRoot, mStartValues, mEndValues);
+ runAnimators();
}
/**
@@ -933,7 +1056,7 @@ public abstract class Transition implements Cloneable {
/**
* This method is called automatically by the transition and
- * TransitionGroup classes prior to a Transition subclass starting;
+ * TransitionSet classes prior to a Transition subclass starting;
* subclasses should not need to call it directly.
*
* @hide
@@ -954,9 +1077,9 @@ public abstract class Transition implements Cloneable {
/**
* This method is called automatically by the Transition and
- * TransitionGroup classes when a transition finishes, either because
+ * TransitionSet classes when a transition finishes, either because
* a transition did nothing (returned a null Animator from
- * {@link Transition#play(ViewGroup, TransitionValues,
+ * {@link Transition#createAnimator(ViewGroup, TransitionValues,
* TransitionValues)}) or because the transition returned a valid
* Animator and end() was called in the onAnimationEnd()
* callback of the AnimatorListener.
@@ -993,11 +1116,10 @@ public abstract class Transition implements Cloneable {
/**
* This method cancels a transition that is currently running.
- * Implementation TBD.
+ *
+ * @hide
*/
protected void cancel() {
- // TODO: how does this work with instances?
- // TODO: this doesn't actually do *anything* yet
int numAnimators = mCurrentAnimators.size();
for (int i = numAnimators - 1; i >= 0; i--) {
Animator animator = mCurrentAnimators.get(i);
@@ -1019,12 +1141,14 @@ public abstract class Transition implements Cloneable {
*
* @param listener the listener to be added to the current set of listeners
* for this animation.
+ * @return This transition object.
*/
- public void addListener(TransitionListener listener) {
+ public Transition addListener(TransitionListener listener) {
if (mListeners == null) {
mListeners = new ArrayList<TransitionListener>();
}
mListeners.add(listener);
+ return this;
}
/**
@@ -1032,29 +1156,22 @@ public abstract class Transition implements Cloneable {
*
* @param listener the listener to be removed from the current set of
* listeners for this transition.
+ * @return This transition object.
*/
- public void removeListener(TransitionListener listener) {
+ public Transition removeListener(TransitionListener listener) {
if (mListeners == null) {
- return;
+ return this;
}
mListeners.remove(listener);
if (mListeners.size() == 0) {
mListeners = null;
}
+ return this;
}
- /**
- * Gets the set of {@link TransitionListener} objects that are currently
- * listening for events on this <code>Transition</code> object.
- *
- * @return ArrayList<TransitionListener> The set of listeners.
- */
- public ArrayList<TransitionListener> getListeners() {
- return mListeners;
- }
-
- void setSceneRoot(ViewGroup sceneRoot) {
+ Transition setSceneRoot(ViewGroup sceneRoot) {
mSceneRoot = sceneRoot;
+ return this;
}
@Override
@@ -1076,9 +1193,9 @@ public abstract class Transition implements Cloneable {
/**
* Returns the name of this Transition. This name is used internally to distinguish
* between different transitions to determine when interrupting transitions overlap.
- * For example, a Move running on the same target view as another Move should determine
- * whether the old transition is animating to different end values and should be
- * canceled in favor of the new transition.
+ * For example, a ChangeBounds running on the same target view as another ChangeBounds
+ * should determine whether the old transition is animating to different end values
+ * and should be canceled in favor of the new transition.
*
* <p>By default, a Transition's name is simply the value of {@link Class#getName()},
* but subclasses are free to override and return something different.</p>
@@ -1101,22 +1218,22 @@ public abstract class Transition implements Cloneable {
if (mInterpolator != null) {
result += "interp(" + mInterpolator + ") ";
}
- if (mTargetIds != null || mTargets != null) {
+ if (mTargetIds.size() > 0 || mTargets.size() > 0) {
result += "tgts(";
- if (mTargetIds != null) {
- for (int i = 0; i < mTargetIds.length; ++i) {
+ if (mTargetIds.size() > 0) {
+ for (int i = 0; i < mTargetIds.size(); ++i) {
if (i > 0) {
result += ", ";
}
- result += mTargetIds[i];
+ result += mTargetIds.get(i);
}
}
- if (mTargets != null) {
- for (int i = 0; i < mTargets.length; ++i) {
+ if (mTargets.size() > 0) {
+ for (int i = 0; i < mTargets.size(); ++i) {
if (i > 0) {
result += ", ";
}
- result += mTargets[i];
+ result += mTargets.get(i);
}
}
result += ")";
@@ -1149,11 +1266,11 @@ public abstract class Transition implements Cloneable {
/**
* Notification about the cancellation of the transition.
- * Note that cancel() may be called by a parent {@link TransitionGroup} on
+ * Note that cancel may be called by a parent {@link TransitionSet} on
* a child transition which has not yet started. This allows the child
* transition to restore state on target objects which was set at
- * {@link #play(android.view.ViewGroup, TransitionValues, TransitionValues)
- * play()} time.
+ * {@link #createAnimator(android.view.ViewGroup, TransitionValues, TransitionValues)
+ * createAnimator()} time.
*
* @param transition The transition which was canceled.
*/
@@ -1161,11 +1278,11 @@ public abstract class Transition implements Cloneable {
/**
* Notification when a transition is paused.
- * Note that play() may be called by a parent {@link TransitionGroup} on
+ * Note that createAnimator() may be called by a parent {@link TransitionSet} on
* a child transition which has not yet started. This allows the child
* transition to restore state on target objects which was set at
- * {@link #play(android.view.ViewGroup, TransitionValues, TransitionValues)
- * play()} time.
+ * {@link #createAnimator(android.view.ViewGroup, TransitionValues, TransitionValues)
+ * createAnimator()} time.
*
* @param transition The transition which was paused.
*/
@@ -1173,7 +1290,7 @@ public abstract class Transition implements Cloneable {
/**
* Notification when a transition is resumed.
- * Note that resume() may be called by a parent {@link TransitionGroup} on
+ * Note that resume() may be called by a parent {@link TransitionSet} on
* a child transition which has not yet started. This allows the child
* transition to restore state which may have changed in an earlier call
* to {@link #onTransitionPause(Transition)}.
diff --git a/core/java/android/view/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index be658af..ebedeeb 100644
--- a/core/java/android/view/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
import android.content.Context;
import android.content.res.Resources;
@@ -35,6 +35,11 @@ import java.util.ArrayList;
/**
* This class inflates scenes and transitions from resource files.
+ *
+ * Information on XML resource descriptions for transitions can be found for
+ * {@link android.R.styleable#Transition}, {@link android.R.styleable#TransitionSet},
+ * {@link android.R.styleable#TransitionTarget}, {@link android.R.styleable#Fade},
+ * and {@link android.R.styleable#TransitionManager}.
*/
public class TransitionInflater {
@@ -121,46 +126,12 @@ public class TransitionInflater {
}
}
- /**
- * Loads a {@link Scene} object from a resource
- *
- * @param resource The resource id of the scene to load
- * @return The loaded Scene object
- * @throws android.content.res.Resources.NotFoundException when the scene
- * cannot be loaded
- */
- public Scene inflateScene(int resource, ViewGroup parent) {
- Scene scene = mScenes.get(resource);
- if (scene != null) {
- return scene;
- }
- XmlResourceParser parser = mContext.getResources().getXml(resource);
- try {
- scene = createSceneFromXml(parser, Xml.asAttributeSet(parser), parent);
- mScenes.put(resource, scene);
- return scene;
- } catch (XmlPullParserException e) {
- InflateException ex = new InflateException(e.getMessage());
- ex.initCause(e);
- throw ex;
- } catch (IOException e) {
- InflateException ex = new InflateException(
- parser.getPositionDescription()
- + ": " + e.getMessage());
- ex.initCause(e);
- throw ex;
- } finally {
- parser.close();
- }
- }
-
-
//
// Transition loading
//
private Transition createTransitionFromXml(XmlPullParser parser,
- AttributeSet attrs, TransitionGroup transitionGroup)
+ AttributeSet attrs, TransitionSet transitionSet)
throws XmlPullParserException, IOException {
Transition transition = null;
@@ -180,10 +151,14 @@ public class TransitionInflater {
String name = parser.getName();
if ("fade".equals(name)) {
- transition = new Fade();
+ TypedArray a = mContext.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.Fade);
+ int fadingMode = a.getInt(com.android.internal.R.styleable.Fade_fadingMode,
+ Fade.IN | Fade.OUT);
+ transition = new Fade(fadingMode);
newTransition = true;
- } else if ("move".equals(name)) {
- transition = new Move();
+ } else if ("changeBounds".equals(name)) {
+ transition = new ChangeBounds();
newTransition = true;
} else if ("slide".equals(name)) {
transition = new Slide();
@@ -194,24 +169,31 @@ public class TransitionInflater {
} else if ("recolor".equals(name)) {
transition = new Recolor();
newTransition = true;
- } else if ("transitionGroup".equals(name)) {
- transition = new TransitionGroup();
- createTransitionFromXml(parser, attrs, ((TransitionGroup) transition));
+ } else if ("set".equals(name)) {
+ transition = new TransitionSet();
+ TypedArray a = mContext.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.TransitionSet);
+ int ordering = a.getInt(
+ com.android.internal.R.styleable.TransitionSet_transitionOrdering,
+ TransitionSet.ORDERING_TOGETHER);
+ ((TransitionSet) transition).setOrdering(ordering);
+ createTransitionFromXml(parser, attrs, ((TransitionSet) transition));
+ a.recycle();
newTransition = true;
} else if ("targets".equals(name)) {
if (parser.getDepth() - 1 > depth && transition != null) {
// We're inside the child tag - add targets to the child
- getTargetIDs(parser, attrs, transition);
- } else if (parser.getDepth() - 1 == depth && transitionGroup != null) {
- // add targets to the group
- getTargetIDs(parser, attrs, transitionGroup);
+ getTargetIds(parser, attrs, transition);
+ } else if (parser.getDepth() - 1 == depth && transitionSet != null) {
+ // add targets to the set
+ getTargetIds(parser, attrs, transitionSet);
}
}
if (transition != null || "targets".equals(name)) {
if (newTransition) {
loadTransition(transition, attrs);
- if (transitionGroup != null) {
- transitionGroup.addTransitions(transition);
+ if (transitionSet != null) {
+ transitionSet.addTransition(transition);
}
}
} else {
@@ -222,7 +204,7 @@ public class TransitionInflater {
return transition;
}
- private void getTargetIDs(XmlPullParser parser,
+ private void getTargetIds(XmlPullParser parser,
AttributeSet attrs, Transition transition) throws XmlPullParserException, IOException {
// Make sure we are on a start tag.
@@ -240,8 +222,9 @@ public class TransitionInflater {
String name = parser.getName();
if (name.equals("target")) {
TypedArray a = mContext.obtainStyledAttributes(attrs,
- com.android.internal.R.styleable.Transition);
- int id = a.getResourceId(com.android.internal.R.styleable.Transition_targetID, -1);
+ com.android.internal.R.styleable.TransitionTarget);
+ int id = a.getResourceId(
+ com.android.internal.R.styleable.TransitionTarget_targetId, -1);
if (id >= 0) {
targetIds.add(id);
}
@@ -251,11 +234,9 @@ public class TransitionInflater {
}
int numTargets = targetIds.size();
if (numTargets > 0) {
- int[] targetsArray = new int[numTargets];
- for (int i = 0; i < targetIds.size(); ++i) {
- targetsArray[i] = targetIds.get(i);
+ for (int i = 0; i < numTargets; ++i) {
+ transition.addTargetId(targetIds.get(i));
}
- transition.setTargetIds(targetsArray);
}
}
@@ -268,9 +249,9 @@ public class TransitionInflater {
if (duration >= 0) {
transition.setDuration(duration);
}
- long startOffset = a.getInt(com.android.internal.R.styleable.Transition_startOffset, -1);
- if (startOffset > 0) {
- transition.setStartDelay(startOffset);
+ long startDelay = a.getInt(com.android.internal.R.styleable.Transition_startDelay, -1);
+ if (startDelay > 0) {
+ transition.setStartDelay(startDelay);
}
final int resID =
a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
@@ -313,20 +294,19 @@ public class TransitionInflater {
}
private void loadTransition(AttributeSet attrs, ViewGroup sceneRoot,
- TransitionManager transitionManager)
- throws Resources.NotFoundException {
+ TransitionManager transitionManager) throws Resources.NotFoundException {
TypedArray a = mContext.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.TransitionManager);
- int transitionId = attrs.getAttributeResourceValue(
+ int transitionId = a.getResourceId(
com.android.internal.R.styleable.TransitionManager_transition, -1);
Scene fromScene = null, toScene = null;
- int fromId = attrs.getAttributeResourceValue(
+ int fromId = a.getResourceId(
com.android.internal.R.styleable.TransitionManager_fromScene, -1);
- if (fromId >= 0) fromScene = inflateScene(fromId, sceneRoot);
- int toId = attrs.getAttributeResourceValue(
+ if (fromId >= 0) fromScene = Scene.getSceneForLayout(sceneRoot, fromId, mContext);
+ int toId = a.getResourceId(
com.android.internal.R.styleable.TransitionManager_toScene, -1);
- if (toId >= 0) toScene = inflateScene(toId, sceneRoot);
+ if (toId >= 0) toScene = Scene.getSceneForLayout(sceneRoot, toId, mContext);
if (transitionId >= 0) {
Transition transition = inflateTransition(transitionId);
if (transition != null) {
@@ -344,50 +324,4 @@ public class TransitionInflater {
}
a.recycle();
}
-
- //
- // Scene loading
- //
-
- private Scene createSceneFromXml(XmlPullParser parser, AttributeSet attrs, ViewGroup parent)
- throws XmlPullParserException, IOException {
- Scene scene = null;
-
- // Make sure we are on a start tag.
- int type;
- int depth = parser.getDepth();
-
- while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
- && type != XmlPullParser.END_DOCUMENT) {
-
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
-
- String name = parser.getName();
- if (name.equals("scene")) {
- scene = loadScene(attrs, parent);
- } else {
- throw new RuntimeException("Unknown scene name: " + parser.getName());
- }
- }
-
- return scene;
- }
-
- private Scene loadScene(AttributeSet attrs, ViewGroup parent)
- throws Resources.NotFoundException {
-
- Scene scene;
- TypedArray a = mContext.obtainStyledAttributes(attrs,
- com.android.internal.R.styleable.Scene);
- int layoutId = a.getResourceId(com.android.internal.R.styleable.Scene_layout, -1);
- if (layoutId >= 0) {
- scene = new Scene(parent, layoutId, mContext);
- } else {
- scene = new Scene(parent);
- }
- a.recycle();
- return scene;
- }
}
diff --git a/core/java/android/view/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index bde891d..9904413 100644
--- a/core/java/android/view/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
+import android.content.Context;
import android.util.ArrayMap;
import android.util.Log;
-import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
@@ -34,14 +34,36 @@ import java.util.ArrayList;
* situations. Specifying other transitions for particular scene changes is
* only necessary if the application wants different transition behavior
* in these situations.
+ *
+ * <p>TransitionManagers can be declared in XML resource files inside the
+ * <code>res/transition</code> directory. TransitionManager resources consist of
+ * the <code>transitionManager</code>tag name, containing one or more
+ * <code>transition</code> tags, each of which describe the relationship of
+ * that transition to the from/to scene information in that tag.
+ * For example, here is a resource file that declares several scene
+ * transitions:</p>
+ *
+ * {@sample development/samples/ApiDemos/res/transition/transitions_mgr.xml TransitionManager}
+ *
+ * <p>For each of the <code>fromScene</code> and <code>toScene</code> attributes,
+ * there is a reference to a standard XML layout file. This is equivalent to
+ * creating a scene from a layout in code by calling
+ * {@link Scene#getSceneForLayout(ViewGroup, int, Context)}. For the
+ * <code>transition</code> attribute, there is a reference to a resource
+ * file in the <code>res/transition</code> directory which describes that
+ * transition.</p>
+ *
+ * Information on XML resource descriptions for transitions can be found for
+ * {@link android.R.styleable#Transition}, {@link android.R.styleable#TransitionSet},
+ * {@link android.R.styleable#TransitionTarget}, {@link android.R.styleable#Fade},
+ * and {@link android.R.styleable#TransitionManager}.
*/
public class TransitionManager {
// TODO: how to handle enter/exit?
private static String LOG_TAG = "TransitionManager";
- private static final Transition sDefaultTransition = new AutoTransition();
- private Transition mDefaultTransition = new AutoTransition();
+ private static Transition sDefaultTransition = new AutoTransition();
ArrayMap<Scene, Transition> mSceneTransitions = new ArrayMap<Scene, Transition>();
ArrayMap<Scene, ArrayMap<Scene, Transition>> mScenePairTransitions =
@@ -59,7 +81,7 @@ public class TransitionManager {
* @param transition The default transition to be used for scene changes.
*/
public void setDefaultTransition(Transition transition) {
- mDefaultTransition = transition;
+ sDefaultTransition = transition;
}
/**
@@ -69,8 +91,8 @@ public class TransitionManager {
* @return The current default transition.
* @see #setDefaultTransition(Transition)
*/
- public Transition getDefaultTransition() {
- return mDefaultTransition;
+ public static Transition getDefaultTransition() {
+ return sDefaultTransition;
}
/**
@@ -80,7 +102,7 @@ public class TransitionManager {
* transition to run.
* @param transition The transition that will play when the given scene is
* entered. A value of null will result in the default behavior of
- * using {@link AutoTransition}.
+ * using the {@link #getDefaultTransition() default transition} instead.
*/
public void setTransition(Scene scene, Transition transition) {
mSceneTransitions.put(scene, transition);
@@ -96,7 +118,7 @@ public class TransitionManager {
* be run
* @param transition The transition that will play when the given scene is
* entered. A value of null will result in the default behavior of
- * using {@link AutoTransition}.
+ * using the {@link #getDefaultTransition() default transition} instead.
*/
public void setTransition(Scene fromScene, Scene toScene, Transition transition) {
ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(toScene);
@@ -114,15 +136,15 @@ public class TransitionManager {
*
* @param scene The scene being entered
* @return The Transition to be used for the given scene change. If no
- * Transition was specified for this scene change, {@link AutoTransition}
- * will be used instead.
+ * Transition was specified for this scene change, the {@link #getDefaultTransition()
+ * default transition} will be used instead.
*/
private Transition getTransition(Scene scene) {
Transition transition = null;
ViewGroup sceneRoot = scene.getSceneRoot();
if (sceneRoot != null) {
// TODO: cached in Scene instead? long-term, cache in View itself
- Scene currScene = sceneRoot.getCurrentScene();
+ Scene currScene = Scene.getCurrentScene(sceneRoot);
if (currScene != null) {
ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(scene);
if (sceneTransitionMap != null) {
@@ -134,7 +156,7 @@ public class TransitionManager {
}
}
transition = mSceneTransitions.get(scene);
- return (transition != null) ? transition : new AutoTransition();
+ return (transition != null) ? transition : sDefaultTransition;
}
/**
@@ -234,7 +256,7 @@ public class TransitionManager {
}
// Notify previous scene that it is being exited
- Scene previousScene = sceneRoot.getCurrentScene();
+ Scene previousScene = Scene.getCurrentScene(sceneRoot);
if (previousScene != null) {
previousScene.exit();
}
@@ -256,7 +278,7 @@ public class TransitionManager {
}
/**
- * Static utility method to simply change to the given scene using
+ * Convenience method to simply change to the given scene using
* the default transition for TransitionManager.
*
* @param scene The Scene to change to
@@ -266,15 +288,14 @@ public class TransitionManager {
}
/**
- * Static utility method to simply change to the given scene using
+ * Convenience method to simply change to the given scene using
* the given transition.
*
* <p>Passing in <code>null</code> for the transition parameter will
* result in the scene changing without any transition running, and is
* equivalent to calling {@link Scene#exit()} on the scene root's
- * {@link ViewGroup#getCurrentScene() current scene}, followed by
- * {@link Scene#enter()} on the scene specified by the <code>scene</code>
- * parameter.</p>
+ * current scene, followed by {@link Scene#enter()} on the scene
+ * specified by the <code>scene</code> parameter.</p>
*
* @param scene The Scene to change to
* @param transition The transition to use for this scene change. A
@@ -285,55 +306,20 @@ public class TransitionManager {
}
/**
- * Static utility method to simply change to a scene defined by the
- * code in the given runnable, which will be executed after
- * the current values have been captured for the transition.
- * This is equivalent to creating a Scene and calling {@link
- * Scene#setEnterAction(Runnable)} with the runnable, then calling
- * {@link #go(Scene, Transition)}. The transition used will be the
- * default provided by TransitionManager.
- *
- * @param sceneRoot The root of the View hierarchy used when this scene
- * runs a transition automatically.
- * @param action The runnable whose {@link Runnable#run() run()} method will
- * be called.
- */
- public static void go(ViewGroup sceneRoot, Runnable action) {
- Scene scene = new Scene(sceneRoot);
- scene.setEnterAction(action);
- changeScene(scene, sDefaultTransition);
- }
-
- /**
- * Static utility method to simply change to a scene defined by the
- * code in the given runnable, which will be executed after
- * the current values have been captured for the transition.
- * This is equivalent to creating a Scene and calling {@link
- * Scene#setEnterAction(Runnable)} with the runnable, then calling
- * {@link #go(Scene, Transition)}. The given transition will be
- * used to animate the changes.
- *
- * <p>Passing in <code>null</code> for the transition parameter will
- * result in the scene changing without any transition running, and is
- * equivalent to calling {@link Scene#exit()} on the scene root's
- * {@link ViewGroup#getCurrentScene() current scene}, followed by
- * {@link Scene#enter()} on a new scene specified by the
- * <code>action</code> parameter.</p>
+ * Convenience method to animate, using the default transition,
+ * to a new scene defined by all changes within the given scene root between
+ * calling this method and the next rendering frame.
+ * Equivalent to calling {@link #beginDelayedTransition(ViewGroup, Transition)}
+ * with a value of <code>null</code> for the <code>transition</code> parameter.
*
* @param sceneRoot The root of the View hierarchy to run the transition on.
- * @param action The runnable whose {@link Runnable#run() run()} method will
- * be called.
- * @param transition The transition to use for this change. A
- * value of null causes the change to happen with no transition.
*/
- public static void go(ViewGroup sceneRoot, Runnable action, Transition transition) {
- Scene scene = new Scene(sceneRoot);
- scene.setEnterAction(action);
- changeScene(scene, transition);
+ public static void beginDelayedTransition(final ViewGroup sceneRoot) {
+ beginDelayedTransition(sceneRoot, null);
}
/**
- * Static utility method to animate to a new scene defined by all changes within
+ * Convenience method to animate to a new scene defined by all changes within
* the given scene root between calling this method and the next rendering frame.
* Calling this method causes TransitionManager to capture current values in the
* scene root and then post a request to run a transition on the next frame.
@@ -367,7 +353,7 @@ public class TransitionManager {
}
final Transition finalTransition = transition.clone();
sceneChangeSetup(sceneRoot, transition);
- sceneRoot.setCurrentScene(null);
+ Scene.setCurrentScene(sceneRoot, null);
sceneChangeRunTransition(sceneRoot, finalTransition);
}
}
diff --git a/core/java/android/view/transition/TransitionGroup.java b/core/java/android/transition/TransitionSet.java
index b3bacde..1972c2a 100644
--- a/core/java/android/view/transition/TransitionGroup.java
+++ b/core/java/android/transition/TransitionSet.java
@@ -14,22 +14,24 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
+import android.animation.TimeInterpolator;
import android.util.AndroidRuntimeException;
+import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
/**
- * A TransitionGroup is a parent of child transitions (including other
- * TransitionGroups). Using TransitionGroups enables more complex
- * choreography of transitions, where some groups play {@link #TOGETHER} and
- * others play {@link #SEQUENTIALLY}. For example, {@link AutoTransition}
- * uses a TransitionGroup to sequentially play a Fade(Fade.OUT), followed by
- * a {@link Move}, followed by a Fade(Fade.OUT) transition.
+ * A TransitionSet is a parent of child transitions (including other
+ * TransitionSets). Using TransitionSets enables more complex
+ * choreography of transitions, where some sets play {@link #ORDERING_TOGETHER} and
+ * others play {@link #ORDERING_SEQUENTIAL}. For example, {@link AutoTransition}
+ * uses a TransitionSet to sequentially play a Fade(Fade.OUT), followed by
+ * a {@link ChangeBounds}, followed by a Fade(Fade.OUT) transition.
*/
-public class TransitionGroup extends Transition {
+public class TransitionSet extends Transition {
ArrayList<Transition> mTransitions = new ArrayList<Transition>();
private boolean mPlayTogether = true;
@@ -37,88 +39,96 @@ public class TransitionGroup extends Transition {
boolean mStarted = false;
/**
- * A flag used to indicate that the child transitions of this group
+ * A flag used to indicate that the child transitions of this set
* should all start at the same time.
*/
- public static final int TOGETHER = 0;
+ public static final int ORDERING_TOGETHER = 0;
/**
- * A flag used to indicate that the child transitions of this group should
+ * A flag used to indicate that the child transitions of this set should
* play in sequence; when one child transition ends, the next child
* transition begins. Note that a transition does not end until all
* instances of it (which are playing on all applicable targets of the
* transition) end.
*/
- public static final int SEQUENTIALLY = 1;
+ public static final int ORDERING_SEQUENTIAL = 1;
/**
- * Constructs an empty transition group. Add child transitions to the
- * group by calling to {@link #addTransitions(Transition...)} )}. By default,
- * child transitions will play {@link #TOGETHER}.
+ * Constructs an empty transition set. Add child transitions to the
+ * set by calling {@link #addTransition(Transition)} )}. By default,
+ * child transitions will play {@link #ORDERING_TOGETHER together}.
*/
- public TransitionGroup() {
+ public TransitionSet() {
}
/**
- * Constructs an empty transition group with the specified ordering.
+ * Sets the play order of this set's child transitions.
*
- * @param ordering {@link #TOGETHER} to start this group's child
- * transitions together, {@link #SEQUENTIALLY} to play the child
- * transitions in sequence.
- * @see #setOrdering(int)
- */
- public TransitionGroup(int ordering) {
- setOrdering(ordering);
- }
-
- /**
- * Sets the play order of this group's child transitions.
- *
- * @param ordering {@link #TOGETHER} to start this group's child
- * transitions together, {@link #SEQUENTIALLY} to play the child
+ * @param ordering {@link #ORDERING_TOGETHER} to play this set's child
+ * transitions together, {@link #ORDERING_SEQUENTIAL} to play the child
* transitions in sequence.
+ * @return This transitionSet object.
*/
- public void setOrdering(int ordering) {
+ public TransitionSet setOrdering(int ordering) {
switch (ordering) {
- case SEQUENTIALLY:
+ case ORDERING_SEQUENTIAL:
mPlayTogether = false;
break;
- case TOGETHER:
+ case ORDERING_TOGETHER:
mPlayTogether = true;
break;
default:
- throw new AndroidRuntimeException("Invalid parameter for TransitionGroup " +
+ throw new AndroidRuntimeException("Invalid parameter for TransitionSet " +
"ordering: " + ordering);
}
+ return this;
}
/**
- * Adds child transitions to this group. The order of the child transitions
- * passed in determines the order in which they are started.
+ * Returns the ordering of this TransitionSet. By default, the value is
+ * {@link #ORDERING_TOGETHER}.
+ *
+ * @return {@link #ORDERING_TOGETHER} if child transitions will play at the same
+ * time, {@link #ORDERING_SEQUENTIAL} if they will play in sequence.
*
- * @param transitions A list of child transition to be added to this group.
+ * @see #setOrdering(int)
*/
- public void addTransitions(Transition... transitions) {
- if (transitions != null) {
- int numTransitions = transitions.length;
- for (int i = 0; i < numTransitions; ++i) {
- mTransitions.add(transitions[i]);
- transitions[i].mParent = this;
- if (mDuration >= 0) {
- transitions[i].setDuration(mDuration);
- }
+ public int getOrdering() {
+ return mPlayTogether ? ORDERING_TOGETHER : ORDERING_SEQUENTIAL;
+ }
+
+ /**
+ * Adds child transition to this set. The order in which this child transition
+ * is added relative to other child transitions that are added, in addition to
+ * the {@link #getOrdering() ordering} property, determines the
+ * order in which the transitions are started.
+ *
+ * <p>If this transitionSet has a {@link #getDuration() duration} set on it, the
+ * child transition will inherit that duration. Transitions are assumed to have
+ * a maximum of one transitionSet parent.</p>
+ *
+ * @param transition A non-null child transition to be added to this set.
+ * @return This transitionSet object.
+ */
+ public TransitionSet addTransition(Transition transition) {
+ if (transition != null) {
+ mTransitions.add(transition);
+ transition.mParent = this;
+ if (mDuration >= 0) {
+ transition.setDuration(mDuration);
}
}
+ return this;
}
/**
- * Setting a non-negative duration on a TransitionGroup causes all of the child
+ * Setting a non-negative duration on a TransitionSet causes all of the child
* transitions (current and future) to inherit this duration.
*
* @param duration The length of the animation, in milliseconds.
- * @return This transitionGroup object.
+ * @return This transitionSet object.
*/
@Override
- public Transition setDuration(long duration) {
+ public TransitionSet setDuration(long duration) {
super.setDuration(duration);
if (mDuration >= 0) {
int numTransitions = mTransitions.size();
@@ -129,23 +139,65 @@ public class TransitionGroup extends Transition {
return this;
}
+ @Override
+ public TransitionSet setStartDelay(long startDelay) {
+ return (TransitionSet) super.setStartDelay(startDelay);
+ }
+
+ @Override
+ public TransitionSet setInterpolator(TimeInterpolator interpolator) {
+ return (TransitionSet) super.setInterpolator(interpolator);
+ }
+
+ @Override
+ public TransitionSet addTarget(View target) {
+ return (TransitionSet) super.addTarget(target);
+ }
+
+ @Override
+ public TransitionSet addTargetId(int targetId) {
+ return (TransitionSet) super.addTargetId(targetId);
+ }
+
+ @Override
+ public TransitionSet addListener(TransitionListener listener) {
+ return (TransitionSet) super.addListener(listener);
+ }
+
+ @Override
+ public TransitionSet removeTargetId(int targetId) {
+ return (TransitionSet) super.removeTargetId(targetId);
+ }
+
+ @Override
+ public TransitionSet removeTarget(View target) {
+ return (TransitionSet) super.removeTarget(target);
+ }
+
+ @Override
+ public TransitionSet removeListener(TransitionListener listener) {
+ return (TransitionSet) super.removeListener(listener);
+ }
+
/**
- * Removes the specified child transition from this group.
+ * Removes the specified child transition from this set.
*
* @param transition The transition to be removed.
+ * @return This transitionSet object.
*/
- public void removeTransition(Transition transition) {
+ public TransitionSet removeTransition(Transition transition) {
mTransitions.remove(transition);
transition.mParent = null;
+ return this;
}
/**
* Sets up listeners for each of the child transitions. This is used to
- * determine when this transition group is finished (all child transitions
+ * determine when this transition set is finished (all child transitions
* must finish first).
*/
private void setupStartEndListeners() {
- TransitionGroupListener listener = new TransitionGroupListener(this);
+ TransitionSetListener listener = new TransitionSetListener(this);
for (Transition childTransition : mTransitions) {
childTransition.addListener(listener);
}
@@ -154,28 +206,28 @@ public class TransitionGroup extends Transition {
/**
* This listener is used to detect when all child transitions are done, at
- * which point this transition group is also done.
+ * which point this transition set is also done.
*/
- static class TransitionGroupListener extends TransitionListenerAdapter {
- TransitionGroup mTransitionGroup;
- TransitionGroupListener(TransitionGroup transitionGroup) {
- mTransitionGroup = transitionGroup;
+ static class TransitionSetListener extends TransitionListenerAdapter {
+ TransitionSet mTransitionSet;
+ TransitionSetListener(TransitionSet transitionSet) {
+ mTransitionSet = transitionSet;
}
@Override
public void onTransitionStart(Transition transition) {
- if (!mTransitionGroup.mStarted) {
- mTransitionGroup.start();
- mTransitionGroup.mStarted = true;
+ if (!mTransitionSet.mStarted) {
+ mTransitionSet.start();
+ mTransitionSet.mStarted = true;
}
}
@Override
public void onTransitionEnd(Transition transition) {
- --mTransitionGroup.mCurrentListeners;
- if (mTransitionGroup.mCurrentListeners == 0) {
+ --mTransitionSet.mCurrentListeners;
+ if (mTransitionSet.mCurrentListeners == 0) {
// All child trans
- mTransitionGroup.mStarted = false;
- mTransitionGroup.end();
+ mTransitionSet.mStarted = false;
+ mTransitionSet.end();
}
transition.removeListener(this);
}
@@ -185,10 +237,10 @@ public class TransitionGroup extends Transition {
* @hide
*/
@Override
- protected void play(ViewGroup sceneRoot, TransitionValuesMaps startValues,
+ protected void createAnimators(ViewGroup sceneRoot, TransitionValuesMaps startValues,
TransitionValuesMaps endValues) {
for (Transition childTransition : mTransitions) {
- childTransition.play(sceneRoot, startValues, endValues);
+ childTransition.createAnimators(sceneRoot, startValues, endValues);
}
}
@@ -196,7 +248,7 @@ public class TransitionGroup extends Transition {
* @hide
*/
@Override
- protected void runAnimations() {
+ protected void runAnimators() {
setupStartEndListeners();
if (!mPlayTogether) {
// Setup sequence with listeners
@@ -207,28 +259,38 @@ public class TransitionGroup extends Transition {
previousTransition.addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
- nextTransition.runAnimations();
+ nextTransition.runAnimators();
transition.removeListener(this);
}
});
}
Transition firstTransition = mTransitions.get(0);
if (firstTransition != null) {
- firstTransition.runAnimations();
+ firstTransition.runAnimators();
}
} else {
for (Transition childTransition : mTransitions) {
- childTransition.runAnimations();
+ childTransition.runAnimators();
}
}
}
@Override
- protected void captureValues(TransitionValues transitionValues, boolean start) {
+ public void captureStartValues(TransitionValues transitionValues) {
int targetId = transitionValues.view.getId();
for (Transition childTransition : mTransitions) {
if (childTransition.isValidTarget(transitionValues.view, targetId)) {
- childTransition.captureValues(transitionValues, start);
+ childTransition.captureStartValues(transitionValues);
+ }
+ }
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ int targetId = transitionValues.view.getId();
+ for (Transition childTransition : mTransitions) {
+ if (childTransition.isValidTarget(transitionValues.view, targetId)) {
+ childTransition.captureEndValues(transitionValues);
}
}
}
@@ -253,6 +315,7 @@ public class TransitionGroup extends Transition {
}
}
+ /** @hide */
@Override
protected void cancel() {
super.cancel();
@@ -263,12 +326,13 @@ public class TransitionGroup extends Transition {
}
@Override
- void setSceneRoot(ViewGroup sceneRoot) {
+ TransitionSet setSceneRoot(ViewGroup sceneRoot) {
super.setSceneRoot(sceneRoot);
int numTransitions = mTransitions.size();
for (int i = 0; i < numTransitions; ++i) {
mTransitions.get(i).setSceneRoot(sceneRoot);
}
+ return (TransitionSet) this;
}
@Override
@@ -281,8 +345,8 @@ public class TransitionGroup extends Transition {
}
@Override
- public TransitionGroup clone() {
- TransitionGroup clone = (TransitionGroup) super.clone();
+ public TransitionSet clone() {
+ TransitionSet clone = (TransitionSet) super.clone();
clone.mTransitions = new ArrayList<Transition>();
int numTransitions = mTransitions.size();
for (int i = 0; i < numTransitions; ++i) {
diff --git a/core/java/android/view/transition/TransitionValues.java b/core/java/android/transition/TransitionValues.java
index 6e5d3d3..8989f89 100644
--- a/core/java/android/view/transition/TransitionValues.java
+++ b/core/java/android/transition/TransitionValues.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
import android.util.ArrayMap;
import android.view.View;
@@ -33,11 +33,11 @@ import java.util.Map;
* "Fader" as "android:fader:alpha".
*
* <p>These values are cached during the
- * {@link Transition#captureValues(TransitionValues, boolean)}
+ * {@link Transition#captureStartValues(TransitionValues)}
* capture} phases of a scene change, once when the start values are captured
* and again when the end values are captured. These start/end values are then
* passed into the transitions via the
- * for {@link Transition#play(ViewGroup, TransitionValues, TransitionValues)}
+ * for {@link Transition#createAnimator(ViewGroup, TransitionValues, TransitionValues)}
* method.</p>
*/
public class TransitionValues {
diff --git a/core/java/android/view/transition/TransitionValuesMaps.java b/core/java/android/transition/TransitionValuesMaps.java
index 4cfce4d..131596b 100644
--- a/core/java/android/view/transition/TransitionValuesMaps.java
+++ b/core/java/android/transition/TransitionValuesMaps.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
import android.util.ArrayMap;
import android.util.LongSparseArray;
diff --git a/core/java/android/view/transition/Visibility.java b/core/java/android/transition/Visibility.java
index 348dcfb..75d3e7c 100644
--- a/core/java/android/view/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-package android.view.transition;
+package android.transition;
import android.animation.Animator;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewOverlay;
-import android.view.ViewParent;
/**
* This transition tracks changes to the visibility of target views in the
@@ -30,13 +28,13 @@ import android.view.ViewParent;
* utility for subclasses such as {@link Fade}, which use this visibility
* information to determine the specific animations to run when visibility
* changes occur. Subclasses should implement one or both of the methods
- * {@link #appear(ViewGroup, TransitionValues, int, TransitionValues, int), and
- * {@link #disappear(ViewGroup, TransitionValues, int, TransitionValues, int)}.
+ * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)},
+ * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)},
*
* <p>Note that a view's visibility change is determined by both whether the view
* itself is changing and whether its parent hierarchy's visibility is changing.
* That is, a view that appears in the end scene will only trigger a call to
- * {@link #appear(android.view.ViewGroup, TransitionValues, int, TransitionValues, int)
+ * {@link #onAppear(android.view.ViewGroup, TransitionValues, int, TransitionValues, int)
* appear()} if its parent hierarchy was stable between the start and end scenes.
* This is done to avoid causing a visibility transition on every node in a hierarchy
* when only the top-most node is the one that should be transitioned in/out.
@@ -75,11 +73,20 @@ public abstract class Visibility extends Transition {
return sTransitionProperties;
}
+ private void captureValues(TransitionValues transitionValues) {
+ int visibility = transitionValues.view.getVisibility();
+ transitionValues.values.put(PROPNAME_VISIBILITY, visibility);
+ transitionValues.values.put(PROPNAME_PARENT, transitionValues.view.getParent());
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
@Override
- protected void captureValues(TransitionValues values, boolean start) {
- int visibility = values.view.getVisibility();
- values.values.put(PROPNAME_VISIBILITY, visibility);
- values.values.put(PROPNAME_PARENT, values.view.getParent());
+ public void captureEndValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
}
/**
@@ -87,7 +94,7 @@ public abstract class Visibility extends Transition {
* object. This is determined by testing the same properties in the values
* object that are used to determine whether the object is appearing or
* disappearing in the {@link
- * #play(android.view.ViewGroup, TransitionValues, TransitionValues)}
+ * Transition#createAnimator(ViewGroup, TransitionValues, TransitionValues)}
* method. This method can be called by, for example, subclasses that want
* to know whether the object is visible in the same way that Visibility
* determines it for the actual animation.
@@ -207,14 +214,14 @@ public abstract class Visibility extends Transition {
}
@Override
- protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
TransitionValues endValues) {
VisibilityInfo visInfo = getVisibilityChangeInfo(startValues, endValues);
if (visInfo.visibilityChange) {
// Only transition views that are either targets of this transition
// or whose parent hierarchies remain stable between scenes
boolean isTarget = false;
- if (mTargets != null || mTargetIds != null) {
+ if (mTargets.size() > 0 || mTargetIds.size() > 0) {
View startView = startValues != null ? startValues.view : null;
View endView = endValues != null ? endValues.view : null;
int startId = startView != null ? startView.getId() : -1;
@@ -225,10 +232,10 @@ public abstract class Visibility extends Transition {
!isHierarchyVisibilityChanging(sceneRoot,
visInfo.startParent, visInfo.endParent))) {
if (visInfo.fadeIn) {
- return appear(sceneRoot, startValues, visInfo.startVisibility,
+ return onAppear(sceneRoot, startValues, visInfo.startVisibility,
endValues, visInfo.endVisibility);
} else {
- return disappear(sceneRoot, startValues, visInfo.startVisibility,
+ return onDisappear(sceneRoot, startValues, visInfo.startVisibility,
endValues, visInfo.endVisibility
);
}
@@ -239,17 +246,20 @@ public abstract class Visibility extends Transition {
/**
* The default implementation of this method does nothing. Subclasses
- * should override if they need to set up anything prior to the
- * transition starting.
+ * should override if they need to create an Animator when targets appear.
+ * The method should only be called by the Visibility class; it is
+ * not intended to be called from external classes.
*
- * @param sceneRoot
- * @param startValues
- * @param startVisibility
- * @param endValues
- * @param endVisibility
- * @return
+ * @param sceneRoot The root of the transition hierarchy
+ * @param startValues The target values in the start scene
+ * @param startVisibility The target visibility in the start scene
+ * @param endValues The target values in the end scene
+ * @param endVisibility The target visibility in the end scene
+ * @return An Animator to be started at the appropriate time in the
+ * overall transition for this scene change. A null value means no animation
+ * should be run.
*/
- protected Animator appear(ViewGroup sceneRoot,
+ public Animator onAppear(ViewGroup sceneRoot,
TransitionValues startValues, int startVisibility,
TransitionValues endValues, int endVisibility) {
return null;
@@ -257,17 +267,21 @@ public abstract class Visibility extends Transition {
/**
* The default implementation of this method does nothing. Subclasses
- * should override if they need to set up anything prior to the
- * transition starting.
+ * should override if they need to create an Animator when targets disappear.
+ * The method should only be called by the Visibility class; it is
+ * not intended to be called from external classes.
+ *
*
- * @param sceneRoot
- * @param startValues
- * @param startVisibility
- * @param endValues
- * @param endVisibility
- * @return
+ * @param sceneRoot The root of the transition hierarchy
+ * @param startValues The target values in the start scene
+ * @param startVisibility The target visibility in the start scene
+ * @param endValues The target values in the end scene
+ * @param endVisibility The target visibility in the end scene
+ * @return An Animator to be started at the appropriate time in the
+ * overall transition for this scene change. A null value means no animation
+ * should be run.
*/
- protected Animator disappear(ViewGroup sceneRoot,
+ public Animator onDisappear(ViewGroup sceneRoot,
TransitionValues startValues, int startVisibility,
TransitionValues endValues, int endVisibility) {
return null;
diff --git a/core/java/android/view/transition/package.html b/core/java/android/transition/package.html
index 37dc0ec..f357d34 100644
--- a/core/java/android/view/transition/package.html
+++ b/core/java/android/transition/package.html
@@ -3,15 +3,15 @@
<p>The classes in this package enable "scenes & transitions" functionality for
view hiearchies.</p>
-<p>A <b>Scene</b> is an encapsulation of the state of a view hiearchy,
+<p>A <b>Scene</b> is an encapsulation of the state of a view hierarchy,
including the views in that hierarchy and the various values (layout-related
-and otherwise) that those views have. A scene be defined by a layout hierarchy
-directly or some code which sets up the scene dynamically as it is entered.</p>
+and otherwise) that those views have. A scene can be defined by a layout hierarchy
+directly or by code which sets up the scene dynamically as it is entered.</p>
<p>A <b>Transition</b> is a mechanism to automatically animate changes that occur
when a new scene is entered. Some transition capabilities are automatic. That
is, entering a scene may cause animations to run which fade out views that
-go away, move and resize existing views that change, and fade in views that
+go away, changeBounds and resize existing views that change, and fade in views that
become visible. There are additional transitions that can animate other
attributes, such as color changes, and which can optionally be specified
to take place during particular scene changes. Finally, developers can
@@ -19,7 +19,8 @@ define their own Transition subclasses which monitor particular property
changes and which run custom animations when those properties change values.</p>
<p><b>TransitionManager</b> is used to specify custom transitions for particular
-scene changes, and to cause scene changes with transitions to take place.</p>
+scene changes, and to cause scene changes with specific transitions to
+take place.</p>
</body>
</html>
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 28c1058..6bbfe0f 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -323,7 +323,7 @@ public class GestureDetector {
/**
* Creates a GestureDetector with the supplied listener.
- * You may only use this constructor from a UI thread (this is the usual situation).
+ * You may only use this constructor from a {@link android.os.Looper} thread.
* @see android.os.Handler#Handler()
*
* @param context the application's context
@@ -337,14 +337,14 @@ public class GestureDetector {
}
/**
- * Creates a GestureDetector with the supplied listener.
- * You may only use this constructor from a UI thread (this is the usual situation).
+ * Creates a GestureDetector with the supplied listener that runs deferred events on the
+ * thread associated with the supplied {@link android.os.Handler}.
* @see android.os.Handler#Handler()
*
* @param context the application's context
* @param listener the listener invoked for all the callbacks, this must
* not be null.
- * @param handler the handler to use
+ * @param handler the handler to use for running deferred listener events.
*
* @throws NullPointerException if {@code listener} is null.
*/
@@ -362,14 +362,15 @@ public class GestureDetector {
}
/**
- * Creates a GestureDetector with the supplied listener.
- * You may only use this constructor from a UI thread (this is the usual situation).
+ * Creates a GestureDetector with the supplied listener that runs deferred events on the
+ * thread associated with the supplied {@link android.os.Handler}.
* @see android.os.Handler#Handler()
*
* @param context the application's context
* @param listener the listener invoked for all the callbacks, this must
* not be null.
- * @param handler the handler to use
+ * @param handler the handler to use for running deferred listener events.
+ * @param unused currently not used.
*
* @throws NullPointerException if {@code listener} is null.
*/
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 51c5c7b..0bebc04 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -18,6 +18,8 @@ package android.view;
import android.content.Context;
import android.content.res.Resources;
+import android.os.Build;
+import android.os.Handler;
import android.os.SystemClock;
import android.util.FloatMath;
@@ -128,6 +130,8 @@ public class ScaleGestureDetector {
private float mFocusX;
private float mFocusY;
+ private boolean mDoubleTapScales;
+
private float mCurrSpan;
private float mPrevSpan;
private float mInitialSpan;
@@ -148,9 +152,14 @@ public class ScaleGestureDetector {
private int mTouchHistoryDirection;
private long mTouchHistoryLastAcceptedTime;
private int mTouchMinMajor;
+ private MotionEvent mDoubleTapEvent;
+ private int mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
+ private final Handler mHandler;
private static final long TOUCH_STABILIZE_TIME = 128; // ms
- private static final int TOUCH_MIN_MAJOR = 48; // dp
+ private static final int DOUBLE_TAP_MODE_NONE = 0;
+ private static final int DOUBLE_TAP_MODE_IN_PROGRESS = 1;
+
/**
* Consistency verifier for debugging purposes.
@@ -158,8 +167,37 @@ public class ScaleGestureDetector {
private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
new InputEventConsistencyVerifier(this, 0) : null;
+ private GestureDetector mGestureDetector;
+
+ private boolean mEventBeforeOrAboveStartingGestureEvent;
+ /**
+ * Creates a ScaleGestureDetector with the supplied listener.
+ * You may only use this constructor from a {@link android.os.Looper Looper} thread.
+ *
+ * @param context the application's context
+ * @param listener the listener invoked for all the callbacks, this must
+ * not be null.
+ *
+ * @throws NullPointerException if {@code listener} is null.
+ */
public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
+ this(context, listener, null);
+ }
+
+ /**
+ * Creates a ScaleGestureDetector with the supplied listener.
+ * @see android.os.Handler#Handler()
+ *
+ * @param context the application's context
+ * @param listener the listener invoked for all the callbacks, this must
+ * not be null.
+ * @param handler the handler to use for running deferred listener events.
+ *
+ * @throws NullPointerException if {@code listener} is null.
+ */
+ public ScaleGestureDetector(Context context, OnScaleGestureListener listener,
+ Handler handler) {
mContext = context;
mListener = listener;
mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
@@ -167,8 +205,12 @@ public class ScaleGestureDetector {
final Resources res = context.getResources();
mTouchMinMajor = res.getDimensionPixelSize(
com.android.internal.R.dimen.config_minScalingTouchMajor);
- mMinSpan = res.getDimensionPixelSize(
- com.android.internal.R.dimen.config_minScalingSpan);
+ mMinSpan = res.getDimensionPixelSize(com.android.internal.R.dimen.config_minScalingSpan);
+ mHandler = handler;
+ // Quick scale is enabled by default after JB_MR2
+ if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ setQuickScaleEnabled(true);
+ }
}
/**
@@ -263,8 +305,14 @@ public class ScaleGestureDetector {
final int action = event.getActionMasked();
+ // Forward the event to check for double tap gesture
+ if (mDoubleTapScales) {
+ mGestureDetector.onTouchEvent(event);
+ }
+
final boolean streamComplete = action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_CANCEL;
+
if (action == MotionEvent.ACTION_DOWN || streamComplete) {
// Reset any scale in progress with the listener.
// If it's an ACTION_DOWN we're beginning a new event stream.
@@ -273,6 +321,7 @@ public class ScaleGestureDetector {
mListener.onScaleEnd(this);
mInProgress = false;
mInitialSpan = 0;
+ mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
}
if (streamComplete) {
@@ -284,21 +333,37 @@ public class ScaleGestureDetector {
final boolean configChanged = action == MotionEvent.ACTION_DOWN ||
action == MotionEvent.ACTION_POINTER_UP ||
action == MotionEvent.ACTION_POINTER_DOWN;
+
+
final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
final int skipIndex = pointerUp ? event.getActionIndex() : -1;
// Determine focal point
float sumX = 0, sumY = 0;
final int count = event.getPointerCount();
- for (int i = 0; i < count; i++) {
- if (skipIndex == i) continue;
- sumX += event.getX(i);
- sumY += event.getY(i);
- }
final int div = pointerUp ? count - 1 : count;
- final float focusX = sumX / div;
- final float focusY = sumY / div;
+ final float focusX;
+ final float focusY;
+ if (mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS) {
+ // In double tap mode, the focal pt is always where the double tap
+ // gesture started
+ focusX = mDoubleTapEvent.getX();
+ focusY = mDoubleTapEvent.getY();
+ if (event.getY() < focusY) {
+ mEventBeforeOrAboveStartingGestureEvent = true;
+ } else {
+ mEventBeforeOrAboveStartingGestureEvent = false;
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ sumX += event.getX(i);
+ sumY += event.getY(i);
+ }
+ focusX = sumX / div;
+ focusY = sumY / div;
+ }
addTouchHistory(event);
@@ -320,7 +385,12 @@ public class ScaleGestureDetector {
// the focal point.
final float spanX = devX * 2;
final float spanY = devY * 2;
- final float span = FloatMath.sqrt(spanX * spanX + spanY * spanY);
+ final float span;
+ if (inDoubleTapMode()) {
+ span = spanY;
+ } else {
+ span = FloatMath.sqrt(spanX * spanX + spanY * spanY);
+ }
// Dispatch begin/end events as needed.
// If the configuration changes, notify the app to reset its current state by beginning
@@ -328,10 +398,11 @@ public class ScaleGestureDetector {
final boolean wasInProgress = mInProgress;
mFocusX = focusX;
mFocusY = focusY;
- if (mInProgress && (span < mMinSpan || configChanged)) {
+ if (!inDoubleTapMode() && mInProgress && (span < mMinSpan || configChanged)) {
mListener.onScaleEnd(this);
mInProgress = false;
mInitialSpan = span;
+ mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
}
if (configChanged) {
mPrevSpanX = mCurrSpanX = spanX;
@@ -354,6 +425,7 @@ public class ScaleGestureDetector {
mCurrSpan = span;
boolean updatePrev = true;
+
if (mInProgress) {
updatePrev = mListener.onScale(this);
}
@@ -369,6 +441,34 @@ public class ScaleGestureDetector {
return true;
}
+
+ private boolean inDoubleTapMode() {
+ return mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS;
+ }
+
+ /**
+ * Set whether the associated {@link OnScaleGestureListener} should receive onScale callbacks
+ * when the user performs a doubleTap followed by a swipe. Note that this is enabled by default
+ * if the app targets API 19 and newer.
+ * @param scales true to enable quick scaling, false to disable
+ */
+ public void setQuickScaleEnabled(boolean scales) {
+ mDoubleTapScales = scales;
+ if (mDoubleTapScales && mGestureDetector == null) {
+ GestureDetector.SimpleOnGestureListener gestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ // Double tap: start watching for a swipe
+ mDoubleTapEvent = e;
+ mDoubleTapMode = DOUBLE_TAP_MODE_IN_PROGRESS;
+ return true;
+ }
+ };
+ mGestureDetector = new GestureDetector(mContext, gestureListener, mHandler);
+ }
+ }
+
/**
* Returns {@code true} if a scale gesture is in progress.
*/
@@ -472,6 +572,12 @@ public class ScaleGestureDetector {
* @return The current scaling factor.
*/
public float getScaleFactor() {
+ if (inDoubleTapMode() && mEventBeforeOrAboveStartingGestureEvent) {
+ // Drag is moving up; the further away from the gesture
+ // start, the smaller the span should be, the closer,
+ // the larger the span, and therefore the larger the scale
+ return (1 / mCurrSpan) / (1 / mPrevSpan);
+ }
return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1;
}
@@ -493,4 +599,4 @@ public class ScaleGestureDetector {
public long getEventTime() {
return mCurrTime;
}
-}
+} \ No newline at end of file
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5b279ec..f2b3e89 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -74,7 +74,6 @@ import android.view.animation.Transformation;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
-import android.view.transition.Scene;
import android.widget.ScrollBarDrawable;
import static android.os.Build.VERSION_CODES.*;
@@ -1575,8 +1574,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
protected Object mTag;
- private Scene mCurrentScene = null;
-
// for mPrivateFlags:
/** {@hide} */
static final int PFLAG_WANTS_FOCUS = 0x00000001;
@@ -8836,9 +8833,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Change the view's z order in the tree, so it's on top of other sibling
* views. This ordering change may affect layout, if the parent container
- * uses an order-dependent layout scheme (e.g., LinearLayout). This
+ * uses an order-dependent layout scheme (e.g., LinearLayout). Prior
+ * to {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE} this
* method should be followed by calls to {@link #requestLayout()} and
- * {@link View#invalidate()} on the parent.
+ * {@link View#invalidate()} on the view's parent to force the parent to redraw
+ * with the new child ordering.
*
* @see ViewGroup#bringChildToFront(View)
*/
@@ -12187,7 +12186,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
cleanupDraw();
mCurrentAnimation = null;
- mCurrentScene = null;
}
private void cleanupDraw() {
@@ -13256,14 +13254,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// Keep the DRAWING_CACHE_QUALITY_LOW flag just in case
switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {
case DRAWING_CACHE_QUALITY_AUTO:
- quality = Bitmap.Config.ARGB_8888;
- break;
case DRAWING_CACHE_QUALITY_LOW:
- quality = Bitmap.Config.ARGB_8888;
- break;
case DRAWING_CACHE_QUALITY_HIGH:
- quality = Bitmap.Config.ARGB_8888;
- break;
default:
quality = Bitmap.Config.ARGB_8888;
break;
@@ -18106,31 +18098,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Set the current Scene that this view is in. The current scene is set only
- * on the root view of a scene, not for every view in that hierarchy. This
- * information is used by Scene to determine whether there is a previous
- * scene which should be exited before the new scene is entered.
- *
- * @param scene The new scene being set on the view
- *
- * @hide
- */
- public void setCurrentScene(Scene scene) {
- mCurrentScene = scene;
- }
-
- /**
- * Gets the current {@link Scene} set on this view. A scene is set on a view
- * only if that view is the scene root.
- *
- * @return The current Scene set on this view. A value of null indicates that
- * no Scene is current set.
- */
- public Scene getCurrentScene() {
- return mCurrentScene;
- }
-
- /**
* Interface definition for a callback to be invoked when a hardware key event is
* dispatched to this view. The callback will be invoked before the key event is
* given to the view. This is only useful for hardware keyboards; a software input
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 03a9c37..2d75b06 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1123,6 +1123,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
removeFromArray(index);
addInArray(child, mChildrenCount);
child.mParent = this;
+ requestLayout();
+ invalidate();
}
}
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 26596d9..656d756 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -148,9 +148,11 @@ public interface ViewParent {
/**
* Change the z order of the child so it's on top of all other children.
* This ordering change may affect layout, if this container
- * uses an order-dependent layout scheme (e.g., LinearLayout). This
+ * uses an order-dependent layout scheme (e.g., LinearLayout). Prior
+ * to {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE} this
* method should be followed by calls to {@link #requestLayout()} and
- * {@link View#invalidate()} on this parent.
+ * {@link View#invalidate()} on this parent to force the parent to redraw
+ * with the new child ordering.
*
* @param child The child to bring to the top of the z order
*/
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 354e815..0f9a2ac 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1449,7 +1449,7 @@ public final class ViewRootImpl implements ViewParent,
}
DisplayList displayList = mView.mDisplayList;
- if (displayList != null) {
+ if (displayList != null && displayList.isValid()) {
layerCanvas.drawDisplayList(displayList, null,
DisplayList.FLAG_CLIP_CHILDREN);
} else {
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index fea6be6..7707392 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -50,7 +50,9 @@ import java.util.Map;
*/
class CallbackProxy extends Handler {
// Logging tag
- private static final String LOGTAG = "CallbackProxy";
+ static final String LOGTAG = "WebViewCallback";
+ // Enables API callback tracing
+ private static final boolean TRACE = DebugFlags.TRACE_CALLBACK;
// Instance of WebViewClient that is the client callback.
private volatile WebViewClient mWebViewClient;
// Instance of WebChromeClient for handling all chrome functions.
@@ -258,6 +260,7 @@ class CallbackProxy extends Handler {
}
boolean override = false;
if (mWebViewClient != null) {
+ if (TRACE) Log.d(LOGTAG, "shouldOverrideUrlLoading=" + overrideUrl);
override = mWebViewClient.shouldOverrideUrlLoading(mWebView.getWebView(),
overrideUrl);
} else {
@@ -307,6 +310,7 @@ class CallbackProxy extends Handler {
String startedUrl = msg.getData().getString("url");
mWebView.onPageStarted(startedUrl);
if (mWebViewClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onPageStarted=" + startedUrl);
mWebViewClient.onPageStarted(mWebView.getWebView(), startedUrl,
(Bitmap) msg.obj);
}
@@ -316,18 +320,21 @@ class CallbackProxy extends Handler {
String finishedUrl = (String) msg.obj;
mWebView.onPageFinished(finishedUrl);
if (mWebViewClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onPageFinished=" + finishedUrl);
mWebViewClient.onPageFinished(mWebView.getWebView(), finishedUrl);
}
break;
case RECEIVED_ICON:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onReceivedIcon");
mWebChromeClient.onReceivedIcon(mWebView.getWebView(), (Bitmap) msg.obj);
}
break;
case RECEIVED_TOUCH_ICON_URL:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onReceivedTouchIconUrl");
mWebChromeClient.onReceivedTouchIconUrl(mWebView.getWebView(),
(String) msg.obj, msg.arg1 == 1);
}
@@ -335,6 +342,7 @@ class CallbackProxy extends Handler {
case RECEIVED_TITLE:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onReceivedTitle");
mWebChromeClient.onReceivedTitle(mWebView.getWebView(),
(String) msg.obj);
}
@@ -345,6 +353,7 @@ class CallbackProxy extends Handler {
int reasonCode = msg.arg1;
final String description = msg.getData().getString("description");
final String failUrl = msg.getData().getString("failingUrl");
+ if (TRACE) Log.d(LOGTAG, "onReceivedError=" + failUrl);
mWebViewClient.onReceivedError(mWebView.getWebView(), reasonCode,
description, failUrl);
}
@@ -356,6 +365,7 @@ class CallbackProxy extends Handler {
Message dontResend =
(Message) msg.getData().getParcelable("dontResend");
if (mWebViewClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onFormResubmission");
mWebViewClient.onFormResubmission(mWebView.getWebView(), dontResend,
resend);
} else {
@@ -379,6 +389,7 @@ class CallbackProxy extends Handler {
HttpAuthHandler handler = (HttpAuthHandler) msg.obj;
String host = msg.getData().getString("host");
String realm = msg.getData().getString("realm");
+ if (TRACE) Log.d(LOGTAG, "onReceivedHttpAuthRequest");
mWebViewClient.onReceivedHttpAuthRequest(mWebView.getWebView(), handler,
host, realm);
}
@@ -388,6 +399,7 @@ class CallbackProxy extends Handler {
if (mWebViewClient != null) {
HashMap<String, Object> map =
(HashMap<String, Object>) msg.obj;
+ if (TRACE) Log.d(LOGTAG, "onReceivedSslError");
mWebViewClient.onReceivedSslError(mWebView.getWebView(),
(SslErrorHandler) map.get("handler"),
(SslError) map.get("error"));
@@ -396,6 +408,7 @@ class CallbackProxy extends Handler {
case PROCEEDED_AFTER_SSL_ERROR:
if (mWebViewClient != null && mWebViewClient instanceof WebViewClientClassicExt) {
+ if (TRACE) Log.d(LOGTAG, "onProceededAfterSslError");
((WebViewClientClassicExt) mWebViewClient).onProceededAfterSslError(
mWebView.getWebView(),
(SslError) msg.obj);
@@ -404,6 +417,7 @@ class CallbackProxy extends Handler {
case CLIENT_CERT_REQUEST:
if (mWebViewClient != null && mWebViewClient instanceof WebViewClientClassicExt) {
+ if (TRACE) Log.d(LOGTAG, "onReceivedClientCertRequest");
HashMap<String, Object> map = (HashMap<String, Object>) msg.obj;
((WebViewClientClassicExt) mWebViewClient).onReceivedClientCertRequest(
mWebView.getWebView(),
@@ -418,6 +432,7 @@ class CallbackProxy extends Handler {
// changed.
synchronized (this) {
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onProgressChanged=" + mLatestProgress);
mWebChromeClient.onProgressChanged(mWebView.getWebView(),
mLatestProgress);
}
@@ -427,14 +442,18 @@ class CallbackProxy extends Handler {
case UPDATE_VISITED:
if (mWebViewClient != null) {
+ String url = (String) msg.obj;
+ if (TRACE) Log.d(LOGTAG, "doUpdateVisitedHistory=" + url);
mWebViewClient.doUpdateVisitedHistory(mWebView.getWebView(),
- (String) msg.obj, msg.arg1 != 0);
+ url, msg.arg1 != 0);
}
break;
case LOAD_RESOURCE:
if (mWebViewClient != null) {
- mWebViewClient.onLoadResource(mWebView.getWebView(), (String) msg.obj);
+ String url = (String) msg.obj;
+ if (TRACE) Log.d(LOGTAG, "onLoadResource=" + url);
+ mWebViewClient.onLoadResource(mWebView.getWebView(), url);
}
break;
@@ -448,6 +467,7 @@ class CallbackProxy extends Handler {
String referer = msg.getData().getString("referer");
Long contentLength = msg.getData().getLong("contentLength");
+ if (TRACE) Log.d(LOGTAG, "onDownloadStart");
if (mDownloadListener instanceof BrowserDownloadListener) {
((BrowserDownloadListener) mDownloadListener).onDownloadStart(url,
userAgent, contentDisposition, mimetype, referer, contentLength);
@@ -460,6 +480,7 @@ class CallbackProxy extends Handler {
case CREATE_WINDOW:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onCreateWindow");
if (!mWebChromeClient.onCreateWindow(mWebView.getWebView(),
msg.arg1 == 1, msg.arg2 == 1,
(Message) msg.obj)) {
@@ -473,12 +494,14 @@ class CallbackProxy extends Handler {
case REQUEST_FOCUS:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onRequestFocus");
mWebChromeClient.onRequestFocus(mWebView.getWebView());
}
break;
case CLOSE_WINDOW:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onCloseWindow");
mWebChromeClient.onCloseWindow(((WebViewClassic) msg.obj).getWebView());
}
break;
@@ -500,6 +523,7 @@ class CallbackProxy extends Handler {
case ASYNC_KEYEVENTS:
if (mWebViewClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onUnhandledKeyEvent");
mWebViewClient.onUnhandledKeyEvent(mWebView.getWebView(),
(KeyEvent) msg.obj);
}
@@ -521,6 +545,7 @@ class CallbackProxy extends Handler {
WebStorage.QuotaUpdater quotaUpdater =
(WebStorage.QuotaUpdater) map.get("quotaUpdater");
+ if (TRACE) Log.d(LOGTAG, "onExceededDatabaseQuota");
mWebChromeClient.onExceededDatabaseQuota(url,
databaseIdentifier, quota, estimatedDatabaseSize,
totalQuota, quotaUpdater);
@@ -538,6 +563,7 @@ class CallbackProxy extends Handler {
WebStorage.QuotaUpdater quotaUpdater =
(WebStorage.QuotaUpdater) map.get("quotaUpdater");
+ if (TRACE) Log.d(LOGTAG, "onReachedMaxAppCacheSize");
mWebChromeClient.onReachedMaxAppCacheSize(requiredStorage,
quota, quotaUpdater);
}
@@ -551,6 +577,7 @@ class CallbackProxy extends Handler {
GeolocationPermissions.Callback callback =
(GeolocationPermissions.Callback)
map.get("callback");
+ if (TRACE) Log.d(LOGTAG, "onGeolocationPermissionsShowPrompt");
mWebChromeClient.onGeolocationPermissionsShowPrompt(origin,
callback);
}
@@ -558,6 +585,7 @@ class CallbackProxy extends Handler {
case GEOLOCATION_PERMISSIONS_HIDE_PROMPT:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onGeolocationPermissionsHidePrompt");
mWebChromeClient.onGeolocationPermissionsHidePrompt();
}
break;
@@ -566,6 +594,7 @@ class CallbackProxy extends Handler {
if (mWebChromeClient != null) {
final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
JsDialogHelper helper = new JsDialogHelper(receiver.mJsResult, msg);
+ if (TRACE) Log.d(LOGTAG, "onJsAlert");
if (!helper.invokeCallback(mWebChromeClient, mWebView.getWebView())) {
helper.showDialog(mContext);
}
@@ -577,6 +606,7 @@ class CallbackProxy extends Handler {
if(mWebChromeClient != null) {
final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
final JsResult res = receiver.mJsResult;
+ if (TRACE) Log.d(LOGTAG, "onJsTimeout");
if (mWebChromeClient.onJsTimeout()) {
res.confirm();
} else {
@@ -598,6 +628,7 @@ class CallbackProxy extends Handler {
case SCALE_CHANGED:
if (mWebViewClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onScaleChanged");
mWebViewClient.onScaleChanged(mWebView.getWebView(), msg.getData()
.getFloat("old"), msg.getData().getFloat("new"));
}
@@ -624,6 +655,7 @@ class CallbackProxy extends Handler {
ConsoleMessage.MessageLevel messageLevel =
ConsoleMessage.MessageLevel.values()[msgLevel];
+ if (TRACE) Log.d(LOGTAG, "onConsoleMessage");
if (!mWebChromeClient.onConsoleMessage(new ConsoleMessage(message, sourceID,
lineNumber, messageLevel))) {
// If false was returned the user did not provide their own console function so
@@ -654,12 +686,14 @@ class CallbackProxy extends Handler {
case GET_VISITED_HISTORY:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "getVisitedHistory");
mWebChromeClient.getVisitedHistory((ValueCallback<String[]>)msg.obj);
}
break;
case OPEN_FILE_CHOOSER:
if (mWebChromeClient != null) {
+ if (TRACE) Log.d(LOGTAG, "openFileChooser");
UploadFileMessageData data = (UploadFileMessageData)msg.obj;
mWebChromeClient.openFileChooser(data.getUploadFile(), data.getAcceptType(),
data.getCapture());
@@ -668,6 +702,7 @@ class CallbackProxy extends Handler {
case ADD_HISTORY_ITEM:
if (mWebBackForwardListClient != null) {
+ if (TRACE) Log.d(LOGTAG, "onNewHistoryItem");
mWebBackForwardListClient.onNewHistoryItem(
(WebHistoryItem) msg.obj);
}
@@ -693,6 +728,7 @@ class CallbackProxy extends Handler {
String realm = msg.getData().getString("realm");
String account = msg.getData().getString("account");
String args = msg.getData().getString("args");
+ if (TRACE) Log.d(LOGTAG, "onReceivedLoginRequest");
mWebViewClient.onReceivedLoginRequest(mWebView.getWebView(), realm,
account, args);
}
@@ -910,6 +946,7 @@ class CallbackProxy extends Handler {
return null;
}
// Note: This method does _not_ send a message.
+ if (TRACE) Log.d(LOGTAG, "shouldInterceptRequest=" + url);
WebResourceResponse r =
mWebViewClient.shouldInterceptRequest(mWebView.getWebView(), url);
if (r == null) {
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
index 349113e..524f610 100644
--- a/core/java/android/webkit/DebugFlags.java
+++ b/core/java/android/webkit/DebugFlags.java
@@ -24,25 +24,33 @@ package android.webkit;
* The name of each flags maps directly to the name of the class in which that
* flag is used.
*
+ * @hide Only used by WebView implementations.
*/
-class DebugFlags {
+public class DebugFlags {
+ public static final boolean COOKIE_SYNC_MANAGER = false;
+ public static final boolean TRACE_API = false;
+ public static final boolean TRACE_CALLBACK = false;
+ public static final boolean TRACE_JAVASCRIPT_BRIDGE = false;
+ public static final boolean URL_UTIL = false;
+ public static final boolean WEB_SYNC_MANAGER = false;
+
+ // TODO: Delete these when WebViewClassic is moved
public static final boolean BROWSER_FRAME = false;
public static final boolean CACHE_MANAGER = false;
public static final boolean CALLBACK_PROXY = false;
public static final boolean COOKIE_MANAGER = false;
- public static final boolean COOKIE_SYNC_MANAGER = false;
public static final boolean FRAME_LOADER = false;
public static final boolean J_WEB_CORE_JAVA_BRIDGE = false;// HIGHLY VERBOSE
public static final boolean LOAD_LISTENER = false;
+ public static final boolean MEASURE_PAGE_SWAP_FPS = false;
public static final boolean NETWORK = false;
public static final boolean SSL_ERROR_HANDLER = false;
public static final boolean STREAM_LOADER = false;
- public static final boolean URL_UTIL = false;
public static final boolean WEB_BACK_FORWARD_LIST = false;
public static final boolean WEB_SETTINGS = false;
- public static final boolean WEB_SYNC_MANAGER = false;
public static final boolean WEB_VIEW = false;
public static final boolean WEB_VIEW_CORE = false;
- public static final boolean MEASURE_PAGE_SWAP_FPS = false;
+
+
}
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index b52218d..6fb32c8 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -19,6 +19,7 @@ package android.webkit;
import android.content.Context;
import android.media.MediaPlayer;
import android.media.Metadata;
+import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
@@ -293,12 +294,16 @@ public class HTML5VideoFullScreen extends HTML5VideoView
mLayout.setVisibility(View.VISIBLE);
WebChromeClient client = webView.getWebChromeClient();
if (client != null) {
+ if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onShowCustomView");
client.onShowCustomView(mLayout, mCallback);
// Plugins like Flash will draw over the video so hide
// them while we're playing.
if (webView.getViewManager() != null)
webView.getViewManager().hideAll();
+ if (DebugFlags.TRACE_CALLBACK) {
+ Log.d(CallbackProxy.LOGTAG, "getVideoLoadingProgressView");
+ }
mProgressView = client.getVideoLoadingProgressView();
if (mProgressView != null) {
mLayout.addView(mProgressView, layoutParams);
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index a3d62ae..e8538f6 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -180,6 +180,7 @@ class HTML5VideoViewProxy extends Handler
if (!mHTML5VideoView.fullScreenExited() && mHTML5VideoView.isFullScreenMode()) {
WebChromeClient client = webView.getWebChromeClient();
if (client != null) {
+ if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onHideCustomView");
client.onHideCustomView();
}
}
@@ -405,6 +406,7 @@ class HTML5VideoViewProxy extends Handler
case ERROR: {
WebChromeClient client = mWebView.getWebChromeClient();
if (client != null) {
+ if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onHideCustomView");
client.onHideCustomView();
}
break;
@@ -412,6 +414,9 @@ class HTML5VideoViewProxy extends Handler
case LOAD_DEFAULT_POSTER: {
WebChromeClient client = mWebView.getWebChromeClient();
if (client != null) {
+ if (DebugFlags.TRACE_CALLBACK) {
+ Log.d(CallbackProxy.LOGTAG, "getDefaultVideoPoster");
+ }
doSetPoster(client.getDefaultVideoPoster());
}
break;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index eded438..f0e8c4f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -244,7 +244,7 @@ public class WebView extends AbsoluteLayout
implements ViewTreeObserver.OnGlobalFocusChangeListener,
ViewGroup.OnHierarchyChangeListener, ViewDebug.HierarchyHandler {
- private static final String LOGTAG = "webview_proxy";
+ private static final String LOGTAG = "WebView";
// Throwing an exception for incorrect thread usage if the
// build target is JB MR2 or newer. Defaults to false, and is
@@ -496,6 +496,7 @@ public class WebView extends AbsoluteLayout
sEnforceThreadChecking = context.getApplicationInfo().targetSdkVersion >=
Build.VERSION_CODES.JELLY_BEAN_MR2;
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "WebView<init>");
ensureProviderCreated();
mProvider.init(javaScriptInterfaces, privateBrowsing);
@@ -510,6 +511,7 @@ public class WebView extends AbsoluteLayout
*/
public void setHorizontalScrollbarOverlay(boolean overlay) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setHorizontalScrollbarOverlay=" + overlay);
mProvider.setHorizontalScrollbarOverlay(overlay);
}
@@ -520,6 +522,7 @@ public class WebView extends AbsoluteLayout
*/
public void setVerticalScrollbarOverlay(boolean overlay) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setVerticalScrollbarOverlay=" + overlay);
mProvider.setVerticalScrollbarOverlay(overlay);
}
@@ -574,6 +577,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public void setCertificate(SslCertificate certificate) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setCertificate=" + certificate);
mProvider.setCertificate(certificate);
}
@@ -597,6 +601,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public void savePassword(String host, String username, String password) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "savePassword=" + host);
mProvider.savePassword(host, username, password);
}
@@ -616,6 +621,7 @@ public class WebView extends AbsoluteLayout
public void setHttpAuthUsernamePassword(String host, String realm,
String username, String password) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setHttpAuthUsernamePassword=" + host);
mProvider.setHttpAuthUsernamePassword(host, realm, username, password);
}
@@ -645,6 +651,7 @@ public class WebView extends AbsoluteLayout
*/
public void destroy() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "destroy");
mProvider.destroy();
}
@@ -683,6 +690,7 @@ public class WebView extends AbsoluteLayout
*/
public void setNetworkAvailable(boolean networkUp) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setNetworkAvailable=" + networkUp);
mProvider.setNetworkAvailable(networkUp);
}
@@ -699,6 +707,7 @@ public class WebView extends AbsoluteLayout
*/
public WebBackForwardList saveState(Bundle outState) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "saveState");
return mProvider.saveState(outState);
}
@@ -715,6 +724,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public boolean savePicture(Bundle b, final File dest) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "savePicture=" + dest.getName());
return mProvider.savePicture(b, dest);
}
@@ -732,6 +742,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public boolean restorePicture(Bundle b, File src) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "restorePicture=" + src.getName());
return mProvider.restorePicture(b, src);
}
@@ -749,6 +760,7 @@ public class WebView extends AbsoluteLayout
*/
public WebBackForwardList restoreState(Bundle inState) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "restoreState");
return mProvider.restoreState(inState);
}
@@ -765,6 +777,7 @@ public class WebView extends AbsoluteLayout
*/
public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "loadUrl(extra headers)=" + url);
mProvider.loadUrl(url, additionalHttpHeaders);
}
@@ -775,6 +788,7 @@ public class WebView extends AbsoluteLayout
*/
public void loadUrl(String url) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "loadUrl=" + url);
mProvider.loadUrl(url);
}
@@ -789,6 +803,7 @@ public class WebView extends AbsoluteLayout
*/
public void postUrl(String url, byte[] postData) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "postUrl=" + url);
mProvider.postUrl(url, postData);
}
@@ -823,6 +838,7 @@ public class WebView extends AbsoluteLayout
*/
public void loadData(String data, String mimeType, String encoding) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "loadData");
mProvider.loadData(data, mimeType, encoding);
}
@@ -855,6 +871,7 @@ public class WebView extends AbsoluteLayout
public void loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String historyUrl) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "loadDataWithBaseURL=" + baseUrl);
mProvider.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
@@ -871,6 +888,7 @@ public class WebView extends AbsoluteLayout
*/
public void evaluateJavascript(String script, ValueCallback<String> resultCallback) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "evaluateJavascript=" + script);
mProvider.evaluateJavaScript(script, resultCallback);
}
@@ -881,6 +899,7 @@ public class WebView extends AbsoluteLayout
*/
public void saveWebArchive(String filename) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "saveWebArchive=" + filename);
mProvider.saveWebArchive(filename);
}
@@ -898,6 +917,7 @@ public class WebView extends AbsoluteLayout
*/
public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "saveWebArchive(auto)=" + basename);
mProvider.saveWebArchive(basename, autoname, callback);
}
@@ -906,6 +926,7 @@ public class WebView extends AbsoluteLayout
*/
public void stopLoading() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "stopLoading");
mProvider.stopLoading();
}
@@ -914,6 +935,7 @@ public class WebView extends AbsoluteLayout
*/
public void reload() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "reload");
mProvider.reload();
}
@@ -932,6 +954,7 @@ public class WebView extends AbsoluteLayout
*/
public void goBack() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "goBack");
mProvider.goBack();
}
@@ -950,6 +973,7 @@ public class WebView extends AbsoluteLayout
*/
public void goForward() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "goForward");
mProvider.goForward();
}
@@ -975,6 +999,7 @@ public class WebView extends AbsoluteLayout
*/
public void goBackOrForward(int steps) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "goBackOrForwad=" + steps);
mProvider.goBackOrForward(steps);
}
@@ -994,6 +1019,7 @@ public class WebView extends AbsoluteLayout
*/
public boolean pageUp(boolean top) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "pageUp");
return mProvider.pageUp(top);
}
@@ -1005,6 +1031,7 @@ public class WebView extends AbsoluteLayout
*/
public boolean pageDown(boolean bottom) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "pageDown");
return mProvider.pageDown(bottom);
}
@@ -1017,6 +1044,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public void clearView() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearView");
mProvider.clearView();
}
@@ -1036,6 +1064,7 @@ public class WebView extends AbsoluteLayout
*/
public Picture capturePicture() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "capturePicture");
return mProvider.capturePicture();
}
@@ -1073,6 +1102,7 @@ public class WebView extends AbsoluteLayout
ValueCallback<Boolean> resultCallback, CancellationSignal cancellationSignal)
throws java.io.IOException {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "exportToPdf");
mProvider.exportToPdf(fd, attributes, resultCallback, cancellationSignal);
}
@@ -1104,6 +1134,7 @@ public class WebView extends AbsoluteLayout
*/
public void setInitialScale(int scaleInPercent) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setInitialScale=" + scaleInPercent);
mProvider.setInitialScale(scaleInPercent);
}
@@ -1114,6 +1145,7 @@ public class WebView extends AbsoluteLayout
*/
public void invokeZoomPicker() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "invokeZoomPicker");
mProvider.invokeZoomPicker();
}
@@ -1137,6 +1169,7 @@ public class WebView extends AbsoluteLayout
*/
public HitTestResult getHitTestResult() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "getHitTestResult");
return mProvider.getHitTestResult();
}
@@ -1155,6 +1188,7 @@ public class WebView extends AbsoluteLayout
*/
public void requestFocusNodeHref(Message hrefMsg) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "requestFocusNodeHref");
mProvider.requestFocusNodeHref(hrefMsg);
}
@@ -1167,6 +1201,7 @@ public class WebView extends AbsoluteLayout
*/
public void requestImageRef(Message msg) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "requestImageRef");
mProvider.requestImageRef(msg);
}
@@ -1271,6 +1306,7 @@ public class WebView extends AbsoluteLayout
*/
public void pauseTimers() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "pauseTimers");
mProvider.pauseTimers();
}
@@ -1280,6 +1316,7 @@ public class WebView extends AbsoluteLayout
*/
public void resumeTimers() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "resumeTimers");
mProvider.resumeTimers();
}
@@ -1292,6 +1329,7 @@ public class WebView extends AbsoluteLayout
*/
public void onPause() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "onPause");
mProvider.onPause();
}
@@ -1300,6 +1338,7 @@ public class WebView extends AbsoluteLayout
*/
public void onResume() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "onResume");
mProvider.onResume();
}
@@ -1319,6 +1358,7 @@ public class WebView extends AbsoluteLayout
*/
public void freeMemory() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "freeMemory");
mProvider.freeMemory();
}
@@ -1330,6 +1370,7 @@ public class WebView extends AbsoluteLayout
*/
public void clearCache(boolean includeDiskFiles) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearCache");
mProvider.clearCache(includeDiskFiles);
}
@@ -1341,6 +1382,7 @@ public class WebView extends AbsoluteLayout
*/
public void clearFormData() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearFormData");
mProvider.clearFormData();
}
@@ -1349,6 +1391,7 @@ public class WebView extends AbsoluteLayout
*/
public void clearHistory() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearHistory");
mProvider.clearHistory();
}
@@ -1358,6 +1401,7 @@ public class WebView extends AbsoluteLayout
*/
public void clearSslPreferences() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearSslPreferences");
mProvider.clearSslPreferences();
}
@@ -1399,6 +1443,7 @@ public class WebView extends AbsoluteLayout
*/
public void findNext(boolean forward) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "findNext");
mProvider.findNext(forward);
}
@@ -1414,6 +1459,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public int findAll(String find) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "findAll");
StrictMode.noteSlowCall("findAll blocks UI: prefer findAllAsync");
return mProvider.findAll(find);
}
@@ -1428,6 +1474,7 @@ public class WebView extends AbsoluteLayout
*/
public void findAllAsync(String find) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "findAllAsync");
mProvider.findAllAsync(find);
}
@@ -1448,6 +1495,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public boolean showFindDialog(String text, boolean showIme) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "showFindDialog");
return mProvider.showFindDialog(text, showIme);
}
@@ -1483,6 +1531,7 @@ public class WebView extends AbsoluteLayout
*/
public void clearMatches() {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearMatches");
mProvider.clearMatches();
}
@@ -1543,6 +1592,7 @@ public class WebView extends AbsoluteLayout
@Deprecated
public void setPictureListener(PictureListener listener) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "setPictureListener=" + listener);
mProvider.setPictureListener(listener);
}
@@ -1592,6 +1642,7 @@ public class WebView extends AbsoluteLayout
*/
public void addJavascriptInterface(Object object, String name) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "addJavascriptInterface=" + name);
mProvider.addJavascriptInterface(object, name);
}
@@ -1604,6 +1655,7 @@ public class WebView extends AbsoluteLayout
*/
public void removeJavascriptInterface(String name) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "removeJavascriptInterface=" + name);
mProvider.removeJavascriptInterface(name);
}
@@ -1693,6 +1745,7 @@ public class WebView extends AbsoluteLayout
public void flingScroll(int vx, int vy) {
checkThread();
+ if (DebugFlags.TRACE_API) Log.d(LOGTAG, "flingScroll");
mProvider.flingScroll(vx, vy);
}
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index b1a7878..3f22d53 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -7951,6 +7951,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
// triggered in setNewPicture
Picture picture = mContext.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.JELLY_BEAN_MR2 ? capturePicture() : null;
+ if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onNewPicture");
mPictureListener.onNewPicture(getWebView(), picture);
}
}
@@ -8038,6 +8039,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
// triggered in pageSwapCallback
Picture picture = mContext.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.JELLY_BEAN_MR2 ? capturePicture() : null;
+ if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onNewPicture");
mPictureListener.onNewPicture(getWebView(), picture);
}
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index be47bf0..c308024 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -4924,11 +4924,37 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
* Scrolls the list items within the view by a specified number of pixels.
*
* @param y the amount of pixels to scroll by vertically
- * @return true if the list is able to scroll, or false if the list is
- * already at the beginning/end and unable to scroll any more.
+ * @see #canScrollList(int)
*/
- public boolean scrollListBy(int y) {
- return !trackMotionScroll(-y, -y);
+ public void scrollListBy(int y) {
+ trackMotionScroll(-y, -y);
+ }
+
+ /**
+ * Check if the items in the list can be scrolled in a certain direction.
+ *
+ * @param direction Negative to check scrolling up, positive to check
+ * scrolling down.
+ * @return true if the list can be scrolled in the specified direction,
+ * false otherwise.
+ * @see #scrollListBy(int)
+ */
+ public boolean canScrollList(int direction) {
+ final int childCount = getChildCount();
+ if (childCount == 0) {
+ return false;
+ }
+
+ final int firstPosition = mFirstPosition;
+ final Rect listPadding = mListPadding;
+ if (direction > 0) {
+ final int lastBottom = getChildAt(childCount - 1).getBottom();
+ final int lastPosition = firstPosition + childCount;
+ return lastPosition < mItemCount || lastBottom > getHeight() - listPadding.bottom;
+ } else {
+ final int firstTop = getChildAt(0).getTop();
+ return firstPosition > 0 || firstTop < listPadding.top;
+ }
}
/**
diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java
index 778c8db..dff1531 100644
--- a/core/java/android/widget/ActivityChooserView.java
+++ b/core/java/android/widget/ActivityChooserView.java
@@ -29,12 +29,14 @@ import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.ActionProvider;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ActivityChooserModel.ActivityChooserModelClient;
+import android.widget.ListPopupWindow.ForwardingListener;
/**
* This class is a view for choosing an activity for handling a given {@link Intent}.
@@ -228,17 +230,37 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
mDefaultActivityButton.setOnLongClickListener(mCallbacks);
mDefaultActivityButtonImage = (ImageView) mDefaultActivityButton.findViewById(R.id.image);
- mExpandActivityOverflowButton = (FrameLayout) findViewById(R.id.expand_activities_button);
- mExpandActivityOverflowButton.setOnClickListener(mCallbacks);
- mExpandActivityOverflowButton.setAccessibilityDelegate(new AccessibilityDelegate() {
+ final FrameLayout expandButton = (FrameLayout) findViewById(R.id.expand_activities_button);
+ expandButton.setOnClickListener(mCallbacks);
+ expandButton.setAccessibilityDelegate(new AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.setCanOpenPopup(true);
}
});
+ expandButton.setOnTouchListener(new ForwardingListener(expandButton) {
+ @Override
+ public ListPopupWindow getPopup() {
+ return getListPopupWindow();
+ }
+
+ @Override
+ protected boolean onForwardingStarted() {
+ showPopup();
+ return true;
+ }
+
+ @Override
+ protected boolean onForwardingStopped() {
+ dismissPopup();
+ return true;
+ }
+ });
+ mExpandActivityOverflowButton = expandButton;
+
mExpandActivityOverflowButtonImage =
- (ImageView) mExpandActivityOverflowButton.findViewById(R.id.image);
+ (ImageView) expandButton.findViewById(R.id.image);
mExpandActivityOverflowButtonImage.setImageDrawable(expandActivityOverflowButtonDrawable);
mAdapter = new ActivityChooserViewAdapter();
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index f2da765..b7e1fdd 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -974,10 +974,12 @@ public class ListPopupWindow {
* currently touched list item.
* <p>
* Example usage:
- * <pre>ListPopupWindow myPopup = new ListPopupWindow(context);
+ * <pre>
+ * ListPopupWindow myPopup = new ListPopupWindow(context);
* myPopup.setAnchor(myAnchor);
* OnTouchListener dragListener = myPopup.createDragToOpenListener(myAnchor);
- * myAnchor.setOnTouchListener(dragListener);</pre>
+ * myAnchor.setOnTouchListener(dragListener);
+ * </pre>
*
* @param src the view on which the resulting listener will be set
* @return a touch listener that controls drag-to-open behavior
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index e5344c6..603db70 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -82,8 +82,10 @@ public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback {
* currently touched list item.
* <p>
* Example usage:
- * <pre>PopupMenu myPopup = new PopupMenu(context, myAnchor);
- * myAnchor.setOnTouchListener(myPopup.getDragToOpenListener());</pre>
+ * <pre>
+ * PopupMenu myPopup = new PopupMenu(context, myAnchor);
+ * myAnchor.setOnTouchListener(myPopup.getDragToOpenListener());
+ * </pre>
*
* @return a touch listener that controls drag-to-open behavior
*/
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index f73e2c4..b9b6b08 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -462,6 +462,7 @@ public class RelativeLayout extends ViewGroup {
views = mSortedVerticalChildren;
count = views.length;
+ final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
for (int i = 0; i < count; i++) {
View child = views[i];
@@ -476,14 +477,26 @@ public class RelativeLayout extends ViewGroup {
if (isWrapContentWidth) {
if (isLayoutRtl()) {
- width = Math.max(width, myWidth - params.mLeft);
+ if (targetSdkVersion < Build.VERSION_CODES.KEY_LIME_PIE) {
+ width = Math.max(width, myWidth - params.mLeft);
+ } else {
+ width = Math.max(width, myWidth - params.mLeft - params.leftMargin);
+ }
} else {
- width = Math.max(width, params.mRight);
+ if (targetSdkVersion < Build.VERSION_CODES.KEY_LIME_PIE) {
+ width = Math.max(width, params.mRight);
+ } else {
+ width = Math.max(width, params.mRight + params.rightMargin);
+ }
}
}
if (isWrapContentHeight) {
- height = Math.max(height, params.mBottom);
+ if (targetSdkVersion < Build.VERSION_CODES.KEY_LIME_PIE) {
+ height = Math.max(height, params.mBottom);
+ } else {
+ height = Math.max(height, params.mBottom + params.bottomMargin);
+ }
}
if (child != ignore || verticalGravity) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a2d48a8..3c9cc98 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1519,6 +1519,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* with this TextView. By default there is no associated UndoManager, so null
* is returned. One can be associated with the TextView through
* {@link #setUndoManager(android.content.UndoManager, String)}
+ *
+ * @hide
*/
public final UndoManager getUndoManager() {
return mEditor == null ? null : mEditor.mUndoManager;
@@ -1535,6 +1537,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @param tag String tag identifying this particular TextView owner in the
* UndoManager. This is used to keep the correct association with the
* {@link android.content.UndoOwner} of any operations inside of the UndoManager.
+ *
+ * @hide
*/
public final void setUndoManager(UndoManager undoManager, String tag) {
if (undoManager != null) {
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index ebf9fe0..f449797 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -21,15 +21,20 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
+import android.graphics.Canvas;
import android.media.AudioManager;
+import android.media.MediaFormat;
import android.media.MediaPlayer;
-import android.media.Metadata;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
+import android.media.Metadata;
+import android.media.SubtitleController;
+import android.media.WebVttRenderer;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Pair;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
@@ -40,7 +45,10 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.MediaController.MediaPlayerControl;
import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
import java.util.Map;
+import java.util.Vector;
/**
* Displays a video file. The VideoView class
@@ -49,7 +57,8 @@ import java.util.Map;
* it can be used in any layout manager, and provides various display options
* such as scaling and tinting.
*/
-public class VideoView extends SurfaceView implements MediaPlayerControl {
+public class VideoView extends SurfaceView
+ implements MediaPlayerControl, SubtitleController.Anchor {
private String TAG = "VideoView";
// settable by the client
private Uri mUri;
@@ -91,6 +100,15 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
private boolean mCanSeekBack;
private boolean mCanSeekForward;
+ /** List of views overlaid on top of the video. */
+ private ArrayList<View> mOverlays;
+
+ /**
+ * Listener for overlay layout changes. Invalidates the video view to ensure
+ * that captions are redrawn whenever their layout changes.
+ */
+ private OnLayoutChangeListener mOverlayLayoutListener;
+
public VideoView(Context context) {
super(context);
initVideoView();
@@ -194,6 +212,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
+ mPendingSubtitleTracks = new Vector<Pair<InputStream, MediaFormat>>();
mCurrentState = STATE_IDLE;
mTargetState = STATE_IDLE;
}
@@ -218,6 +237,43 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
invalidate();
}
+ /**
+ * Adds an external subtitle source file (from the provided input stream.)
+ *
+ * Note that a single external subtitle source may contain multiple or no
+ * supported tracks in it. If the source contained at least one track in
+ * it, one will receive an {@link MediaPlayer#MEDIA_INFO_METADATA_UPDATE}
+ * info message. Otherwise, if reading the source takes excessive time,
+ * one will receive a {@link MediaPlayer#MEDIA_INFO_SUBTITLE_TIMED_OUT}
+ * message. If the source contained no supported track (including an empty
+ * source file or null input stream), one will receive a {@link
+ * MediaPlayer#MEDIA_INFO_UNSUPPORTED_SUBTITLE} message. One can find the
+ * total number of available tracks using {@link MediaPlayer#getTrackInfo()}
+ * to see what additional tracks become available after this method call.
+ *
+ * @param is input stream containing the subtitle data. It will be
+ * closed by the media framework.
+ * @param format the format of the subtitle track(s). Must contain at least
+ * the mime type ({@link MediaFormat#KEY_MIME}) and the
+ * language ({@link MediaFormat#KEY_LANGUAGE}) of the file.
+ * If the file itself contains the language information,
+ * specify "und" for the language.
+ */
+ public void addSubtitleSource(InputStream is, MediaFormat format) {
+ if (mMediaPlayer == null) {
+ mPendingSubtitleTracks.add(Pair.create(is, format));
+ } else {
+ try {
+ mMediaPlayer.addSubtitleSource(is, format);
+ } catch (IllegalStateException e) {
+ mInfoListener.onInfo(
+ mMediaPlayer, MediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE, 0);
+ }
+ }
+ }
+
+ private Vector<Pair<InputStream, MediaFormat>> mPendingSubtitleTracks;
+
public void stopPlayback() {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
@@ -244,6 +300,15 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
release(false);
try {
mMediaPlayer = new MediaPlayer();
+ // TODO: create SubtitleController in MediaPlayer, but we need
+ // a context for the subtitle renderers
+ SubtitleController controller = new SubtitleController(
+ getContext(),
+ mMediaPlayer.getMediaTimeProvider(),
+ mMediaPlayer);
+ controller.registerRenderer(new WebVttRenderer(getContext(), null));
+ mMediaPlayer.setSubtitleAnchor(controller, this);
+
if (mAudioSession != 0) {
mMediaPlayer.setAudioSessionId(mAudioSession);
} else {
@@ -253,7 +318,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
- mMediaPlayer.setOnInfoListener(mOnInfoListener);
+ mMediaPlayer.setOnInfoListener(mInfoListener);
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mCurrentBufferPercentage = 0;
mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
@@ -261,6 +326,16 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.prepareAsync();
+
+ for (Pair<InputStream, MediaFormat> pending: mPendingSubtitleTracks) {
+ try {
+ mMediaPlayer.addSubtitleSource(pending.first, pending.second);
+ } catch (IllegalStateException e) {
+ mInfoListener.onInfo(
+ mMediaPlayer, MediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE, 0);
+ }
+ }
+
// we don't set the target state here either, but preserve the
// target state that was there before.
mCurrentState = STATE_PREPARING;
@@ -277,6 +352,8 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
mTargetState = STATE_ERROR;
mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
return;
+ } finally {
+ mPendingSubtitleTracks.clear();
}
}
@@ -386,6 +463,16 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
}
};
+ private MediaPlayer.OnInfoListener mInfoListener =
+ new MediaPlayer.OnInfoListener() {
+ public boolean onInfo(MediaPlayer mp, int arg1, int arg2) {
+ if (mOnInfoListener != null) {
+ mOnInfoListener.onInfo(mp, arg1, arg2);
+ }
+ return true;
+ }
+ };
+
private MediaPlayer.OnErrorListener mErrorListener =
new MediaPlayer.OnErrorListener() {
public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
@@ -530,6 +617,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;
+ mPendingSubtitleTracks.clear();
mCurrentState = STATE_IDLE;
if (cleartargetstate) {
mTargetState = STATE_IDLE;
@@ -702,4 +790,119 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
}
return mAudioSession;
}
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ // Layout overlay views, if necessary.
+ if (changed && mOverlays != null && !mOverlays.isEmpty()) {
+ measureAndLayoutOverlays();
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+
+ final int count = mOverlays.size();
+ for (int i = 0; i < count; i++) {
+ final View overlay = mOverlays.get(i);
+ overlay.draw(canvas);
+ }
+ }
+
+ /**
+ * Adds a view to be overlaid on top of this video view. During layout, the
+ * view will be forced to match the bounds, less padding, of the video view.
+ * <p>
+ * Overlays are drawn in the order they are added. The last added overlay
+ * will be drawn on top.
+ *
+ * @param overlay the view to overlay
+ * @see #removeOverlay(View)
+ */
+ private void addOverlay(View overlay) {
+ if (mOverlays == null) {
+ mOverlays = new ArrayList<View>(1);
+ }
+
+ if (mOverlayLayoutListener == null) {
+ mOverlayLayoutListener = new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ invalidate();
+ }
+ };
+ }
+
+ if (mOverlays.isEmpty()) {
+ setWillNotDraw(false);
+ }
+
+ mOverlays.add(overlay);
+ overlay.addOnLayoutChangeListener(mOverlayLayoutListener);
+ measureAndLayoutOverlays();
+ }
+
+ /**
+ * Removes a view previously added using {@link #addOverlay}.
+ *
+ * @param overlay the view to remove
+ * @see #addOverlay(View)
+ */
+ private void removeOverlay(View overlay) {
+ if (mOverlays == null) {
+ return;
+ }
+
+ overlay.removeOnLayoutChangeListener(mOverlayLayoutListener);
+ mOverlays.remove(overlay);
+
+ if (mOverlays.isEmpty()) {
+ setWillNotDraw(true);
+ }
+
+ invalidate();
+ }
+
+ /**
+ * Forces a measurement and layout pass for all overlaid views.
+ *
+ * @see #addOverlay(View)
+ */
+ private void measureAndLayoutOverlays() {
+ final int left = getPaddingLeft();
+ final int top = getPaddingTop();
+ final int right = getWidth() - left - getPaddingRight();
+ final int bottom = getHeight() - top - getPaddingBottom();
+ final int widthSpec = MeasureSpec.makeMeasureSpec(right - left, MeasureSpec.EXACTLY);
+ final int heightSpec = MeasureSpec.makeMeasureSpec(bottom - top, MeasureSpec.EXACTLY);
+
+ final int count = mOverlays.size();
+ for (int i = 0; i < count; i++) {
+ final View overlay = mOverlays.get(i);
+ overlay.measure(widthSpec, heightSpec);
+ overlay.layout(left, top, right, bottom);
+ }
+ }
+
+ /** @hide */
+ @Override
+ public void setSubtitleView(View view) {
+ if (mSubtitleView == view) {
+ return;
+ }
+
+ if (mSubtitleView != null) {
+ removeOverlay(mSubtitleView);
+ }
+ mSubtitleView = view;
+ if (mSubtitleView != null) {
+ addOverlay(mSubtitleView);
+ }
+ }
+
+ private View mSubtitleView;
}