Merge "Fix launching logic."
diff --git a/Android.mk b/Android.mk
index e82c133..3f323e5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -126,6 +126,7 @@
core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
core/java/android/hardware/location/IGeofenceHardware.aidl \
core/java/android/hardware/location/IGeofenceHardwareCallback.aidl \
+ core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl \
core/java/android/hardware/usb/IUsbManager.aidl \
core/java/android/net/IConnectivityManager.aidl \
core/java/android/net/INetworkManagementEventObserver.aidl \
diff --git a/api/current.txt b/api/current.txt
index fcfeea9..74cc93d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -512,6 +512,7 @@
field public static final int freezesText = 16843116; // 0x101016c
field public static final int fromAlpha = 16843210; // 0x10101ca
field public static final int fromDegrees = 16843187; // 0x10101b3
+ field public static final int fromScene = 16843737; // 0x10103d9
field public static final int fromXDelta = 16843206; // 0x10101c6
field public static final int fromXScale = 16843202; // 0x10101c2
field public static final int fromYDelta = 16843208; // 0x10101c8
@@ -1012,6 +1013,7 @@
field public static final int targetActivity = 16843266; // 0x1010202
field public static final int targetClass = 16842799; // 0x101002f
field public static final int targetDescriptions = 16843680; // 0x10103a0
+ field public static final int targetID = 16843736; // 0x10103d8
field public static final int targetPackage = 16842785; // 0x1010021
field public static final int targetSdkVersion = 16843376; // 0x1010270
field public static final int taskAffinity = 16842770; // 0x1010012
@@ -1100,6 +1102,7 @@
field public static final int titleTextStyle = 16843512; // 0x10102f8
field public static final int toAlpha = 16843211; // 0x10101cb
field public static final int toDegrees = 16843188; // 0x10101b4
+ field public static final int toScene = 16843738; // 0x10103da
field public static final int toXDelta = 16843207; // 0x10101c7
field public static final int toXScale = 16843203; // 0x10101c3
field public static final int toYDelta = 16843209; // 0x10101c9
@@ -1114,6 +1117,7 @@
field public static final int transcriptMode = 16843008; // 0x1010100
field public static final int transformPivotX = 16843552; // 0x1010320
field public static final int transformPivotY = 16843553; // 0x1010321
+ field public static final int transition = 16843739; // 0x10103db
field public static final int translationX = 16843554; // 0x1010322
field public static final int translationY = 16843555; // 0x1010323
field public static final int type = 16843169; // 0x10101a1
@@ -2307,28 +2311,23 @@
package android.animation {
- public abstract interface Animatable {
- method public abstract long getDuration();
- method public abstract android.animation.TimeInterpolator getInterpolator();
- method public abstract long getStartDelay();
- method public abstract android.animation.Animatable setDuration(long);
- method public abstract void setInterpolator(android.animation.TimeInterpolator);
- method public abstract void setStartDelay(long);
- }
-
- public abstract class Animator implements android.animation.Animatable java.lang.Cloneable {
+ public abstract class Animator implements java.lang.Cloneable {
ctor public Animator();
method public void addListener(android.animation.Animator.AnimatorListener);
method public void cancel();
method public android.animation.Animator clone();
method public void end();
+ method public abstract long getDuration();
method public android.animation.TimeInterpolator getInterpolator();
method public java.util.ArrayList<android.animation.Animator.AnimatorListener> getListeners();
+ method public abstract long getStartDelay();
method public abstract boolean isRunning();
method public boolean isStarted();
method public void removeAllListeners();
method public void removeListener(android.animation.Animator.AnimatorListener);
method public abstract android.animation.Animator setDuration(long);
+ method public abstract void setInterpolator(android.animation.TimeInterpolator);
+ method public abstract void setStartDelay(long);
method public void setTarget(java.lang.Object);
method public void setupEndValues();
method public void setupStartValues();
@@ -4634,13 +4633,11 @@
method public boolean isEnabled();
method public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
method public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
- method public boolean registerCallback(android.bluetooth.BluetoothAdapterCallback);
method public boolean setName(java.lang.String);
method public boolean startDiscovery();
- method public boolean startLeScan();
- method public boolean startLeScan(java.util.UUID[]);
- method public void stopLeScan();
- method public boolean unRegisterCallback(android.bluetooth.BluetoothAdapterCallback);
+ method public boolean startLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback);
+ method public boolean startLeScan(java.util.UUID[], android.bluetooth.BluetoothAdapter.LeScanCallback);
+ method public void stopLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback);
field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
field public static final java.lang.String ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
field public static final java.lang.String ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
@@ -4671,12 +4668,8 @@
field public static final int STATE_TURNING_ON = 11; // 0xb
}
- public abstract class BluetoothAdapterCallback {
- ctor public BluetoothAdapterCallback();
- method public void onCallbackRegistration(int);
- method public void onLeScan(android.bluetooth.BluetoothDevice, int, byte[]);
- field public static final int CALLBACK_REGISTERED = 0; // 0x0
- field public static final int CALLBACK_REGISTRATION_FAILURE = 1; // 0x1
+ public static abstract interface BluetoothAdapter.LeScanCallback {
+ method public abstract void onLeScan(android.bluetooth.BluetoothDevice, int, byte[]);
}
public class BluetoothAssignedNumbers {
@@ -5619,7 +5612,6 @@
method public abstract java.lang.String[] fileList();
method public abstract android.content.Context getApplicationContext();
method public abstract android.content.pm.ApplicationInfo getApplicationInfo();
- method public java.util.List<android.content.RestrictionEntry> getApplicationRestrictions();
method public abstract android.content.res.AssetManager getAssets();
method public abstract java.io.File getCacheDir();
method public abstract java.lang.ClassLoader getClassLoader();
@@ -6207,8 +6199,9 @@
field public static final java.lang.String EXTRA_REFERRER = "android.intent.extra.REFERRER";
field public static final java.lang.String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token";
field public static final java.lang.String EXTRA_REPLACING = "android.intent.extra.REPLACING";
- field public static final java.lang.String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions";
+ field public static final java.lang.String EXTRA_RESTRICTIONS_BUNDLE = "android.intent.extra.restrictions_bundle";
field public static final java.lang.String EXTRA_RESTRICTIONS_INTENT = "android.intent.extra.restrictions_intent";
+ field public static final java.lang.String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list";
field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
@@ -10479,13 +10472,14 @@
package android.hardware.location {
public final class GeofenceHardware {
- method public boolean addCircularFence(int, double, double, double, int, int, int, int, int, android.hardware.location.GeofenceHardwareCallback);
- method public int[] getMonitoringTypesAndStatus();
+ method public boolean addGeofence(int, int, android.hardware.location.GeofenceHardwareRequest, android.hardware.location.GeofenceHardwareCallback);
+ method public int[] getMonitoringTypes();
+ method public int getStatusOfMonitoringType(int);
method public boolean pauseGeofence(int, int);
- method public boolean registerForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareCallback);
+ method public boolean registerForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareMonitorCallback);
method public boolean removeGeofence(int, int);
method public boolean resumeGeofence(int, int, int);
- method public boolean unregisterForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareCallback);
+ method public boolean unregisterForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareMonitorCallback);
field public static final int GEOFENCE_ENTERED = 1; // 0x1
field public static final int GEOFENCE_ERROR_ID_EXISTS = 2; // 0x2
field public static final int GEOFENCE_ERROR_ID_UNKNOWN = 3; // 0x3
@@ -10504,13 +10498,33 @@
public abstract class GeofenceHardwareCallback {
ctor public GeofenceHardwareCallback();
method public void onGeofenceAdd(int, int);
- method public void onGeofenceChange(int, int, android.location.Location, long, int);
method public void onGeofencePause(int, int);
method public void onGeofenceRemove(int, int);
method public void onGeofenceResume(int, int);
+ method public void onGeofenceTransition(int, int, android.location.Location, long, int);
+ }
+
+ public abstract class GeofenceHardwareMonitorCallback {
+ ctor public GeofenceHardwareMonitorCallback();
method public void onMonitoringSystemChange(int, boolean, android.location.Location);
}
+ public final class GeofenceHardwareRequest {
+ ctor public GeofenceHardwareRequest();
+ method public static android.hardware.location.GeofenceHardwareRequest createCircularGeofence(double, double, double);
+ method public int getLastTransition();
+ method public double getLatitude();
+ method public double getLongitude();
+ method public int getMonitorTransitions();
+ method public int getNotificationResponsiveness();
+ method public double getRadius();
+ method public int getUnknownTimer();
+ method public void setLastTransition(int);
+ method public void setMonitorTransitions(int);
+ method public void setNotificationResponsiveness(int);
+ method public void setUnknownTimer(int);
+ }
+
}
package android.hardware.usb {
@@ -11804,6 +11818,66 @@
ctor public MediaCryptoException(java.lang.String);
}
+ public final class MediaDrm {
+ ctor public MediaDrm(java.util.UUID) throws android.media.MediaDrmException;
+ method public void closeSession(byte[]);
+ method public android.media.MediaDrm.CryptoSession getCryptoSession(byte[], java.lang.String, java.lang.String);
+ method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
+ method public byte[] getPropertyByteArray(java.lang.String);
+ method public java.lang.String getPropertyString(java.lang.String);
+ method public android.media.MediaDrm.ProvisionRequest getProvisionRequest();
+ method public java.util.List<byte[]> getSecureStops();
+ method public static final boolean isCryptoSchemeSupported(java.util.UUID);
+ method public byte[] openSession();
+ method public byte[] provideKeyResponse(byte[], byte[]);
+ method public void provideProvisionResponse(byte[]);
+ method public java.util.HashMap<java.lang.String, java.lang.String> queryKeyStatus(byte[]);
+ method public final void release();
+ method public void releaseSecureStops(byte[]);
+ method public void removeKeys(byte[]);
+ method public void restoreKeys(byte[], byte[]);
+ method public void setOnEventListener(android.media.MediaDrm.OnEventListener);
+ method public void setPropertyByteArray(java.lang.String, byte[]);
+ method public void setPropertyString(java.lang.String, java.lang.String);
+ field public static final int EVENT_KEY_EXPIRED = 3; // 0x3
+ field public static final int EVENT_KEY_REQUIRED = 2; // 0x2
+ field public static final int EVENT_PROVISION_REQUIRED = 1; // 0x1
+ field public static final int EVENT_VENDOR_DEFINED = 4; // 0x4
+ field public static final int KEY_TYPE_OFFLINE = 2; // 0x2
+ field public static final int KEY_TYPE_RELEASE = 3; // 0x3
+ field public static final int KEY_TYPE_STREAMING = 1; // 0x1
+ field public static final java.lang.String PROPERTY_ALGORITHM = "algorithm";
+ field public static final java.lang.String PROPERTY_DESCRIPTION = "description";
+ field public static final java.lang.String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
+ field public static final java.lang.String PROPERTY_VENDOR = "vendor";
+ field public static final java.lang.String PROPERTY_VERSION = "version";
+ }
+
+ public final class MediaDrm.CryptoSession {
+ method public byte[] decrypt(byte[], byte[], byte[]);
+ method public byte[] encrypt(byte[], byte[], byte[]);
+ method public byte[] sign(byte[], byte[]);
+ method public boolean verify(byte[], byte[], byte[]);
+ }
+
+ public static final class MediaDrm.KeyRequest {
+ method public byte[] getData();
+ method public java.lang.String getDefaultUrl();
+ }
+
+ public static abstract interface MediaDrm.OnEventListener {
+ method public abstract void onEvent(android.media.MediaDrm, byte[], int, int, byte[]);
+ }
+
+ public static final class MediaDrm.ProvisionRequest {
+ method public byte[] getData();
+ method public java.lang.String getDefaultUrl();
+ }
+
+ public final class MediaDrmException extends java.lang.Exception {
+ ctor public MediaDrmException(java.lang.String);
+ }
+
public final class MediaExtractor {
ctor public MediaExtractor();
method public boolean advance();
@@ -17560,7 +17634,7 @@
}
public class UserManager {
- method public static synchronized android.os.UserManager get(android.content.Context);
+ method public android.os.Bundle getApplicationRestrictions(java.lang.String);
method public long getSerialNumberForUser(android.os.UserHandle);
method public int getUserCount();
method public android.os.UserHandle getUserForSerialNumber(long);
@@ -21499,7 +21573,7 @@
method public int getLac();
method public int getMcc();
method public int getMnc();
- method public int getPsc();
+ method public deprecated int getPsc();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
@@ -21515,6 +21589,17 @@
field public static final android.os.Parcelable.Creator CREATOR;
}
+ public final class CellIdentityWcdma implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCid();
+ method public int getLac();
+ method public int getMcc();
+ method public int getMnc();
+ method public int getPsc();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
public abstract class CellInfo implements android.os.Parcelable {
method public int describeContents();
method public long getTimeStamp();
@@ -21544,6 +21629,13 @@
field public static final android.os.Parcelable.Creator CREATOR;
}
+ public final class CellInfoWcdma extends android.telephony.CellInfo implements android.os.Parcelable {
+ method public android.telephony.CellIdentityWcdma getCellIdentity();
+ method public android.telephony.CellSignalStrengthWcdma getCellSignalStrength();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
public abstract class CellLocation {
ctor public CellLocation();
method public static android.telephony.CellLocation getEmpty();
@@ -21599,6 +21691,17 @@
field public static final android.os.Parcelable.Creator CREATOR;
}
+ public final class CellSignalStrengthWcdma extends android.telephony.CellSignalStrength implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean equals(java.lang.Object);
+ method public int getAsuLevel();
+ method public int getDbm();
+ method public int getLevel();
+ method public int hashCode();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
public class NeighboringCellInfo implements android.os.Parcelable {
ctor public deprecated NeighboringCellInfo();
ctor public deprecated NeighboringCellInfo(int, int);
@@ -25860,6 +25963,7 @@
method public java.lang.CharSequence getContentDescription();
method public final android.content.Context getContext();
method protected android.view.ContextMenu.ContextMenuInfo getContextMenuInfo();
+ method public android.view.transition.Scene getCurrentScene();
method public static int getDefaultSize(int, int);
method public android.view.Display getDisplay();
method public final int[] getDrawableState();
@@ -28007,6 +28111,146 @@
}
+package android.view.transition {
+
+ public class AutoTransition extends android.view.transition.TransitionGroup {
+ ctor public AutoTransition();
+ }
+
+ public class Crossfade extends android.view.transition.Transition {
+ ctor public Crossfade();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ }
+
+ public class Fade extends android.view.transition.Visibility {
+ ctor public Fade();
+ ctor public Fade(int);
+ field public static final int IN = 1; // 0x1
+ field public static final int OUT = 2; // 0x2
+ }
+
+ public class Move extends android.view.transition.Transition {
+ ctor public Move();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ method public void setReparent(boolean);
+ method public void setResizeClip(boolean);
+ }
+
+ public class Recolor extends android.view.transition.Transition {
+ ctor public Recolor();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ }
+
+ public class Rotate extends android.view.transition.Transition {
+ ctor public Rotate();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ }
+
+ public final class Scene {
+ ctor public Scene(android.view.ViewGroup);
+ ctor public Scene(android.view.ViewGroup, int, android.content.Context);
+ ctor public Scene(android.view.ViewGroup, android.view.ViewGroup);
+ method public void enter();
+ method public void exit();
+ method public android.view.ViewGroup getSceneRoot();
+ method public void setEnterAction(java.lang.Runnable);
+ method public void setExitAction(java.lang.Runnable);
+ }
+
+ public class Slide extends android.view.transition.Visibility {
+ ctor public Slide();
+ }
+
+ public class TextChange extends android.view.transition.Transition {
+ ctor public TextChange();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ }
+
+ public abstract class Transition {
+ ctor public Transition();
+ method public void addListener(android.view.transition.Transition.TransitionListener);
+ method protected void cancelTransition();
+ method protected abstract void captureValues(android.view.transition.TransitionValues, boolean);
+ method public long getDuration();
+ method public android.animation.TimeInterpolator getInterpolator();
+ method public java.util.ArrayList<android.view.transition.Transition.TransitionListener> getListeners();
+ method public long getStartDelay();
+ method public int[] getTargetIds();
+ method public android.view.View[] getTargets();
+ method protected void onTransitionCancel();
+ method protected void onTransitionEnd();
+ method protected void onTransitionStart();
+ method protected abstract android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ method protected boolean prePlay(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ method public void removeListener(android.view.transition.Transition.TransitionListener);
+ method public android.view.transition.Transition setDuration(long);
+ method public void setInterpolator(android.animation.TimeInterpolator);
+ method public void setStartDelay(long);
+ method public android.view.transition.Transition setTargetIds(int...);
+ method public android.view.transition.Transition setTargets(android.view.View...);
+ }
+
+ public static abstract interface Transition.TransitionListener {
+ method public abstract void onTransitionCancel(android.view.transition.Transition);
+ method public abstract void onTransitionEnd(android.view.transition.Transition);
+ method public abstract void onTransitionStart(android.view.transition.Transition);
+ }
+
+ public class TransitionGroup extends android.view.transition.Transition {
+ ctor public TransitionGroup();
+ ctor public TransitionGroup(int);
+ method public void addTransitions(android.view.transition.Transition...);
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ method public void removeTransition(android.view.transition.Transition);
+ method public void setOrdering(int);
+ field public static final int SEQUENTIALLY = 1; // 0x1
+ field public static final int TOGETHER = 0; // 0x0
+ }
+
+ public class TransitionInflater {
+ method public static android.view.transition.TransitionInflater from(android.content.Context);
+ method public android.view.transition.Scene inflateScene(int, android.view.ViewGroup);
+ method public android.view.transition.Transition inflateTransition(int);
+ method public android.view.transition.TransitionManager inflateTransitionManager(int, android.view.ViewGroup);
+ }
+
+ public class TransitionManager {
+ ctor public TransitionManager();
+ method public android.view.transition.Transition getDefaultTransition();
+ method public static void go(android.view.transition.Scene);
+ method public static void go(android.view.transition.Scene, android.view.transition.Transition);
+ method public static void go(android.view.ViewGroup, java.lang.Runnable);
+ method public static void go(android.view.ViewGroup, java.lang.Runnable, android.view.transition.Transition);
+ method public void setDefaultTransition(android.view.transition.Transition);
+ method public void setTransition(android.view.transition.Scene, android.view.transition.Transition);
+ method public void setTransition(android.view.transition.Scene, android.view.transition.Scene, android.view.transition.Transition);
+ method public void transitionTo(android.view.transition.Scene);
+ }
+
+ public class TransitionValues {
+ ctor public TransitionValues();
+ field public final java.util.HashMap values;
+ field public android.view.View view;
+ }
+
+ public abstract class Visibility extends android.view.transition.Transition {
+ ctor public Visibility();
+ method protected android.animation.Animator appear(android.view.ViewGroup, android.view.View, int, android.view.View, int);
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator disappear(android.view.ViewGroup, android.view.View, int, android.view.View, int);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ method protected boolean preAppear(android.view.ViewGroup, android.view.View, int, android.view.View, int);
+ method protected boolean preDisappear(android.view.ViewGroup, android.view.View, int, android.view.View, int);
+ }
+
+}
+
package android.webkit {
public class ConsoleMessage {
@@ -30057,7 +30301,6 @@
method public void setRelativeScrollPosition(int, int);
method public deprecated void setRemoteAdapter(int, int, android.content.Intent);
method public void setRemoteAdapter(int, android.content.Intent);
- method public void setRemoteAdapter(int, java.util.ArrayList<android.widget.RemoteViews>, int);
method public void setScrollPosition(int, int);
method public void setShort(int, java.lang.String, short);
method public void setString(int, java.lang.String, java.lang.String);
diff --git a/core/java/android/animation/Animatable.java b/core/java/android/animation/Animatable.java
deleted file mode 100644
index f9ccb4d..0000000
--- a/core/java/android/animation/Animatable.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.animation;
-
-/**
- * This interface is implemented by animation-related classes that expose
- * the ability to set and get duration, startDelay, and interpolators.
- */
-public interface Animatable {
-
- /**
- * The amount of time, in milliseconds, to delay processing the animation
- * after the animation is started. The {@link #setDuration(long)} of the
- * animation will not begin to elapse until after the startDelay has elapsed.
- *
- * @return the number of milliseconds to delay running the animation
- */
- long getStartDelay();
-
- /**
- * The amount of time, in milliseconds, to delay processing the animation
- * after the animation is started. The {@link #setDuration(long)} of the
- * animation will not begin to elapse until after the startDelay has elapsed.
-
- * @param startDelay The amount of the delay, in milliseconds
- */
- void setStartDelay(long startDelay);
-
- /**
- * Sets the length of the animation.
- *
- * @param duration The length of the animation, in milliseconds.
- */
- Animatable setDuration(long duration);
-
- /**
- * Gets the duration of the animation.
- *
- * @return The length of the animation, in milliseconds.
- */
- long getDuration();
-
- /**
- * The time interpolator used in calculating the elapsed fraction of the
- * animation. The interpolator determines whether the animation runs with
- * linear or non-linear motion, such as acceleration and deceleration.
- *
- * @param value the interpolator to be used by this animation
- */
- void setInterpolator(TimeInterpolator value);
-
- /**
- * Returns the timing interpolator that this animation uses.
- *
- * @return The timing interpolator for this animation.
- */
- public TimeInterpolator getInterpolator();
-}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index da97d72..39eb8d6 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -22,21 +22,13 @@
* This is the superclass for classes which provide basic support for animations which can be
* started, ended, and have <code>AnimatorListeners</code> added to them.
*/
-public abstract class Animator implements Cloneable, Animatable {
+public abstract class Animator implements Cloneable {
/**
* The set of listeners to be sent events through the life of an animation.
*/
ArrayList<AnimatorListener> mListeners = null;
- @Override
- public abstract Animator setDuration(long duration);
-
- @Override
- public TimeInterpolator getInterpolator() {
- return null;
- }
-
/**
* Starts this animation. If the animation has a nonzero startDelay, the animation will start
* running after that delay elapses. A non-delayed animation will have its initial
@@ -77,6 +69,55 @@
}
/**
+ * The amount of time, in milliseconds, to delay processing the animation
+ * after {@link #start()} is called.
+ *
+ * @return the number of milliseconds to delay running the animation
+ */
+ public abstract long getStartDelay();
+
+ /**
+ * The amount of time, in milliseconds, to delay processing the animation
+ * after {@link #start()} is called.
+
+ * @param startDelay The amount of the delay, in milliseconds
+ */
+ public abstract void setStartDelay(long startDelay);
+
+ /**
+ * Sets the duration of the animation.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ */
+ public abstract Animator setDuration(long duration);
+
+ /**
+ * Gets the duration of the animation.
+ *
+ * @return The length of the animation, in milliseconds.
+ */
+ public abstract long getDuration();
+
+ /**
+ * The time interpolator used in calculating the elapsed fraction of the
+ * animation. The interpolator determines whether the animation runs with
+ * linear or non-linear motion, such as acceleration and deceleration. The
+ * default value is {@link android.view.animation.AccelerateDecelerateInterpolator}.
+ *
+ * @param value the interpolator to be used by this animation
+ */
+ public abstract void setInterpolator(TimeInterpolator value);
+
+ /**
+ * Returns the timing interpolator that this animation uses.
+ *
+ * @return The timing interpolator for this animation.
+ */
+ public TimeInterpolator getInterpolator() {
+ return null;
+ }
+
+ /**
* Returns whether this Animator is currently running (having been started and gone past any
* initial startDelay period and not yet ended).
*
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 7b07438..0d7f0a7 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -134,11 +134,6 @@
}
}
- public List<RestrictionEntry> getApplicationRestrictions() {
- return ((UserManager) getSystemService(USER_SERVICE))
- .getApplicationRestrictions(getPackageName(), android.os.Process.myUserHandle());
- }
-
public void registerComponentCallbacks(ComponentCallbacks callback) {
synchronized (mComponentCallbacks) {
mComponentCallbacks.add(callback);
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2e9c9e3..3498bb8 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -20,7 +20,6 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.ParcelUuid;
@@ -30,11 +29,14 @@
import android.util.Pair;
import java.io.IOException;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.HashMap;
import java.util.LinkedList;
+import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
@@ -357,9 +359,7 @@
private final IBluetoothManager mManagerService;
private IBluetooth mService;
- private Handler mServiceRecordHandler;
- private BluetoothAdapterCallback mCallback;
- private int mClientIf;
+ private final Map<LeScanCallback, GattCallbackWrapper> mLeScanClients;
/**
* Get a handle to the default local Bluetooth adapter.
@@ -394,7 +394,7 @@
mService = managerService.registerAdapter(mManagerCallback);
} catch (RemoteException e) {Log.e(TAG, "", e);}
mManagerService = managerService;
- mServiceRecordHandler = null;
+ mLeScanClients = new HashMap<LeScanCallback, GattCallbackWrapper>();
}
/**
@@ -1409,72 +1409,38 @@
}
/**
- * Register an callback to receive async results, such as LE scan result.
+ * Callback interface used to deliver LE scan results.
*
- * <p>This is an asynchronous call. The callback
- * {@link BluetoothAdapterCallback#onCallbackRegistration}
- * is used to notify success or failure if the function returns true.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param callback BluetootAdapter callback handler that will receive asynchronous callbacks.
- * @return If true, the callback will be called to notify success or failure,
- * false on immediate error
+ * @see #startLeScan(LeScanCallback)
+ * @see #startLeScan(UUID[], LeScanCallback)
*/
- public boolean registerCallback(BluetoothAdapterCallback callback) {
- try {
- IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
- mCallback = callback;
- UUID uuid = UUID.randomUUID();
- if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
-
- iGatt.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
- return true;
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- return false;
- }
- }
-
- /**
- * Unregister the registered callback.
- */
- public boolean unRegisterCallback(BluetoothAdapterCallback callback) {
- if (callback != mCallback) return false;
- try {
- IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
-
- iGatt.unregisterClient(mClientIf);
- return true;
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- return false;
- }
+ public interface LeScanCallback {
+ /**
+ * Callback reporting an LE device found during a device scan initiated
+ * by the {@link BluetoothAdapter#startLeScan} function.
+ *
+ * @param device Identifies the remote device
+ * @param rssi The RSSI value for the remote device as reported by the
+ * Bluetooth hardware. 0 if no RSSI value is available.
+ * @param scanRecord The content of the advertisement record offered by
+ * the remote device.
+ */
+ public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
}
/**
* Starts a scan for Bluetooth LE devices.
*
* <p>Results of the scan are reported using the
- * {@link BluetoothAdapterCallback#onLeScan} callback.
+ * {@link LeScanCallback#onLeScan} callback.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
+ * @param callback the callback LE scan results are delivered
* @return true, if the scan was started successfully
*/
- public boolean startLeScan() {
- if (DBG) Log.d(TAG, "startLeScan()");
- if (mClientIf == 0) return false;
-
- try {
- IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
- iGatt.startScan(mClientIf, false);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- return false;
- }
-
- return true;
+ public boolean startLeScan(LeScanCallback callback) {
+ return startLeScan(null, callback);
}
/**
@@ -1482,155 +1448,281 @@
* advertise given services.
*
* <p>Devices which advertise all specified services are reported using the
- * {@link BluetoothAdapterCallback#onLeScan} callback.
+ * {@link LeScanCallback#onLeScan} callback.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param serviceUuids Array of services to look for
+ * @param callback the callback LE scan results are delivered
* @return true, if the scan was started successfully
*/
- public boolean startLeScan(UUID[] serviceUuids) {
- if (DBG) Log.d(TAG, "startLeScan() - with UUIDs");
- if (mClientIf == 0) return false;
+ public boolean startLeScan(UUID[] serviceUuids, LeScanCallback callback) {
+ if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids);
- try {
- IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
- ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length];
- for(int i = 0; i != uuids.length; ++i) {
- uuids[i] = new ParcelUuid(serviceUuids[i]);
- }
- iGatt.startScanWithUuids(mClientIf, false, uuids);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
+ if (callback == null) {
+ if (DBG) Log.e(TAG, "startLeScan: null callback");
return false;
}
- return true;
+ synchronized(mLeScanClients) {
+ if (mLeScanClients.containsKey(callback)) {
+ if (DBG) Log.e(TAG, "LE Scan has already started");
+ return false;
+ }
+
+ try {
+ IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
+ UUID uuid = UUID.randomUUID();
+ GattCallbackWrapper wrapper = new GattCallbackWrapper(this, callback, serviceUuids);
+
+ iGatt.registerClient(new ParcelUuid(uuid), wrapper);
+ if (wrapper.scanStarted()) {
+ mLeScanClients.put(callback, wrapper);
+ return true;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+ return false;
}
/**
* Stops an ongoing Bluetooth LE device scan.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param callback used to identify which scan to stop
+ * must be the same handle used to start the scan
*/
- public void stopLeScan() {
- if (DBG) Log.d(TAG, "stopScan()");
- if (mClientIf == 0) return;
-
- try {
- IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
- iGatt.stopScan(mClientIf, false);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
+ public void stopLeScan(LeScanCallback callback) {
+ if (DBG) Log.d(TAG, "stopLeScan()");
+ GattCallbackWrapper wrapper;
+ synchronized(mLeScanClients) {
+ wrapper = mLeScanClients.remove(callback);
+ if (wrapper == null) return;
}
+ wrapper.stopLeScan();
}
/**
* Bluetooth GATT interface callbacks
*/
- private final IBluetoothGattCallback mBluetoothGattCallback =
- new IBluetoothGattCallback.Stub() {
- /**
- * Application interface registered - app is ready to go
- */
- public void onClientRegistered(int status, int clientIf) {
- if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
- + " clientIf=" + clientIf);
- mClientIf = clientIf;
- mCallback.onCallbackRegistration(status == BluetoothGatt.GATT_SUCCESS ?
- BluetoothAdapterCallback.CALLBACK_REGISTERED :
- BluetoothAdapterCallback.CALLBACK_REGISTRATION_FAILURE);
- }
+ private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub {
+ private static final int LE_CALLBACK_REG_TIMEOUT = 2000;
+ private static final int LE_CALLBACK_REG_WAIT_COUNT = 5;
- public void onClientConnectionState(int status, int clientIf,
- boolean connected, String address) {
- // no op
- }
+ private final LeScanCallback mLeScanCb;
+ // mLeHandle 0: not registered
+ // -1: scan stopped
+ // >0: registered and scan started
+ private int mLeHandle;
+ private final UUID[] mScanFilter;
+ private WeakReference<BluetoothAdapter> mBluetoothAdapter;
- /**
- * Callback reporting an LE scan result.
- * @hide
- */
- public void onScanResult(String address, int rssi, byte[] advData) {
- if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
+ public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter,
+ LeScanCallback leScanCb, UUID[] uuid) {
+ mBluetoothAdapter = new WeakReference<BluetoothAdapter>(bluetoothAdapter);
+ mLeScanCb = leScanCb;
+ mScanFilter = uuid;
+ mLeHandle = 0;
+ }
- try {
- mCallback.onLeScan(getRemoteDevice(address), rssi, advData);
- } catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ public boolean scanStarted() {
+ boolean started = false;
+ synchronized(this) {
+ if (mLeHandle == -1) return false;
+
+ int count = 0;
+ // wait for callback registration and LE scan to start
+ while (mLeHandle == 0 && count < LE_CALLBACK_REG_WAIT_COUNT) {
+ try {
+ wait(LE_CALLBACK_REG_TIMEOUT);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Callback reg wait interrupted: " + e);
+ }
+ count++;
}
+ started = (mLeHandle > 0);
}
+ return started;
+ }
- public void onGetService(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid) {
- // no op
+ public void stopLeScan() {
+ synchronized(this) {
+ if (mLeHandle <= 0) {
+ Log.e(TAG, "Error state, mLeHandle: " + mLeHandle);
+ return;
+ }
+ BluetoothAdapter adapter = mBluetoothAdapter.get();
+ if (adapter != null) {
+ try {
+ IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt();
+ iGatt.stopScan(mLeHandle, false);
+ iGatt.unregisterClient(mLeHandle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to stop scan and unregister" + e);
+ }
+ } else {
+ Log.e(TAG, "stopLeScan, BluetoothAdapter is null");
+ }
+ mLeHandle = -1;
+ notifyAll();
}
+ }
- public void onGetIncludedService(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int inclSrvcType, int inclSrvcInstId,
- ParcelUuid inclSrvcUuid) {
- // no op
+ /**
+ * Application interface registered - app is ready to go
+ */
+ public void onClientRegistered(int status, int clientIf) {
+ if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status +
+ " clientIf=" + clientIf);
+ synchronized(this) {
+ if (mLeHandle == -1) {
+ if (DBG) Log.d(TAG, "onClientRegistered LE scan canceled");
+ }
+
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ mLeHandle = clientIf;
+ IBluetoothGatt iGatt = null;
+ try {
+ BluetoothAdapter adapter = mBluetoothAdapter.get();
+ if (adapter != null) {
+ iGatt = adapter.getBluetoothManager().getBluetoothGatt();
+ if (mScanFilter == null) {
+ iGatt.startScan(mLeHandle, false);
+ } else {
+ ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length];
+ for(int i = 0; i != uuids.length; ++i) {
+ uuids[i] = new ParcelUuid(mScanFilter[i]);
+ }
+ iGatt.startScanWithUuids(mLeHandle, false, uuids);
+ }
+ } else {
+ Log.e(TAG, "onClientRegistered, BluetoothAdapter null");
+ mLeHandle = -1;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "fail to start le scan: " + e);
+ mLeHandle = -1;
+ }
+ if (mLeHandle == -1) {
+ // registration succeeded but start scan failed
+ if (iGatt != null) {
+ try {
+ iGatt.unregisterClient(mLeHandle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "fail to unregister callback: " + mLeHandle +
+ " error: " + e);
+ }
+ }
+ }
+ } else {
+ // registration failed
+ mLeHandle = -1;
+ }
+ notifyAll();
}
+ }
- public void onGetCharacteristic(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- int charProps) {
- // no op
+ public void onClientConnectionState(int status, int clientIf,
+ boolean connected, String address) {
+ // no op
+ }
+
+ /**
+ * Callback reporting an LE scan result.
+ * @hide
+ */
+ public void onScanResult(String address, int rssi, byte[] advData) {
+ if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
+
+ // Check null in case the scan has been stopped
+ synchronized(this) {
+ if (mLeHandle <= 0) return;
}
-
- public void onGetDescriptor(String address, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- ParcelUuid descUuid) {
- // no op
+ try {
+ BluetoothAdapter adapter = mBluetoothAdapter.get();
+ if (adapter == null) {
+ Log.d(TAG, "onScanResult, BluetoothAdapter null");
+ return;
+ }
+ mLeScanCb.onLeScan(adapter.getRemoteDevice(address), rssi, advData);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
}
+ }
- public void onSearchComplete(String address, int status) {
- // no op
- }
+ public void onGetService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid) {
+ // no op
+ }
- public void onCharacteristicRead(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid, byte[] value) {
- // no op
- }
+ public void onGetIncludedService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int inclSrvcType, int inclSrvcInstId,
+ ParcelUuid inclSrvcUuid) {
+ // no op
+ }
- public void onCharacteristicWrite(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid) {
- // no op
- }
+ public void onGetCharacteristic(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int charProps) {
+ // no op
+ }
- public void onNotify(String address, int srvcType,
+ public void onGetDescriptor(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ ParcelUuid descUuid) {
+ // no op
+ }
+
+ public void onSearchComplete(String address, int status) {
+ // no op
+ }
+
+ public void onCharacteristicRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid, byte[] value) {
+ // no op
+ }
+
+ public void onCharacteristicWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid) {
+ // no op
+ }
+
+ public void onNotify(String address, int srvcType,
int srvcInstId, ParcelUuid srvcUuid,
int charInstId, ParcelUuid charUuid,
byte[] value) {
- // no op
- }
+ // no op
+ }
- public void onDescriptorRead(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- ParcelUuid descrUuid, byte[] value) {
- // no op
- }
+ public void onDescriptorRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ ParcelUuid descrUuid, byte[] value) {
+ // no op
+ }
- public void onDescriptorWrite(String address, int status, int srvcType,
- int srvcInstId, ParcelUuid srvcUuid,
- int charInstId, ParcelUuid charUuid,
- ParcelUuid descrUuid) {
- // no op
- }
+ public void onDescriptorWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ ParcelUuid descrUuid) {
+ // no op
+ }
- public void onExecuteWrite(String address, int status) {
- // no op
- }
+ public void onExecuteWrite(String address, int status) {
+ // no op
+ }
- public void onReadRemoteRssi(String address, int rssi, int status) {
- // no op
- }
- };
+ public void onReadRemoteRssi(String address, int rssi, int status) {
+ // no op
+ }
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothAdapterCallback.java b/core/java/android/bluetooth/BluetoothAdapterCallback.java
deleted file mode 100644
index a726bc9..0000000
--- a/core/java/android/bluetooth/BluetoothAdapterCallback.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.bluetooth.BluetoothDevice;
-
-/**
- * This abstract class is used to implement {@link BluetoothAdapter} callbacks.
- */
-public abstract class BluetoothAdapterCallback {
-
- /**
- * Indicates the callback has been registered successfully
- */
- public static final int CALLBACK_REGISTERED = 0;
-
- /**
- * Indicates the callback registration has failed
- */
- public static final int CALLBACK_REGISTRATION_FAILURE = 1;
-
- /**
- * Callback to inform change in registration state of the application.
- *
- * @param status Returns {@link #CALLBACK_REGISTERED} if the application
- * was successfully registered.
- */
- public void onCallbackRegistration(int status) {
- }
-
- /**
- * Callback reporting an LE device found during a device scan initiated
- * by the {@link BluetoothAdapter#startLeScan} function.
- *
- * @param device Identifies the remote device
- * @param rssi The RSSI value for the remote device as reported by the
- * Bluetooth hardware. 0 if no RSSI value is available.
- * @param scanRecord The content of the advertisement record offered by
- * the remote device.
- */
- public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
- }
-}
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index 19083b5..172f3bc 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -127,7 +127,7 @@
try {
IBluetoothManager managerService = mAdapter.getBluetoothManager();
- IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
+ IBluetoothGatt iGatt = managerService.getBluetoothGatt();
if (iGatt == null) return connectedDevices;
connectedDevices = iGatt.getDevicesMatchingConnectionStates(
@@ -172,7 +172,7 @@
try {
IBluetoothManager managerService = mAdapter.getBluetoothManager();
- IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
+ IBluetoothGatt iGatt = managerService.getBluetoothGatt();
if (iGatt == null) return devices;
devices = iGatt.getDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
@@ -203,7 +203,7 @@
try {
IBluetoothManager managerService = mAdapter.getBluetoothManager();
- IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
+ IBluetoothGatt iGatt = managerService.getBluetoothGatt();
if (iGatt == null) {
Log.e(TAG, "Fail to get GATT Server connection");
return null;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index cb5c315..81d6f0b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -293,15 +293,6 @@
public abstract Context getApplicationContext();
/**
- * Returns the list of restrictions for the application, or null if there are no
- * restrictions.
- * @return
- */
- public List<RestrictionEntry> getApplicationRestrictions() {
- return getApplicationContext().getApplicationRestrictions();
- }
-
- /**
* Add a new {@link ComponentCallbacks} to the base application of the
* Context, which will be called at the same times as the ComponentCallbacks
* methods of activities and other components are called. Note that you
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 36c69ef..ea0e027 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2416,11 +2416,16 @@
/**
* Broadcast to a specific application to query any supported restrictions to impose
- * on restricted users. The response should contain an extra {@link #EXTRA_RESTRICTIONS},
+ * on restricted users. The broadcast intent contains an extra
+ * {@link #EXTRA_RESTRICTIONS_BUNDLE} with the currently persisted
+ * restrictions as a Bundle of key/value pairs. The value types can be Boolean, String or
+ * String[] depending on the restriction type.<p/>
+ * The response should contain an extra {@link #EXTRA_RESTRICTIONS_LIST},
* which is of type <code>ArrayList<RestrictionEntry></code>. It can also
* contain an extra {@link #EXTRA_RESTRICTIONS_INTENT}, which is of type <code>Intent</code>.
* The activity specified by that intent will be launched for a result which must contain
- * the extra {@link #EXTRA_RESTRICTIONS}. The returned restrictions will be persisted.
+ * the extra {@link #EXTRA_RESTRICTIONS_LIST}. The keys and values of the returned restrictions
+ * will be persisted.
* @see RestrictionEntry
*/
public static final String ACTION_GET_RESTRICTION_ENTRIES =
@@ -3159,7 +3164,8 @@
"android.intent.extra.ALLOW_MULTIPLE";
/**
- * The userHandle carried with broadcast intents related to addition, removal and switching of users
+ * The userHandle carried with broadcast intents related to addition, removal and switching of
+ * users
* - {@link #ACTION_USER_ADDED}, {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}.
* @hide
*/
@@ -3168,9 +3174,18 @@
/**
* Extra used in the response from a BroadcastReceiver that handles
- * {@link #ACTION_GET_RESTRICTION_ENTRIES}.
+ * {@link #ACTION_GET_RESTRICTION_ENTRIES}. The type of the extra is
+ * <code>ArrayList<RestrictionEntry></code>.
*/
- public static final String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions";
+ public static final String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list";
+
+ /**
+ * Extra sent in the intent to the BroadcastReceiver that handles
+ * {@link #ACTION_GET_RESTRICTION_ENTRIES}. The type of the extra is a Bundle containing
+ * the restrictions as key/value pairs.
+ */
+ public static final String EXTRA_RESTRICTIONS_BUNDLE =
+ "android.intent.extra.restrictions_bundle";
/**
* Extra used in the response from a BroadcastReceiver that handles
diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java
index 35bbb9c..e67d0d7 100644
--- a/core/java/android/hardware/location/GeofenceHardware.java
+++ b/core/java/android/hardware/location/GeofenceHardware.java
@@ -129,6 +129,9 @@
private HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>
mCallbacks = new HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>();
+ private HashMap<GeofenceHardwareMonitorCallback, GeofenceHardwareMonitorCallbackWrapper>
+ mMonitorCallbacks = new HashMap<GeofenceHardwareMonitorCallback,
+ GeofenceHardwareMonitorCallbackWrapper>();
/**
* @hide
*/
@@ -137,8 +140,29 @@
}
/**
- * Returns all the hardware geofence monitoring systems and their status.
- * Status can be one of {@link #MONITOR_CURRENTLY_AVAILABLE},
+ * Returns all the hardware geofence monitoring systems which are supported
+ *
+ * <p> Call {@link #getStatusOfMonitoringType(int)} to know the current state
+ * of a monitoring system.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * @return An array of all the monitoring types.
+ * An array of length 0 is returned in case of errors.
+ */
+ public int[] getMonitoringTypes() {
+ try {
+ return mService.getMonitoringTypes();
+ } catch (RemoteException e) {
+ }
+ return new int[0];
+ }
+
+ /**
+ * Returns current status of a hardware geofence monitoring system.
+ *
+ * <p>Status can be one of {@link #MONITOR_CURRENTLY_AVAILABLE},
* {@link #MONITOR_CURRENTLY_UNAVAILABLE} or {@link #MONITOR_UNSUPPORTED}
*
* <p> Some supported hardware monitoring systems might not be available
@@ -147,18 +171,15 @@
* geofences and will change from {@link #MONITOR_CURRENTLY_AVAILABLE} to
* {@link #MONITOR_CURRENTLY_UNAVAILABLE}.
*
- * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
- * geofencing in hardware.
- *
- * @return An array indexed by the various monitoring types and their status.
- * An array of length 0 is returned in case of errors.
+ * @param monitoringType
+ * @return Current status of the monitoring type.
*/
- public int[] getMonitoringTypesAndStatus() {
+ public int getStatusOfMonitoringType(int monitoringType) {
try {
- return mService.getMonitoringTypesAndStatus();
+ return mService.getStatusOfMonitoringType(monitoringType);
} catch (RemoteException e) {
+ return MONITOR_UNSUPPORTED;
}
- return new int[0];
}
/**
@@ -167,8 +188,10 @@
* <p> When the device detects that is has entered, exited or is uncertain
* about the area specified by the geofence, the given callback will be called.
*
- * <p> The {@link GeofenceHardwareCallback#onGeofenceChange} callback will be called,
- * with the following parameters
+ * <p> If this call returns true, it means that the geofence has been sent to the hardware.
+ * {@link GeofenceHardwareCallback#onGeofenceAdd} will be called with the result of the
+ * add call from the hardware. The {@link GeofenceHardwareCallback#onGeofenceAdd} will be
+ * called with the following parameters when a transition event occurs.
* <ul>
* <li> The geofence Id
* <li> The location object indicating the last known location.
@@ -195,43 +218,46 @@
* which abstracts the hardware should be used instead. All the checks are done by the higher
* level public API. Any needed locking should be handled by the higher level API.
*
- * @param latitude Latitude of the area to be monitored.
- * @param longitude Longitude of the area to be monitored.
- * @param radius Radius (in meters) of the area to be monitored.
- * @param lastTransition The current state of the geofence. Can be one of
- * {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED},
- * {@link #GEOFENCE_UNCERTAIN}.
- * @param monitorTransitions Bitwise OR of {@link #GEOFENCE_ENTERED},
- * {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
- * @param notificationResponsivenes Defines the best-effort description
- * of how soon should the callback be called when the transition
- * associated with the Geofence is triggered. For instance, if
- * set to 1000 millseconds with {@link #GEOFENCE_ENTERED},
- * the callback will be called 1000 milliseconds within entering
- * the geofence. This parameter is defined in milliseconds.
- * @param unknownTimer The time limit after which the
- * {@link #GEOFENCE_UNCERTAIN} transition
- * should be triggered. This paramter is defined in milliseconds.
+ * <p> Create a geofence request object using the methods in {@link GeofenceHardwareRequest} to
+ * set all the characteristics of the geofence. Use the created GeofenceHardwareRequest object
+ * in this call.
+ *
+ * @param geofenceId The id associated with the geofence.
* @param monitoringType The type of the hardware subsystem that should be used
* to monitor the geofence.
+ * @param geofenceRequest The {@link GeofenceHardwareRequest} object associated with the
+ * geofence.
* @param callback {@link GeofenceHardwareCallback} that will be use to notify the
* transition.
- * @return true on success.
+ * @return true when the geofence is successfully sent to the hardware for addition.
+ * @throws IllegalArgumentException when the geofence request type is not supported.
*/
- public boolean addCircularFence(int geofenceId, double latitude, double longitude,
- double radius, int lastTransition,int monitorTransitions, int notificationResponsivenes,
- int unknownTimer, int monitoringType, GeofenceHardwareCallback callback) {
+ public boolean addGeofence(int geofenceId, int monitoringType, GeofenceHardwareRequest
+ geofenceRequest, GeofenceHardwareCallback callback) {
try {
- return mService.addCircularFence(geofenceId, latitude, longitude, radius,
- lastTransition, monitorTransitions, notificationResponsivenes, unknownTimer,
- monitoringType, getCallbackWrapper(callback));
+ if (geofenceRequest.getType() == GeofenceHardwareRequest.GEOFENCE_TYPE_CIRCLE) {
+ return mService.addCircularFence(geofenceId, monitoringType,
+ geofenceRequest.getLatitude(),
+ geofenceRequest.getLongitude(), geofenceRequest.getRadius(),
+ geofenceRequest.getLastTransition(),
+ geofenceRequest.getMonitorTransitions(),
+ geofenceRequest.getNotificationResponsiveness(),
+ geofenceRequest.getUnknownTimer(),
+ getCallbackWrapper(callback));
+ } else {
+ throw new IllegalArgumentException("Geofence Request type not supported");
+ }
} catch (RemoteException e) {
}
return false;
}
/**
- * Removes a geofence added by {@link #addCircularFence} call.
+ * Removes a geofence added by {@link #addGeofence} call.
+ *
+ * <p> If this call returns true, it means that the geofence has been sent to the hardware.
+ * {@link GeofenceHardwareCallback#onGeofenceRemove} will be called with the result of the
+ * remove call from the hardware.
*
* <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
* {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
@@ -246,7 +272,7 @@
* @param geofenceId The id of the geofence.
* @param monitoringType The type of the hardware subsystem that should be used
* to monitor the geofence.
- * @return true on success.
+ * @return true when the geofence is successfully sent to the hardware for removal. .
*/
public boolean removeGeofence(int geofenceId, int monitoringType) {
try {
@@ -257,7 +283,11 @@
}
/**
- * Pauses the monitoring of a geofence added by {@link #addCircularFence} call.
+ * Pauses the monitoring of a geofence added by {@link #addGeofence} call.
+ *
+ * <p> If this call returns true, it means that the geofence has been sent to the hardware.
+ * {@link GeofenceHardwareCallback#onGeofencePause} will be called with the result of the
+ * pause call from the hardware.
*
* <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
* {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
@@ -272,7 +302,7 @@
* @param geofenceId The id of the geofence.
* @param monitoringType The type of the hardware subsystem that should be used
* to monitor the geofence.
- * @return true on success.
+ * @return true when the geofence is successfully sent to the hardware for pausing.
*/
public boolean pauseGeofence(int geofenceId, int monitoringType) {
try {
@@ -285,6 +315,10 @@
/**
* Resumes the monitoring of a geofence added by {@link #pauseGeofence} call.
*
+ * <p> If this call returns true, it means that the geofence has been sent to the hardware.
+ * {@link GeofenceHardwareCallback#onGeofenceResume} will be called with the result of the
+ * resume call from the hardware.
+ *
* <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
* {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
*
@@ -296,15 +330,15 @@
* level public API. Any needed locking should be handled by the higher level API.
*
* @param geofenceId The id of the geofence.
- * @param monitorTransition Bitwise OR of {@link #GEOFENCE_ENTERED},
- * {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
* @param monitoringType The type of the hardware subsystem that should be used
* to monitor the geofence.
- * @return true on success.
+ * @param monitorTransition Bitwise OR of {@link #GEOFENCE_ENTERED},
+ * {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
+ * @return true when the geofence is successfully sent to the hardware for resumption.
*/
- public boolean resumeGeofence(int geofenceId, int monitorTransition, int monitoringType) {
+ public boolean resumeGeofence(int geofenceId, int monitoringType, int monitorTransition) {
try {
- return mService.resumeGeofence(geofenceId, monitorTransition, monitoringType);
+ return mService.resumeGeofence(geofenceId, monitoringType, monitorTransition);
} catch (RemoteException e) {
}
return false;
@@ -333,10 +367,10 @@
* @return true on success
*/
public boolean registerForMonitorStateChangeCallback(int monitoringType,
- GeofenceHardwareCallback callback) {
+ GeofenceHardwareMonitorCallback callback) {
try {
return mService.registerForMonitorStateChangeCallback(monitoringType,
- getCallbackWrapper(callback));
+ getMonitorCallbackWrapper(callback));
} catch (RemoteException e) {
}
return false;
@@ -361,12 +395,12 @@
* @return true on success
*/
public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
- GeofenceHardwareCallback callback) {
+ GeofenceHardwareMonitorCallback callback) {
boolean result = false;
try {
result = mService.unregisterForMonitorStateChangeCallback(monitoringType,
- getCallbackWrapper(callback));
- if (result) removeCallback(callback);
+ getMonitorCallbackWrapper(callback));
+ if (result) removeMonitorCallback(callback);
} catch (RemoteException e) {
}
@@ -391,6 +425,38 @@
}
}
+ private void removeMonitorCallback(GeofenceHardwareMonitorCallback callback) {
+ synchronized (mMonitorCallbacks) {
+ mMonitorCallbacks.remove(callback);
+ }
+ }
+
+ private GeofenceHardwareMonitorCallbackWrapper getMonitorCallbackWrapper(
+ GeofenceHardwareMonitorCallback callback) {
+ synchronized (mMonitorCallbacks) {
+ GeofenceHardwareMonitorCallbackWrapper wrapper = mMonitorCallbacks.get(callback);
+ if (wrapper == null) {
+ wrapper = new GeofenceHardwareMonitorCallbackWrapper(callback);
+ mMonitorCallbacks.put(callback, wrapper);
+ }
+ return wrapper;
+ }
+ }
+
+ class GeofenceHardwareMonitorCallbackWrapper extends IGeofenceHardwareMonitorCallback.Stub {
+ private WeakReference<GeofenceHardwareMonitorCallback> mCallback;
+
+ GeofenceHardwareMonitorCallbackWrapper(GeofenceHardwareMonitorCallback c) {
+ mCallback = new WeakReference<GeofenceHardwareMonitorCallback>(c);
+ }
+
+ public void onMonitoringSystemChange(int monitoringType, boolean available,
+ Location location) {
+ GeofenceHardwareMonitorCallback c = mCallback.get();
+ if (c != null) c.onMonitoringSystemChange(monitoringType, available, location);
+ }
+ }
+
class GeofenceHardwareCallbackWrapper extends IGeofenceHardwareCallback.Stub {
private WeakReference<GeofenceHardwareCallback> mCallback;
@@ -398,17 +464,11 @@
mCallback = new WeakReference<GeofenceHardwareCallback>(c);
}
- public void onMonitoringSystemChange(int monitoringType, boolean available,
- Location location) {
- GeofenceHardwareCallback c = mCallback.get();
- if (c != null) c.onMonitoringSystemChange(monitoringType, available, location);
- }
-
- public void onGeofenceChange(int geofenceId, int transition, Location location,
+ public void onGeofenceTransition(int geofenceId, int transition, Location location,
long timestamp, int monitoringType) {
GeofenceHardwareCallback c = mCallback.get();
if (c != null) {
- c.onGeofenceChange(geofenceId, transition, location, timestamp,
+ c.onGeofenceTransition(geofenceId, transition, location, timestamp,
monitoringType);
}
}
@@ -428,7 +488,9 @@
public void onGeofencePause(int geofenceId, int status) {
GeofenceHardwareCallback c = mCallback.get();
- if (c != null) c.onGeofencePause(geofenceId, status);
+ if (c != null) {
+ c.onGeofencePause(geofenceId, status);
+ }
}
public void onGeofenceResume(int geofenceId, int status) {
diff --git a/core/java/android/hardware/location/GeofenceHardwareCallback.java b/core/java/android/hardware/location/GeofenceHardwareCallback.java
index 8ab582a..6cad3da 100644
--- a/core/java/android/hardware/location/GeofenceHardwareCallback.java
+++ b/core/java/android/hardware/location/GeofenceHardwareCallback.java
@@ -22,19 +22,6 @@
* The callback class associated with the APIs in {@link GeofenceHardware}
*/
public abstract class GeofenceHardwareCallback {
-
- /**
- * The callback called when the state of a monitoring system changes.
- * {@link GeofenceHardware#MONITORING_TYPE_GPS_HARDWARE} is an example of a
- * monitoring system.
- *
- * @param monitoringType The type of the monitoring system.
- * @param available Indicates whether the system is currently available or not.
- * @param location The last known location according to the monitoring system.
- */
- public void onMonitoringSystemChange(int monitoringType, boolean available, Location location) {
- }
-
/**
* The callback called when there is a transition to report for the specific
* geofence.
@@ -47,7 +34,7 @@
* detected
* @param monitoringType Type of the monitoring system.
*/
- public void onGeofenceChange(int geofenceId, int transition, Location location,
+ public void onGeofenceTransition(int geofenceId, int transition, Location location,
long timestamp, int monitoringType) {
}
diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java
index 21f1ea6..a62b660 100644
--- a/core/java/android/hardware/location/GeofenceHardwareImpl.java
+++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java
@@ -21,8 +21,10 @@
import android.location.IGpsGeofenceHardware;
import android.location.Location;
import android.location.LocationManager;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -48,8 +50,9 @@
private PowerManager.WakeLock mWakeLock;
private SparseArray<IGeofenceHardwareCallback> mGeofences =
new SparseArray<IGeofenceHardwareCallback>();
- private ArrayList<IGeofenceHardwareCallback>[] mCallbacks =
+ private ArrayList<IGeofenceHardwareMonitorCallback>[] mCallbacks =
new ArrayList[GeofenceHardware.NUM_MONITORS];
+ private ArrayList<Reaper> mReapers = new ArrayList<Reaper>();
private IGpsGeofenceHardware mGpsService;
@@ -63,11 +66,18 @@
private static final int RESUME_GEOFENCE_CALLBACK = 5;
private static final int ADD_GEOFENCE = 6;
private static final int REMOVE_GEOFENCE = 7;
+ private static final int GEOFENCE_CALLBACK_BINDER_DIED = 8;
// mCallbacksHandler message types
private static final int GPS_GEOFENCE_STATUS = 1;
private static final int CALLBACK_ADD = 2;
private static final int CALLBACK_REMOVE = 3;
+ private static final int MONITOR_CALLBACK_BINDER_DIED = 4;
+
+ // mReaperHandler message types
+ private static final int REAPER_GEOFENCE_ADDED = 1;
+ private static final int REAPER_MONITOR_CALLBACK_ADDED = 2;
+ private static final int REAPER_REMOVED = 3;
// The following constants need to match GpsLocationFlags enum in gps.h
private static final int LOCATION_INVALID = 0;
@@ -151,15 +161,28 @@
}
}
- public int[] getMonitoringTypesAndStatus() {
+ public int[] getMonitoringTypes() {
synchronized (mSupportedMonitorTypes) {
- return mSupportedMonitorTypes;
+ if (mSupportedMonitorTypes[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE] !=
+ GeofenceHardware.MONITOR_UNSUPPORTED) {
+ return new int[] {GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE};
+ }
+ return new int[0];
}
}
- public boolean addCircularFence(int geofenceId, double latitude, double longitude,
- double radius, int lastTransition,int monitorTransitions, int notificationResponsivenes,
- int unknownTimer, int monitoringType, IGeofenceHardwareCallback callback) {
+ public int getStatusOfMonitoringType(int monitoringType) {
+ synchronized (mSupportedMonitorTypes) {
+ if (monitoringType >= mSupportedMonitorTypes.length || monitoringType < 0) {
+ throw new IllegalArgumentException("Unknown monitoring type");
+ }
+ return mSupportedMonitorTypes[monitoringType];
+ }
+ }
+
+ public boolean addCircularFence(int geofenceId, int monitoringType, double latitude,
+ double longitude, double radius, int lastTransition,int monitorTransitions,
+ int notificationResponsivenes, int unknownTimer, IGeofenceHardwareCallback callback) {
// This API is not thread safe. Operations on the same geofence need to be serialized
// by upper layers
if (DEBUG) {
@@ -190,7 +213,11 @@
default:
result = false;
}
- if (!result) {
+ if (result) {
+ m = mReaperHandler.obtainMessage(REAPER_GEOFENCE_ADDED, callback);
+ m.arg1 = monitoringType;
+ mReaperHandler.sendMessage(m);
+ } else {
m = mGeofenceHandler.obtainMessage(REMOVE_GEOFENCE);
m.arg1 = geofenceId;
mGeofenceHandler.sendMessage(m);
@@ -245,7 +272,7 @@
}
- public boolean resumeGeofence(int geofenceId, int monitorTransition, int monitoringType) {
+ public boolean resumeGeofence(int geofenceId, int monitoringType, int monitorTransition) {
// This API is not thread safe. Operations on the same geofence need to be serialized
// by upper layers
if (DEBUG) Log.d(TAG, "Resume Geofence: GeofenceId: " + geofenceId);
@@ -268,7 +295,12 @@
}
public boolean registerForMonitorStateChangeCallback(int monitoringType,
- IGeofenceHardwareCallback callback) {
+ IGeofenceHardwareMonitorCallback callback) {
+ Message reaperMessage =
+ mReaperHandler.obtainMessage(REAPER_MONITOR_CALLBACK_ADDED, callback);
+ reaperMessage.arg1 = monitoringType;
+ mReaperHandler.sendMessage(reaperMessage);
+
Message m = mCallbacksHandler.obtainMessage(CALLBACK_ADD, callback);
m.arg1 = monitoringType;
mCallbacksHandler.sendMessage(m);
@@ -276,7 +308,7 @@
}
public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
- IGeofenceHardwareCallback callback) {
+ IGeofenceHardwareMonitorCallback callback) {
Message m = mCallbacksHandler.obtainMessage(CALLBACK_REMOVE, callback);
m.arg1 = monitoringType;
mCallbacksHandler.sendMessage(m);
@@ -477,13 +509,25 @@
"Location: " + geofenceTransition.mLocation + ":" + mGeofences);
try {
- callback.onGeofenceChange(
+ callback.onGeofenceTransition(
geofenceTransition.mGeofenceId, geofenceTransition.mTransition,
geofenceTransition.mLocation, geofenceTransition.mTimestamp,
GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE);
} catch (RemoteException e) {}
releaseWakeLock();
break;
+ case GEOFENCE_CALLBACK_BINDER_DIED:
+ // Find all geofences associated with this callback and remove them.
+ callback = (IGeofenceHardwareCallback) (msg.obj);
+ if (DEBUG) Log.d(TAG, "Geofence callback reaped:" + callback);
+ int monitoringType = msg.arg1;
+ for (int i = 0; i < mGeofences.size(); i++) {
+ if (mGeofences.valueAt(i).equals(callback)) {
+ geofenceId = mGeofences.keyAt(i);
+ removeGeofence(mGeofences.keyAt(i), monitoringType);
+ mGeofences.remove(geofenceId);
+ }
+ }
}
}
};
@@ -493,8 +537,8 @@
@Override
public void handleMessage(Message msg) {
int monitoringType;
- ArrayList<IGeofenceHardwareCallback> callbackList;
- IGeofenceHardwareCallback callback;
+ ArrayList<IGeofenceHardwareMonitorCallback> callbackList;
+ IGeofenceHardwareMonitorCallback callback;
switch (msg.what) {
case GPS_GEOFENCE_STATUS:
@@ -508,7 +552,7 @@
if (DEBUG) Log.d(TAG, "MonitoringSystemChangeCallback: GPS : " + available);
- for (IGeofenceHardwareCallback c: callbackList) {
+ for (IGeofenceHardwareMonitorCallback c: callbackList) {
try {
c.onMonitoringSystemChange(
GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, available,
@@ -519,22 +563,71 @@
break;
case CALLBACK_ADD:
monitoringType = msg.arg1;
- callback = (IGeofenceHardwareCallback) msg.obj;
+ callback = (IGeofenceHardwareMonitorCallback) msg.obj;
callbackList = mCallbacks[monitoringType];
if (callbackList == null) {
- callbackList = new ArrayList<IGeofenceHardwareCallback>();
+ callbackList = new ArrayList<IGeofenceHardwareMonitorCallback>();
mCallbacks[monitoringType] = callbackList;
}
if (!callbackList.contains(callback)) callbackList.add(callback);
break;
case CALLBACK_REMOVE:
monitoringType = msg.arg1;
- callback = (IGeofenceHardwareCallback) msg.obj;
+ callback = (IGeofenceHardwareMonitorCallback) msg.obj;
callbackList = mCallbacks[monitoringType];
if (callbackList != null) {
callbackList.remove(callback);
}
break;
+ case MONITOR_CALLBACK_BINDER_DIED:
+ callback = (IGeofenceHardwareMonitorCallback) msg.obj;
+ if (DEBUG) Log.d(TAG, "Monitor callback reaped:" + callback);
+ callbackList = mCallbacks[msg.arg1];
+ if (callbackList != null && callbackList.contains(callback)) {
+ callbackList.remove(callback);
+ }
+ }
+ }
+ };
+
+ // All operations on mReaper
+ private Handler mReaperHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ Reaper r;
+ IGeofenceHardwareCallback callback;
+ IGeofenceHardwareMonitorCallback monitorCallback;
+ int monitoringType;
+
+ switch (msg.what) {
+ case REAPER_GEOFENCE_ADDED:
+ callback = (IGeofenceHardwareCallback) msg.obj;
+ monitoringType = msg.arg1;
+ r = new Reaper(callback, monitoringType);
+ if (!mReapers.contains(r)) {
+ mReapers.add(r);
+ IBinder b = callback.asBinder();
+ try {
+ b.linkToDeath(r, 0);
+ } catch (RemoteException e) {}
+ }
+ break;
+ case REAPER_MONITOR_CALLBACK_ADDED:
+ monitorCallback = (IGeofenceHardwareMonitorCallback) msg.obj;
+ monitoringType = msg.arg1;
+
+ r = new Reaper(monitorCallback, monitoringType);
+ if (!mReapers.contains(r)) {
+ mReapers.add(r);
+ IBinder b = monitorCallback.asBinder();
+ try {
+ b.linkToDeath(r, 0);
+ } catch (RemoteException e) {}
+ }
+ break;
+ case REAPER_REMOVED:
+ r = (Reaper) msg.obj;
+ mReapers.remove(r);
}
}
};
@@ -567,6 +660,57 @@
return RESOLUTION_LEVEL_NONE;
}
+ class Reaper implements IBinder.DeathRecipient {
+ private IGeofenceHardwareMonitorCallback mMonitorCallback;
+ private IGeofenceHardwareCallback mCallback;
+ private int mMonitoringType;
+
+ Reaper(IGeofenceHardwareCallback c, int monitoringType) {
+ mCallback = c;
+ mMonitoringType = monitoringType;
+ }
+
+ Reaper(IGeofenceHardwareMonitorCallback c, int monitoringType) {
+ mMonitorCallback = c;
+ mMonitoringType = monitoringType;
+ }
+
+ @Override
+ public void binderDied() {
+ Message m;
+ if (mCallback != null) {
+ m = mGeofenceHandler.obtainMessage(GEOFENCE_CALLBACK_BINDER_DIED, mCallback);
+ m.arg1 = mMonitoringType;
+ mGeofenceHandler.sendMessage(m);
+ } else if (mMonitorCallback != null) {
+ m = mCallbacksHandler.obtainMessage(MONITOR_CALLBACK_BINDER_DIED, mMonitorCallback);
+ m.arg1 = mMonitoringType;
+ mCallbacksHandler.sendMessage(m);
+ }
+ Message reaperMessage = mReaperHandler.obtainMessage(REAPER_REMOVED, this);
+ mReaperHandler.sendMessage(reaperMessage);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + (mCallback != null ? mCallback.hashCode() : 0);
+ result = 31 * result + (mMonitorCallback != null ? mMonitorCallback.hashCode() : 0);
+ result = 31 * result + mMonitoringType;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (obj == this) return true;
+
+ Reaper rhs = (Reaper) obj;
+ return rhs.mCallback == mCallback && rhs.mMonitorCallback == mMonitorCallback &&
+ rhs.mMonitoringType == mMonitoringType;
+ }
+ }
+
int getAllowedResolutionLevel(int pid, int uid) {
if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
pid, uid) == PackageManager.PERMISSION_GRANTED) {
diff --git a/core/java/android/hardware/location/GeofenceHardwareMonitorCallback.java b/core/java/android/hardware/location/GeofenceHardwareMonitorCallback.java
new file mode 100644
index 0000000..b8e927e
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareMonitorCallback.java
@@ -0,0 +1,37 @@
+/*
+ * 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.hardware.location;
+
+import android.location.Location;
+
+/**
+ * The callback class associated with the status change of hardware montiors
+ * in {@link GeofenceHardware}
+ */
+public abstract class GeofenceHardwareMonitorCallback {
+ /**
+ * The callback called when the state of a monitoring system changes.
+ * {@link GeofenceHardware#MONITORING_TYPE_GPS_HARDWARE} is an example of a
+ * monitoring system
+ *
+ * @param monitoringType The type of the monitoring system.
+ * @param available Indicates whether the system is currenty available or not.
+ * @param location The last known location according to the monitoring system.
+ */
+ public void onMonitoringSystemChange(int monitoringType, boolean available, Location location) {
+ }
+}
diff --git a/core/java/android/hardware/location/GeofenceHardwareRequest.java b/core/java/android/hardware/location/GeofenceHardwareRequest.java
new file mode 100644
index 0000000..6e7b592
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareRequest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.hardware.location;
+
+import android.location.Location;
+
+/**
+ * This class represents the characteristics of the geofence.
+ *
+ * <p> Use this in conjunction with {@link GeofenceHardware} APIs.
+ */
+
+public final class GeofenceHardwareRequest {
+ static final int GEOFENCE_TYPE_CIRCLE = 0;
+ private int mType;
+ private double mLatitude;
+ private double mLongitude;
+ private double mRadius;
+ private int mLastTransition = GeofenceHardware.GEOFENCE_UNCERTAIN;
+ private int mUnknownTimer = 30000; // 30 secs
+ private int mMonitorTransitions = GeofenceHardware.GEOFENCE_UNCERTAIN |
+ GeofenceHardware.GEOFENCE_ENTERED | GeofenceHardware.GEOFENCE_EXITED;
+ private int mNotificationResponsiveness = 5000; // 5 secs
+
+ private void setCircularGeofence(double latitude, double longitude, double radius) {
+ mLatitude = latitude;
+ mLongitude = longitude;
+ mRadius = radius;
+ mType = GEOFENCE_TYPE_CIRCLE;
+ }
+
+ /**
+ * Create a circular geofence.
+ *
+ * @param latitude Latitude of the geofence
+ * @param longitude Longitude of the geofence
+ * @param radius Radius of the geofence (in meters)
+ */
+ public static GeofenceHardwareRequest createCircularGeofence(double latitude,
+ double longitude, double radius) {
+ GeofenceHardwareRequest geofenceRequest = new GeofenceHardwareRequest();
+ geofenceRequest.setCircularGeofence(latitude, longitude, radius);
+ return geofenceRequest;
+ }
+
+ /**
+ * Set the last known transition of the geofence.
+ *
+ * @param lastTransition The current state of the geofence. Can be one of
+ * {@link GeofenceHardware#GEOFENCE_ENTERED}, {@link GeofenceHardware#GEOFENCE_EXITED},
+ * {@link GeofenceHardware#GEOFENCE_UNCERTAIN}.
+ */
+ public void setLastTransition(int lastTransition) {
+ mLastTransition = lastTransition;
+ }
+
+ /**
+ * Set the unknown timer for this geofence.
+ *
+ * @param unknownTimer The time limit after which the
+ * {@link GeofenceHardware#GEOFENCE_UNCERTAIN} transition
+ * should be triggered. This paramter is defined in milliseconds.
+ */
+ public void setUnknownTimer(int unknownTimer) {
+ mUnknownTimer = unknownTimer;
+ }
+
+ /**
+ * Set the transitions to be monitored.
+ *
+ * @param monitorTransitions Bitwise OR of {@link GeofenceHardware#GEOFENCE_ENTERED},
+ * {@link GeofenceHardware#GEOFENCE_EXITED}, {@link GeofenceHardware#GEOFENCE_UNCERTAIN}
+ */
+ public void setMonitorTransitions(int monitorTransitions) {
+ mMonitorTransitions = monitorTransitions;
+ }
+
+ /**
+ * Set the notification responsiveness of the geofence.
+ *
+ * @param notificationResponsiveness (milliseconds) Defines the best-effort description
+ * of how soon should the callback be called when the transition
+ * associated with the Geofence is triggered. For instance, if
+ * set to 1000 millseconds with {@link GeofenceHardware#GEOFENCE_ENTERED},
+ * the callback will be called 1000 milliseconds within entering
+ * the geofence.
+ */
+ public void setNotificationResponsiveness(int notificationResponsiveness) {
+ mNotificationResponsiveness = notificationResponsiveness;
+ }
+
+ /**
+ * Returns the latitude of this geofence.
+ */
+ public double getLatitude() {
+ return mLatitude;
+ }
+
+ /**
+ * Returns the longitude of this geofence.
+ */
+ public double getLongitude() {
+ return mLongitude;
+ }
+
+ /**
+ * Returns the radius of this geofence.
+ */
+ public double getRadius() {
+ return mRadius;
+ }
+
+ /**
+ * Returns transitions monitored for this geofence.
+ */
+ public int getMonitorTransitions() {
+ return mMonitorTransitions;
+ }
+
+ /**
+ * Returns the unknownTimer of this geofence.
+ */
+ public int getUnknownTimer() {
+ return mUnknownTimer;
+ }
+
+ /**
+ * Returns the notification responsiveness of this geofence.
+ */
+ public int getNotificationResponsiveness() {
+ return mNotificationResponsiveness;
+ }
+
+ /**
+ * Returns the last transition of this geofence.
+ */
+ public int getLastTransition() {
+ return mLastTransition;
+ }
+
+ int getType() {
+ return mType;
+ }
+}
diff --git a/core/java/android/hardware/location/GeofenceHardwareService.java b/core/java/android/hardware/location/GeofenceHardwareService.java
index 0eccee6..3bc70ee 100644
--- a/core/java/android/hardware/location/GeofenceHardwareService.java
+++ b/core/java/android/hardware/location/GeofenceHardwareService.java
@@ -68,23 +68,28 @@
mGeofenceHardwareImpl.setGpsHardwareGeofence(service);
}
- public int[] getMonitoringTypesAndStatus() {
+ public int[] getMonitoringTypes() {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware geofence");
- return mGeofenceHardwareImpl.getMonitoringTypesAndStatus();
+ return mGeofenceHardwareImpl.getMonitoringTypes();
}
- public boolean addCircularFence(int id, double lat, double longitude, double radius,
- int lastTransition, int monitorTransitions, int
- notificationResponsiveness, int unknownTimer, int monitoringType,
- IGeofenceHardwareCallback callback) {
+ public int getStatusOfMonitoringType(int monitoringType) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ return mGeofenceHardwareImpl.getStatusOfMonitoringType(monitoringType);
+ }
+ public boolean addCircularFence(int id, int monitoringType, double lat, double longitude,
+ double radius, int lastTransition, int monitorTransitions, int
+ notificationResponsiveness, int unknownTimer, IGeofenceHardwareCallback callback) {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware geofence");
checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
- return mGeofenceHardwareImpl.addCircularFence(id, lat, longitude, radius,
- lastTransition, monitorTransitions, notificationResponsiveness, unknownTimer,
- monitoringType, callback);
+ return mGeofenceHardwareImpl.addCircularFence(id, monitoringType, lat, longitude,
+ radius, lastTransition, monitorTransitions, notificationResponsiveness,
+ unknownTimer, callback);
}
public boolean removeGeofence(int id, int monitoringType) {
@@ -103,16 +108,16 @@
return mGeofenceHardwareImpl.pauseGeofence(id, monitoringType);
}
- public boolean resumeGeofence(int id, int monitorTransitions, int monitoringType) {
+ public boolean resumeGeofence(int id, int monitoringType, int monitorTransitions) {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware geofence");
checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
- return mGeofenceHardwareImpl.resumeGeofence(id, monitorTransitions, monitoringType);
+ return mGeofenceHardwareImpl.resumeGeofence(id, monitoringType, monitorTransitions);
}
public boolean registerForMonitorStateChangeCallback(int monitoringType,
- IGeofenceHardwareCallback callback) {
+ IGeofenceHardwareMonitorCallback callback) {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware geofence");
@@ -122,7 +127,7 @@
}
public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
- IGeofenceHardwareCallback callback) {
+ IGeofenceHardwareMonitorCallback callback) {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware geofence");
diff --git a/core/java/android/hardware/location/IGeofenceHardware.aidl b/core/java/android/hardware/location/IGeofenceHardware.aidl
index 4ba02b8..6900070 100644
--- a/core/java/android/hardware/location/IGeofenceHardware.aidl
+++ b/core/java/android/hardware/location/IGeofenceHardware.aidl
@@ -18,19 +18,21 @@
import android.location.IGpsGeofenceHardware;
import android.hardware.location.IGeofenceHardwareCallback;
+import android.hardware.location.IGeofenceHardwareMonitorCallback;
/** @hide */
interface IGeofenceHardware {
void setGpsGeofenceHardware(in IGpsGeofenceHardware service);
- int[] getMonitoringTypesAndStatus();
- boolean addCircularFence(int id, double lat, double longitude, double radius,
- int lastTransition, int monitorTransitions, int notificationResponsiveness,
- int unknownTimer, int monitoringType, in IGeofenceHardwareCallback callback);
+ int[] getMonitoringTypes();
+ int getStatusOfMonitoringType(int monitoringType);
+ boolean addCircularFence(int id, int monitoringType, double lat, double longitude,
+ double radius, int lastTransition, int monitorTransitions,
+ int notificationResponsiveness, int unknownTimer,in IGeofenceHardwareCallback callback);
boolean removeGeofence(int id, int monitoringType);
boolean pauseGeofence(int id, int monitoringType);
- boolean resumeGeofence(int id, int monitorTransitions, int monitoringType);
+ boolean resumeGeofence(int id, int monitoringType, int monitorTransitions);
boolean registerForMonitorStateChangeCallback(int monitoringType,
- IGeofenceHardwareCallback callback);
+ IGeofenceHardwareMonitorCallback callback);
boolean unregisterForMonitorStateChangeCallback(int monitoringType,
- IGeofenceHardwareCallback callback);
+ IGeofenceHardwareMonitorCallback callback);
}
diff --git a/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl b/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl
index 678fc49..3a8f430 100644
--- a/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl
+++ b/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl
@@ -20,8 +20,7 @@
/** @hide */
oneway interface IGeofenceHardwareCallback {
- void onMonitoringSystemChange(int monitoringType, boolean available, in Location location);
- void onGeofenceChange(int geofenceId, int transition, in Location location,
+ void onGeofenceTransition(int geofenceId, int transition, in Location location,
long timestamp, int monitoringType);
void onGeofenceAdd(int geofenceId, int status);
void onGeofenceRemove(int geofenceId, int status);
diff --git a/core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl b/core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl
new file mode 100644
index 0000000..0b6e04b
--- /dev/null
+++ b/core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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.hardware.location;
+
+import android.location.Location;
+
+/** @hide */
+oneway interface IGeofenceHardwareMonitorCallback {
+ void onMonitoringSystemChange(int monitoringType, boolean available, in Location location);
+}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 2e8092a..a11358a 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -42,7 +42,8 @@
int getUserHandle(int userSerialNumber);
Bundle getUserRestrictions(int userHandle);
void setUserRestrictions(in Bundle restrictions, int userHandle);
- void setApplicationRestrictions(in String packageName, in List<RestrictionEntry> entries,
+ void setApplicationRestrictions(in String packageName, in Bundle restrictions,
int userHandle);
- List<RestrictionEntry> getApplicationRestrictions(in String packageName, int userHandle);
+ Bundle getApplicationRestrictions(in String packageName);
+ Bundle getApplicationRestrictionsForUser(in String packageName, int userHandle);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index e580e2b..df065e9 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -142,6 +142,7 @@
private static UserManager sInstance = null;
+ /** @hide */
public synchronized static UserManager get(Context context) {
if (sInstance == null) {
sInstance = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -578,13 +579,29 @@
return -1;
}
+ /**
+ * Returns a Bundle containing any saved application restrictions for this user, for the
+ * given package name. Only an application with this package name can call this method.
+ * @param packageName the package name of the calling application
+ * @return a Bundle with the restrictions as key/value pairs, or null if there are no
+ * saved restrictions. The values can be of type Boolean, String or String[], depending
+ * on the restriction type, as defined by the application.
+ */
+ public Bundle getApplicationRestrictions(String packageName) {
+ try {
+ return mService.getApplicationRestrictions(packageName);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get application restrictions for package " + packageName);
+ }
+ return null;
+ }
/**
* @hide
*/
- public List<RestrictionEntry> getApplicationRestrictions(String packageName, UserHandle user) {
+ public Bundle getApplicationRestrictions(String packageName, UserHandle user) {
try {
- return mService.getApplicationRestrictions(packageName, user.getIdentifier());
+ return mService.getApplicationRestrictionsForUser(packageName, user.getIdentifier());
} catch (RemoteException re) {
Log.w(TAG, "Could not get application restrictions for user " + user.getIdentifier());
}
@@ -594,10 +611,10 @@
/**
* @hide
*/
- public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries,
+ public void setApplicationRestrictions(String packageName, Bundle restrictions,
UserHandle user) {
try {
- mService.setApplicationRestrictions(packageName, entries, user.getIdentifier());
+ mService.setApplicationRestrictions(packageName, restrictions, user.getIdentifier());
} catch (RemoteException re) {
Log.w(TAG, "Could not set application restrictions for user " + user.getIdentifier());
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6b09fac..13ddcea 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3268,6 +3268,7 @@
/**
* This preference contains the string that shows for owner info on LockScreen.
* @hide
+ * @deprecated
*/
public static final String LOCK_SCREEN_OWNER_INFO = "lock_screen_owner_info";
@@ -3295,6 +3296,7 @@
/**
* This preference enables showing the owner info on LockScreen.
* @hide
+ * @deprecated
*/
public static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
"lock_screen_owner_info_enabled";
@@ -4110,9 +4112,7 @@
MOUNT_UMS_AUTOSTART,
MOUNT_UMS_PROMPT,
MOUNT_UMS_NOTIFY_ENABLED,
- UI_NIGHT_MODE,
- LOCK_SCREEN_OWNER_INFO,
- LOCK_SCREEN_OWNER_INFO_ENABLED
+ UI_NIGHT_MODE
};
/**
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 24c3128..1ecdf30 100644
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -70,7 +70,7 @@
* Gets the source of the event.
*
* @return The event source or {@link InputDevice#SOURCE_UNKNOWN} if unknown.
- * @see InputDevice#getSourceInfo
+ * @see InputDevice#getSources
*/
public abstract int getSource();
diff --git a/core/java/android/view/InputFilter.java b/core/java/android/view/InputFilter.java
index c25b87b..4aba30c 100644
--- a/core/java/android/view/InputFilter.java
+++ b/core/java/android/view/InputFilter.java
@@ -40,7 +40,7 @@
* <li>Input events are then asynchronously delivered to the input filter's
* {@link #onInputEvent(InputEvent)} method instead of being enqueued for dispatch to
* applications as usual. The input filter only receives input events that were
- * generated by input device; the input filter will not receive input events that were
+ * generated by an input device; the input filter will not receive input events that were
* injected into the system by other means, such as by instrumentation.</li>
* <li>The input filter processes and optionally transforms the stream of events. For example,
* it may transform a sequence of motion events representing an accessibility gesture into
@@ -68,7 +68,7 @@
* The input filter must take into account the fact that the input events coming from different
* devices or even different sources all consist of distinct streams of input.
* Use {@link InputEvent#getDeviceId()} and {@link InputEvent#getSource()} to identify
- * the source of the event and its semantics. There are be multiple sources of keys,
+ * the source of the event and its semantics. There may be multiple sources of keys,
* touches and other input: they must be kept separate.
* </p>
* <h3>Policy flags</h3>
@@ -88,7 +88,7 @@
* The input filter should clear its internal state about the gesture and then send key or
* motion events to the dispatcher to cancel any keys or pointers that are down.
* </p><p>
- * Corollary: Events that set sent to the dispatcher should usually include the
+ * Corollary: Events that get sent to the dispatcher should usually include the
* {@link WindowManagerPolicy#FLAG_PASS_TO_USER} flag. Otherwise, they will be dropped!
* </p><p>
* It may be prudent to disable automatic key repeating for synthetic key events
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index edfef56..4989c3a 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -220,14 +220,14 @@
/**
* Gets a {@link Canvas} for drawing into this surface.
*
- * After drawing into the provided {@link Canvas}, the caller should
+ * After drawing into the provided {@link Canvas}, the caller must
* invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
*
* @param inOutDirty A rectangle that represents the dirty region that the caller wants
* to redraw. This function may choose to expand the dirty rectangle if for example
* the surface has been resized or if the previous contents of the surface were
- * not available. The caller should redraw the entire dirty region as represented
- * by the contents of the dirty rect upon return from this function.
+ * not available. The caller must redraw the entire dirty region as represented
+ * by the contents of the inOutDirty rectangle upon return from this function.
* The caller may also pass <code>null</code> instead, in the case where the
* entire surface should be redrawn.
* @return A canvas for drawing into the surface.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 14fa9cb..793fb5e 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -755,12 +755,36 @@
mHandler.sendMessage(msg);
}
+ /**
+ * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
+ *
+ * After drawing into the provided {@link Canvas}, the caller must
+ * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
+ *
+ * The caller must redraw the entire surface.
+ * @return A canvas for drawing into the surface.
+ */
public Canvas lockCanvas() {
return internalLockCanvas(null);
}
- public Canvas lockCanvas(Rect dirty) {
- return internalLockCanvas(dirty);
+ /**
+ * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
+ *
+ * After drawing into the provided {@link Canvas}, the caller must
+ * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
+ *
+ * @param inOutDirty A rectangle that represents the dirty region that the caller wants
+ * to redraw. This function may choose to expand the dirty rectangle if for example
+ * the surface has been resized or if the previous contents of the surface were
+ * not available. The caller must redraw the entire dirty region as represented
+ * by the contents of the inOutDirty rectangle upon return from this function.
+ * The caller may also pass <code>null</code> instead, in the case where the
+ * entire surface should be redrawn.
+ * @return A canvas for drawing into the surface.
+ */
+ public Canvas lockCanvas(Rect inOutDirty) {
+ return internalLockCanvas(inOutDirty);
}
private final Canvas internalLockCanvas(Rect dirty) {
@@ -810,6 +834,12 @@
return null;
}
+ /**
+ * Posts the new contents of the {@link Canvas} to the surface and
+ * releases the {@link Canvas}.
+ *
+ * @param canvas The canvas previously obtained from {@link #lockCanvas}.
+ */
public void unlockCanvasAndPost(Canvas canvas) {
mSurface.unlockCanvasAndPost(canvas);
mSurfaceLock.unlock();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 789fe40..7aa2cbf 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -73,6 +73,7 @@
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.*;
@@ -1572,6 +1573,8 @@
*/
protected Object mTag;
+ private Scene mCurrentScene = null;
+
// for mPrivateFlags:
/** {@hide} */
static final int PFLAG_WANTS_FOCUS = 0x00000001;
@@ -12037,6 +12040,8 @@
mCurrentAnimation = null;
+ mCurrentScene = null;
+
resetAccessibilityStateChanged();
}
@@ -13395,18 +13400,32 @@
/**
* Sets a rectangular area on this view to which the view will be clipped
- * it is drawn. Setting the value to null will remove the clip bounds
+ * when it is drawn. Setting the value to null will remove the clip bounds
* and the view will draw normally, using its full bounds.
*
* @param clipBounds The rectangular area, in the local coordinates of
* this view, to which future drawing operations will be clipped.
*/
public void setClipBounds(Rect clipBounds) {
- mClipBounds = clipBounds;
if (clipBounds != null) {
- invalidate(clipBounds);
+ if (clipBounds.equals(mClipBounds)) {
+ return;
+ }
+ if (mClipBounds == null) {
+ invalidate();
+ mClipBounds = new Rect(clipBounds);
+ } else {
+ invalidate(Math.min(mClipBounds.left, clipBounds.left),
+ Math.min(mClipBounds.top, clipBounds.top),
+ Math.max(mClipBounds.right, clipBounds.right),
+ Math.max(mClipBounds.bottom, clipBounds.bottom));
+ mClipBounds.set(clipBounds);
+ }
} else {
- invalidate();
+ if (mClipBounds != null) {
+ invalidate();
+ mClipBounds = null;
+ }
}
}
@@ -17744,6 +17763,31 @@
}
/**
+ * 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/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 98df064..528eadd 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -16,7 +16,6 @@
package android.view;
-import android.animation.Animatable;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.animation.TimeInterpolator;
diff --git a/core/java/android/view/transition/AutoTransition.java b/core/java/android/view/transition/AutoTransition.java
new file mode 100644
index 0000000..d94cf2c
--- /dev/null
+++ b/core/java/android/view/transition/AutoTransition.java
@@ -0,0 +1,34 @@
+/*
+ * 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.view.transition;
+
+/**
+ * Utility class for creating a default transition that automatically fades,
+ * moves, and resizes views during a scene change.
+ */
+public class AutoTransition extends TransitionGroup {
+
+ /**
+ * Constructs an AutoTransition object, which is a TransitionGroup 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));
+ }
+}
diff --git a/core/java/android/view/transition/Crossfade.java b/core/java/android/view/transition/Crossfade.java
new file mode 100644
index 0000000..babf58f
--- /dev/null
+++ b/core/java/android/view/transition/Crossfade.java
@@ -0,0 +1,163 @@
+/*
+ * 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.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+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;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+
+/**
+ * This transition captures bitmap representations of target views before and
+ * after the scene change and fades between them.
+ *
+ * <p>Note: This transition is not compatible with {@link TextureView}
+ * or {@link SurfaceView}.</p>
+ */
+public class Crossfade extends Transition {
+ // TODO: Add a hook that lets a Transition call user code to query whether it should run on
+ // a given target view. This would save bitmap comparisons in this transition, for example.
+
+ private static final String LOG_TAG = "Crossfade";
+
+ private static final String PROPNAME_BITMAP = "android:crossfade:bitmap";
+ private static final String PROPNAME_DRAWABLE = "android:crossfade:drawable";
+ private static final String PROPNAME_BOUNDS = "android:crossfade:bounds";
+
+ private static RectEvaluator sRectEvaluator = new RectEvaluator();
+
+ @Override
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return false;
+ }
+ final View view = startValues.view;
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+ Bitmap startBitmap = (Bitmap) startVals.get(PROPNAME_BITMAP);
+ Bitmap endBitmap = (Bitmap) endVals.get(PROPNAME_BITMAP);
+ Drawable startDrawable = (Drawable) startVals.get(PROPNAME_DRAWABLE);
+ Drawable endDrawable = (Drawable) endVals.get(PROPNAME_DRAWABLE);
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "StartBitmap.sameAs(endBitmap) = " + startBitmap.sameAs(endBitmap) +
+ " for start, end: " + startBitmap + ", " + endBitmap);
+ }
+ if (startDrawable != null && endDrawable != null && !startBitmap.sameAs(endBitmap)) {
+ view.getOverlay().add(endDrawable);
+ view.getOverlay().add(startDrawable);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+
+ final View view = endValues.view;
+ Rect startBounds = (Rect) startVals.get(PROPNAME_BOUNDS);
+ Rect endBounds = (Rect) endVals.get(PROPNAME_BOUNDS);
+ final BitmapDrawable startDrawable = (BitmapDrawable) startVals.get(PROPNAME_DRAWABLE);
+ final BitmapDrawable endDrawable = (BitmapDrawable) endVals.get(PROPNAME_DRAWABLE);
+
+ // The transition works by placing the end drawable under the start drawable and
+ // gradually fading out the start drawable. So it's not really a cross-fade, but rather
+ // a reveal of the end scene over time. Also, animate the bounds of both drawables
+ // to mimic the change in the size of the view itself between scenes.
+ ObjectAnimator anim = ObjectAnimator.ofInt(startDrawable, "alpha", 0);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ // TODO: some way to auto-invalidate views based on drawable changes? callbacks?
+ view.invalidate(startDrawable.getBounds());
+ }
+ });
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Crossfade: created anim " + anim + " for start, end values " +
+ startValues + ", " + endValues);
+ }
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.getOverlay().remove(startDrawable);
+ view.getOverlay().remove(endDrawable);
+ }
+ });
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(anim);
+ if (!startBounds.equals(endBounds)) {
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "animating from startBounds to endBounds: " +
+ startBounds + ", " + endBounds);
+ }
+ Animator anim2 = ObjectAnimator.ofObject(startDrawable, "bounds",
+ sRectEvaluator, startBounds, endBounds);
+ Animator anim3 = ObjectAnimator.ofObject(endDrawable, "bounds",
+ sRectEvaluator, startBounds, endBounds);
+ set.playTogether(anim2);
+ set.playTogether(anim3);
+ }
+ return set;
+ }
+
+ @Override
+ protected void captureValues(TransitionValues values, boolean start) {
+ View view = values.view;
+ values.values.put(PROPNAME_BOUNDS, new Rect(0, 0,
+ view.getWidth(), view.getHeight()));
+
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Captured bounds " + values.values.get(PROPNAME_BOUNDS) + ": start = " +
+ start);
+ }
+ Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ if (view instanceof TextureView) {
+ bitmap = ((TextureView) view).getBitmap();
+ } else {
+ Canvas c = new Canvas(bitmap);
+ view.draw(c);
+ }
+ values.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(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+ values.values.put(PROPNAME_DRAWABLE, drawable);
+ }
+
+}
diff --git a/core/java/android/view/transition/Fade.java b/core/java/android/view/transition/Fade.java
new file mode 100644
index 0000000..8e4909d
--- /dev/null
+++ b/core/java/android/view/transition/Fade.java
@@ -0,0 +1,209 @@
+/*
+ * 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.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * This transition tracks changes to the visibility of target views in the
+ * start and end scenes and fades views in or out when they become visible
+ * 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.
+ */
+public class Fade extends Visibility {
+
+ private static final String LOG_TAG = "Fade";
+
+ /**
+ * Fading mode used in {@link #Fade(int)} to make the transition
+ * operate on targets that are appearing. Maybe be combined with
+ * {@link #OUT} to fade both in and out.
+ */
+ public static final int IN = 0x1;
+ /**
+ * Fading mode used in {@link #Fade(int)} to make the transition
+ * operate on targets that are disappearing. Maybe be combined with
+ * {@link #IN} to fade both in and out.
+ */
+ public static final int OUT = 0x2;
+
+ private int mFadingMode;
+
+ /**
+ * Constructs a Fade transition that will fade targets in and out.
+ */
+ public Fade() {
+ this(IN | OUT);
+ }
+
+ /**
+ * Constructs a Fade transition that will fade targets in
+ * and/or out, according to the value of fadingMode.
+ *
+ * @param fadingMode The behavior of this transition, a combination of
+ * {@link #IN} and {@link #OUT}.
+ */
+ public Fade(int fadingMode) {
+ mFadingMode = fadingMode;
+ }
+
+ /**
+ * Utility method to handle creating and running the Animator.
+ */
+ private Animator runAnimation(View view, float startAlpha, float endAlpha,
+ Animator.AnimatorListener listener) {
+ final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", startAlpha, endAlpha);
+ if (listener != null) {
+ anim.addListener(listener);
+ }
+ // TODO: Maybe extract a method into Transition to run an animation that handles the
+ // duration/startDelay stuff for all subclasses.
+ return anim;
+ }
+
+ @Override
+ protected boolean preAppear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ if ((mFadingMode & IN) != IN) {
+ return false;
+ }
+ endView.setAlpha(0);
+ return true;
+ }
+
+ @Override
+ protected Animator appear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ if ((mFadingMode & IN) != IN) {
+ return null;
+ }
+ // TODO: hack - retain original value from before preAppear
+ return runAnimation(endView, 0, 1, null);
+ // TODO: end listener to make sure we end at 1 no matter what
+ }
+
+ @Override
+ protected boolean preDisappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ if ((mFadingMode & OUT) != OUT) {
+ return false;
+ }
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Fade.predisappear: startView, startVis, endView, endVis = " +
+ startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
+ }
+ View view;
+ View overlayView = null;
+ View viewToKeep = null;
+ if (endView == null) {
+ // view was removed: add the start view to the Overlay
+ view = startView;
+ overlayView = view;
+ } else {
+ // visibility change
+ if (endVisibility == View.INVISIBLE) {
+ view = endView;
+ viewToKeep = view;
+ } else {
+ // Becoming GONE
+ if (startView == endView) {
+ view = endView;
+ viewToKeep = view;
+ } else {
+ view = startView;
+ overlayView = view;
+ }
+ }
+ }
+ // TODO: add automatic facility to Visibility superclass for keeping views around
+ if (overlayView != null) {
+ // TODO: Need to do this for general case of adding to overlay
+ sceneRoot.getOverlay().add(overlayView);
+ return true;
+ }
+ if (viewToKeep != null) {
+ // TODO: find a different way to do this, like just changing the view to be
+ // VISIBLE for the duration of the transition
+ viewToKeep.setVisibility((View.VISIBLE));
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected Animator disappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ if ((mFadingMode & OUT) != OUT) {
+ return null;
+ }
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Fade.disappear: startView, startVis, endView, endVis = " +
+ startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
+ }
+ View view;
+ View overlayView = null;
+ View viewToKeep = null;
+ final int finalVisibility = endVisibility;
+ if (endView == null) {
+ // view was removed: add the start view to the Overlay
+ view = startView;
+ overlayView = view;
+ } else {
+ // visibility change
+ if (endVisibility == View.INVISIBLE) {
+ view = endView;
+ viewToKeep = view;
+ } else {
+ // Becoming GONE
+ if (startView == endView) {
+ view = endView;
+ viewToKeep = view;
+ } else {
+ view = startView;
+ overlayView = view;
+ }
+ }
+ }
+ // TODO: add automatic facility to Visibility superclass for keeping views around
+ final float startAlpha = view.getAlpha();
+ float endAlpha = 0;
+ final View finalView = view;
+ final View finalOverlayView = overlayView;
+ final View finalViewToKeep = viewToKeep;
+ final ViewGroup finalSceneRoot = sceneRoot;
+ final Animator.AnimatorListener endListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finalView.setAlpha(startAlpha);
+ // TODO: restore view offset from overlay repositioning
+ if (finalViewToKeep != null) {
+ finalViewToKeep.setVisibility(finalVisibility);
+ }
+ if (finalOverlayView != null) {
+ finalSceneRoot.getOverlay().remove(finalOverlayView);
+ }
+ }
+ };
+ return runAnimation(view, startAlpha, endAlpha, endListener);
+ }
+
+}
\ No newline at end of file
diff --git a/core/java/android/view/transition/Move.java b/core/java/android/view/transition/Move.java
new file mode 100644
index 0000000..3bd57bd
--- /dev/null
+++ b/core/java/android/view/transition/Move.java
@@ -0,0 +1,299 @@
+/*
+ * 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.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.RectEvaluator;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+
+/**
+ * This transition captures the layout bounds of target views before and after
+ * the scene change and animates those changes during the transition.
+ */
+public class Move 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";
+ int[] tempLocation = new int[2];
+ boolean mResizeClip = false;
+ boolean mReparent = false;
+
+ private static RectEvaluator sRectEvaluator = new RectEvaluator();
+
+ public void setResizeClip(boolean resizeClip) {
+ mResizeClip = resizeClip;
+ }
+
+ /**
+ * Setting this flag tells Move 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
+ * the transition to determine which parents are the same.
+ *
+ * @param reparent true if the transition should track the parent
+ * container of target views and animate parent changes.
+ */
+ public void setReparent(boolean reparent) {
+ mReparent = reparent;
+ }
+
+ @Override
+ protected void captureValues(TransitionValues values, boolean start) {
+ View view = values.view;
+ values.values.put(PROPNAME_BOUNDS, new Rect(view.getLeft(), view.getTop(),
+ view.getRight(), view.getBottom()));
+ values.values.put(PROPNAME_PARENT, values.view.getParent());
+ values.view.getLocationInWindow(tempLocation);
+ values.values.put(PROPNAME_WINDOW_X, tempLocation[0]);
+ values.values.put(PROPNAME_WINDOW_Y, tempLocation[1]);
+ }
+
+ @Override
+ protected Animator play(final ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ final View view = endValues.view;
+ if (view.getParent() == null) {
+ // TODO: Might want to make it possible to Move an disappearing view.
+ // This workaround is here because if a parallel Fade is not running on the view
+ // Then it won't get added to the hierarchy and the animator below will not fire,
+ // causing the transition to not end
+ return null;
+ }
+ // TODO: need to handle non-VG case?
+ ViewGroup startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
+ ViewGroup endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
+ if (startParent == null || endParent == null) {
+ return null;
+ }
+ boolean parentsEqual = (startParent == endParent) ||
+ (startParent.getId() == endParent.getId());
+ if (!mReparent || parentsEqual) {
+ // Common case - view belongs to the same layout before/after. Just animate its bounds
+ Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+ Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ int startLeft = startBounds.left;
+ int endLeft = endBounds.left;
+ int startTop = startBounds.top;
+ int endTop = endBounds.top;
+ int startRight = startBounds.right;
+ int endRight = endBounds.right;
+ int startBottom = startBounds.bottom;
+ int endBottom = endBounds.bottom;
+ int startWidth = startRight - startLeft;
+ int startHeight = startBottom - startTop;
+ int endWidth = endRight - endLeft;
+ int endHeight = endBottom - endTop;
+ int numChanges = 0;
+ if (startWidth != 0 && startHeight != 0 && endWidth != 0 && endHeight != 0) {
+ if (startLeft != endLeft) ++numChanges;
+ if (startTop != endTop) ++numChanges;
+ if (startRight != endRight) ++numChanges;
+ if (startBottom != endBottom) ++numChanges;
+ }
+ if (numChanges > 0) {
+ if (!mResizeClip) {
+ PropertyValuesHolder pvh[] = new PropertyValuesHolder[numChanges];
+ int pvhIndex = 0;
+ if (startLeft != endLeft) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofInt("left", startLeft, endLeft);
+ }
+ if (startTop != endTop) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofInt("top", startTop, endTop);
+ }
+ if (startRight != endRight) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofInt("right",
+ startRight, endRight);
+ }
+ if (startBottom != endBottom) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofInt("bottom",
+ startBottom, endBottom);
+ }
+ ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(view, pvh);
+ if (view.getParent() instanceof ViewGroup) {
+ final ViewGroup parent = (ViewGroup) view.getParent();
+ parent.suppressLayout(true);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ parent.suppressLayout(false);
+ }
+ });
+ }
+ return anim;
+ } else {
+ // Animate location with translationX/Y and size with clip bounds
+ float transXDelta = endLeft - startLeft;
+ float transYDelta = endTop - startTop;
+ int widthDelta = endWidth - startWidth;
+ int heightDelta = endHeight - startHeight;
+ numChanges = 0;
+ if (transXDelta != 0) numChanges++;
+ if (transYDelta != 0) numChanges++;
+ if (widthDelta != 0 || heightDelta != 0) numChanges++;
+ PropertyValuesHolder pvh[] = new PropertyValuesHolder[numChanges];
+ int pvhIndex = 0;
+ if (transXDelta != 0) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofFloat("translationX",
+ view.getTranslationX(), 0);
+ }
+ if (transYDelta != 0) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofFloat("translationY",
+ view.getTranslationY(), 0);
+ }
+ if (widthDelta != 0 || heightDelta != 0) {
+ Rect tempStartBounds = new Rect(0, 0, startWidth, startHeight);
+ Rect tempEndBounds = new Rect(0, 0, endWidth, endHeight);
+ pvh[pvhIndex++] = PropertyValuesHolder.ofObject("clipBounds",
+ sRectEvaluator, tempStartBounds, tempEndBounds);
+ }
+ ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(view, pvh);
+ if (view.getParent() instanceof ViewGroup) {
+ final ViewGroup parent = (ViewGroup) view.getParent();
+ parent.suppressLayout(true);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ parent.suppressLayout(false);
+ }
+ });
+ }
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setClipBounds(null);
+ }
+ });
+ return anim;
+ }
+ }
+ } else {
+ return (ObjectAnimator) endValues.values.get("drawableAnim");
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean prePlay(final ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return false;
+ }
+ HashMap<String, Object> startParentVals = startValues.values;
+ HashMap<String, Object> endParentVals = endValues.values;
+ ViewGroup startParent = (ViewGroup) startParentVals.get(PROPNAME_PARENT);
+ ViewGroup endParent = (ViewGroup) endParentVals.get(PROPNAME_PARENT);
+ if (startParent == null || endParent == null) {
+ return false;
+ }
+ final View view = endValues.view;
+ 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
+ // all parents in layouts have IDs to avoid layout-inflation resulting in a side-effect
+ // of reparenting the views.
+ if (!mReparent || parentsEqual) {
+ Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+ Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ int startLeft = startBounds.left;
+ int endLeft = endBounds.left;
+ int startTop = startBounds.top;
+ int endTop = endBounds.top;
+ int startRight = startBounds.right;
+ int endRight = endBounds.right;
+ int startBottom = startBounds.bottom;
+ int endBottom = endBounds.bottom;
+ int startWidth = startRight - startLeft;
+ int startHeight = startBottom - startTop;
+ int endWidth = endRight - endLeft;
+ int endHeight = endBottom - endTop;
+ int numChanges = 0;
+ if (startWidth != 0 && startHeight != 0 && endWidth != 0 && endHeight != 0) {
+ if (startLeft != endLeft) ++numChanges;
+ if (startTop != endTop) ++numChanges;
+ if (startRight != endRight) ++numChanges;
+ if (startBottom != endBottom) ++numChanges;
+ }
+ if (numChanges > 0) {
+ if (!mResizeClip) {
+ if (startLeft != endLeft) view.setLeft(startLeft);
+ if (startTop != endTop) view.setTop(startTop);
+ if (startRight != endRight) view.setRight(startRight);
+ if (startBottom != endBottom) view.setBottom(startBottom);
+ } else {
+ if (startWidth != endWidth) view.setRight(endLeft +
+ Math.max(startWidth, endWidth));
+ if (startHeight != endHeight) view.setBottom(endTop +
+ Math.max(startHeight, endHeight));
+ // TODO: don't clobber TX/TY
+ if (startLeft != endLeft) view.setTranslationX(startLeft - endLeft);
+ if (startTop != endTop) view.setTranslationY(startTop - endTop);
+ }
+ return true;
+ }
+ } else {
+ int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X);
+ int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y);
+ int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X);
+ int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y);
+ // TODO: also handle size changes: check bounds and animate size changes
+ if (startX != endX || startY != endY) {
+ sceneRoot.getLocationInWindow(tempLocation);
+ Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ view.draw(canvas);
+ final BitmapDrawable drawable = new BitmapDrawable(bitmap);
+ view.setVisibility(View.INVISIBLE);
+ sceneRoot.getOverlay().add(drawable);
+ Rect startBounds = new Rect(startX - tempLocation[0], startY - tempLocation[1],
+ startX - tempLocation[0] + view.getWidth(),
+ startY - tempLocation[1] + view.getHeight());
+ Rect endBounds = new Rect(endX - tempLocation[0], endY - tempLocation[1],
+ endX - tempLocation[0] + view.getWidth(),
+ endY - tempLocation[1] + view.getHeight());
+ ObjectAnimator anim = ObjectAnimator.ofObject(drawable, "bounds",
+ sRectEvaluator, startBounds, endBounds);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ sceneRoot.getOverlay().remove(drawable);
+ view.setVisibility(View.VISIBLE);
+ }
+ });
+ endParentVals.put("drawableAnim", anim);
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/core/java/android/view/transition/Recolor.java b/core/java/android/view/transition/Recolor.java
new file mode 100644
index 0000000..7048be9
--- /dev/null
+++ b/core/java/android/view/transition/Recolor.java
@@ -0,0 +1,118 @@
+/*
+ * 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.view.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.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.HashMap;
+
+/**
+ * This transition tracks changes during scene changes to the
+ * {@link View#setBackground(android.graphics.drawable.Drawable) background}
+ * property of its target views (when the background is a
+ * {@link ColorDrawable}, as well as the
+ * {@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.
+ */
+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());
+ }
+ }
+
+ @Override
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return false;
+ }
+ final View view = endValues.view;
+ Drawable startBackground = (Drawable) startValues.values.get(PROPNAME_BACKGROUND);
+ Drawable endBackground = (Drawable) endValues.values.get(PROPNAME_BACKGROUND);
+ boolean changed = false;
+ if (startBackground instanceof ColorDrawable && endBackground instanceof ColorDrawable) {
+ ColorDrawable startColor = (ColorDrawable) startBackground;
+ ColorDrawable endColor = (ColorDrawable) endBackground;
+ if (startColor.getColor() != endColor.getColor()) {
+ endColor.setColor(startColor.getColor());
+ changed = true;
+ }
+ }
+ if (view instanceof TextView) {
+ TextView textView = (TextView) view;
+ int start = (Integer) startValues.values.get(PROPNAME_TEXT_COLOR);
+ int end = (Integer) endValues.values.get(PROPNAME_TEXT_COLOR);
+ if (start != end) {
+ textView.setTextColor(end);
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ ObjectAnimator anim = null;
+ final View view = endValues.view;
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+ Drawable startBackground = (Drawable) startVals.get(PROPNAME_BACKGROUND);
+ Drawable endBackground = (Drawable) endVals.get(PROPNAME_BACKGROUND);
+ if (startBackground instanceof ColorDrawable && endBackground instanceof ColorDrawable) {
+ ColorDrawable startColor = (ColorDrawable) startBackground;
+ ColorDrawable endColor = (ColorDrawable) endBackground;
+ if (startColor.getColor() != endColor.getColor()) {
+ anim = ObjectAnimator.ofObject(endBackground, "color",
+ new ArgbEvaluator(), startColor.getColor(), endColor.getColor());
+ if (getStartDelay() > 0) {
+ endColor.setColor(startColor.getColor());
+ }
+ }
+ }
+ if (view instanceof TextView) {
+ TextView textView = (TextView) view;
+ int start = (Integer) startValues.values.get(PROPNAME_TEXT_COLOR);
+ int end = (Integer) endValues.values.get(PROPNAME_TEXT_COLOR);
+ if (start != end) {
+ anim = ObjectAnimator.ofObject(textView, "textColor",
+ new ArgbEvaluator(), start, end);
+ if (getStartDelay() > 0) {
+ textView.setTextColor(end);
+ }
+ }
+ }
+ return anim;
+ }
+}
diff --git a/core/java/android/view/transition/Rotate.java b/core/java/android/view/transition/Rotate.java
new file mode 100644
index 0000000..b42fbe5
--- /dev/null
+++ b/core/java/android/view/transition/Rotate.java
@@ -0,0 +1,67 @@
+/*
+ * 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.view.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * This transition captures the rotation property of targets before and after
+ * the scene change and animates any changes.
+ */
+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());
+ }
+
+ @Override
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return false;
+ }
+ final View view = endValues.view;
+ float startRotation = (Float) startValues.values.get(PROPNAME_ROTATION);
+ float endRotation = (Float) endValues.values.get(PROPNAME_ROTATION);
+ if (startRotation != endRotation) {
+ view.setRotation(startRotation);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ final View view = endValues.view;
+ float startRotation = (Float) startValues.values.get(PROPNAME_ROTATION);
+ float endRotation = (Float) endValues.values.get(PROPNAME_ROTATION);
+ if (startRotation != endRotation) {
+ return ObjectAnimator.ofFloat(view, View.ROTATION,
+ startRotation, endRotation);
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/view/transition/Scene.java b/core/java/android/view/transition/Scene.java
new file mode 100644
index 0000000..62cb9d3
--- /dev/null
+++ b/core/java/android/view/transition/Scene.java
@@ -0,0 +1,191 @@
+/*
+ * 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.view.transition;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+/**
+ * A scene represents the collection of values that various properties in the
+ * View hierarchy will have when the scene is applied. A Scene can be
+ * configured to automatically run a Transition when it is applied, which will
+ * animate the various property changes that take place during the
+ * scene change.
+ */
+public final class Scene {
+
+ private Context mContext;
+ private int mLayoutId = -1;
+ private ViewGroup mSceneRoot;
+ private ViewGroup mLayout; // alternative to layoutId
+ Runnable mEnterAction, mExitAction;
+
+ /**
+ * Constructs a Scene with no information about how values will change
+ * when this scene is applied. This constructor might be used when
+ * a Scene is created with the intention of being dynamically configured,
+ * through setting {@link #setEnterAction(Runnable)} and possibly
+ * {@link #setExitAction(Runnable)}.
+ *
+ * @param sceneRoot The root of the hierarchy in which scene changes
+ * and transitions will take place.
+ */
+ public Scene(ViewGroup sceneRoot) {
+ mSceneRoot = sceneRoot;
+ }
+
+ /**
+ * Constructs a Scene which, when entered, will remove any
+ * children from the sceneRoot container and will inflate and add
+ * the hierarchy specified by the layoutId resource file.
+ *
+ * @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
+ * hierarchy of this scene.
+ * @param context The context used in the process of inflating
+ * the layout resource.
+ */
+ public Scene(ViewGroup sceneRoot, int layoutId, Context context) {
+ mContext = context;
+ mSceneRoot = sceneRoot;
+ mLayoutId = layoutId;
+ }
+
+ /**
+ * Constructs a Scene which, when entered, will remove any
+ * children from the sceneRoot container and add the layout
+ * object as a new child of that container.
+ *
+ * @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
+ * of sceneRoot when this scene is entered.
+ */
+ public Scene(ViewGroup sceneRoot, ViewGroup layout) {
+ mSceneRoot = sceneRoot;
+ mLayout = layout;
+ }
+
+ /**
+ * Gets the root of the scene, which is the root of the view hierarchy
+ * affected by changes due to this scene, and which will be animated
+ * when this scene is entered.
+ *
+ * @return The root of the view hierarchy affected by this scene.
+ */
+ public ViewGroup getSceneRoot() {
+ return mSceneRoot;
+ }
+
+ /**
+ * 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}
+ * 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 (mExitAction != null) {
+ mExitAction.run();
+ }
+ }
+ }
+
+ /**
+ * Enters this scene, which entails changing all values that
+ * are specified by this scene. These may be values associated
+ * with a layout view group or layout resource file which will
+ * now be added to the scene root, or it may be values changed by
+ * an {@link #setEnterAction(Runnable)} enter action}, or a
+ * combination of the these. No transition will be run when the
+ * scene is entered. To get transition behavior in scene changes,
+ * use one of the methods in {@link TransitionManager} instead.
+ */
+ public void enter() {
+
+ // 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
+ getSceneRoot().removeAllViews();
+
+ if (mLayoutId >= 0) {
+ LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot);
+ } else {
+ mSceneRoot.addView(mLayout);
+ }
+ }
+
+ // Notify next scene that it is entering. Subclasses may override to configure scene.
+ if (mEnterAction != null) {
+ mEnterAction.run();
+ }
+
+ mSceneRoot.setCurrentScene(this );
+ }
+
+ /**
+ * Scenes that are not defined with layout resources or
+ * hierarchies, or which need to perform additional steps
+ * after those hierarchies are changed to, should set an enter
+ * action, and possibly an exit action as well. An enter action
+ * will cause Scene to call back into application code to do
+ * anything else the application needs after transitions have
+ * captured pre-change values and after any other scene changes
+ * have been applied, such as the layout (if any) being added to
+ * the view hierarchy. After this method is called, Transitions will
+ * be played.
+ *
+ * @param action The runnable whose {@link Runnable#run() run()} method will
+ * be called when this scene is entered
+ * @see #setExitAction(Runnable)
+ * @see Scene#Scene(ViewGroup, int, Context)
+ * @see Scene#Scene(ViewGroup, ViewGroup)
+ */
+ public void setEnterAction(Runnable action) {
+ mEnterAction = action;
+ }
+
+ /**
+ * Scenes that are not defined with layout resources or
+ * hierarchies, or which need to perform additional steps
+ * after those hierarchies are changed to, should set an enter
+ * action, and possibly an exit action as well. An exit action
+ * will cause Scene to call back into application code to do
+ * anything the application needs to do after applicable transitions have
+ * captured pre-change values, but before any other scene changes
+ * have been applied, such as the new layout (if any) being added to
+ * the view hierarchy. After this method is called, the next scene
+ * will be entered, including a call to {@link #setEnterAction(Runnable)}
+ * if an enter action is set.
+ *
+ * @see #setEnterAction(Runnable)
+ * @see Scene#Scene(ViewGroup, int, Context)
+ * @see Scene#Scene(ViewGroup, ViewGroup)
+ */
+ public void setExitAction(Runnable action) {
+ mExitAction = action;
+ }
+
+}
\ No newline at end of file
diff --git a/core/java/android/view/transition/Slide.java b/core/java/android/view/transition/Slide.java
new file mode 100644
index 0000000..8630ee2
--- /dev/null
+++ b/core/java/android/view/transition/Slide.java
@@ -0,0 +1,70 @@
+/*
+ * 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.view.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+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.
+ */
+public class Slide extends Visibility {
+
+ // TODO: Add parameter for sliding factor - it's hard-coded below
+
+ private static final TimeInterpolator sAccelerator = new AccelerateInterpolator();
+ private static final TimeInterpolator sDecelerator = new DecelerateInterpolator();
+
+ @Override
+ protected Animator appear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(endView, View.TRANSLATION_Y,
+ -2 * endView.getHeight(), 0);
+ anim.setInterpolator(sDecelerator);
+ return anim;
+ }
+
+ @Override
+ protected boolean preAppear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ endView.setTranslationY(-2 * endView.getHeight());
+ return true;
+ }
+
+ @Override
+ protected boolean preDisappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ startView.setTranslationY(0);
+ return true;
+ }
+
+ @Override
+ protected Animator disappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(startView, View.TRANSLATION_Y, 0,
+ -2 * startView.getHeight());
+ anim.setInterpolator(sAccelerator);
+ return anim;
+ }
+
+}
diff --git a/core/java/android/view/transition/TextChange.java b/core/java/android/view/transition/TextChange.java
new file mode 100644
index 0000000..0ba2412
--- /dev/null
+++ b/core/java/android/view/transition/TextChange.java
@@ -0,0 +1,90 @@
+/*
+ * 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.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.HashMap;
+
+/**
+ * This transition tracks changes to the text in TextView targets. If the text
+ * changes between the start and end scenes, the transition ensures that the
+ * 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.
+ */
+public class TextChange extends Transition {
+ private static final String PROPNAME_TEXT = "android:textchange:text";
+
+ // TODO: think about other options we could have here, like cross-fading the text, or fading
+ // it out/in. These could be parameters to supply to the constructors (and xml attributes).
+
+ @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());
+ }
+ }
+
+ @Override
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null || !(endValues.view instanceof TextView)) {
+ return false;
+ }
+ final TextView view = (TextView) endValues.view;
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+ String startText = (String) startVals.get(PROPNAME_TEXT);
+ String endText = (String) endVals.get(PROPNAME_TEXT);
+ if (!startText.equals(endText)) {
+ view.setText(startText);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null || !(endValues.view instanceof TextView)) {
+ return null;
+ }
+ final TextView view = (TextView) endValues.view;
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+ final String startText = (String) startVals.get(PROPNAME_TEXT);
+ final String endText = (String) endVals.get(PROPNAME_TEXT);
+ if (!startText.equals(endText)) {
+ // This noop animation is just used to keep the text in its start state
+ // until the transition ends
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setText(endText);
+ }
+ });
+ return anim;
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/view/transition/Transition.java b/core/java/android/view/transition/Transition.java
new file mode 100644
index 0000000..150c218
--- /dev/null
+++ b/core/java/android/view/transition/Transition.java
@@ -0,0 +1,911 @@
+/*
+ * 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.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TimeInterpolator;
+import android.util.LongSparseArray;
+import android.util.SparseArray;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOverlay;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * 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
+ * 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
+ * on View objects are of interest to it, and also knows how to animate
+ * changes to those values. For example, the {@link Fade} transition tracks
+ * changes to visibility-related properties and is able to construct and run
+ * animations that fade items in or out based on changes to those properties.
+ *
+ * <p>Note: Transitions may not work correctly with either {@link SurfaceView}
+ * or {@link TextureView}, due to the way that these views are displayed
+ * on the screen. For SurfaceView, the problem is that the view is updated from
+ * 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
+ * with TextureView because they rely on {@link ViewOverlay} functionality,
+ * which does not currently work with TextureView.</p>
+ */
+public abstract class Transition {
+
+ private static final String LOG_TAG = "Transition";
+ static final boolean DBG = false;
+
+ long mStartDelay = -1;
+ long mDuration = -1;
+ TimeInterpolator mInterpolator = null;
+ int[] mTargetIds;
+ View[] mTargets;
+ // TODO: sparse arrays instead of hashmaps?
+ private HashMap<View, TransitionValues> mStartValues =
+ new HashMap<View, TransitionValues>();
+ private SparseArray<TransitionValues> mStartIdValues = new SparseArray<TransitionValues>();
+ private LongSparseArray<TransitionValues> mStartItemIdValues =
+ new LongSparseArray<TransitionValues>();
+ private HashMap<View, TransitionValues> mEndValues =
+ new HashMap<View, TransitionValues>();
+ private SparseArray<TransitionValues> mEndIdValues = new SparseArray<TransitionValues>();
+ private LongSparseArray<TransitionValues> mEndItemIdValues =
+ new LongSparseArray<TransitionValues>();
+
+ // Used to carry data between preplay() and play(), cleared before every scene transition
+ private ArrayList<TransitionValues> mPlayStartValuesList = new ArrayList<TransitionValues>();
+ private ArrayList<TransitionValues> mPlayEndValuesList = new ArrayList<TransitionValues>();
+
+ // Number of per-target instances of this Transition currently running. This count is
+ // determined by calls to startTransition() and endTransition()
+ int mNumInstances = 0;
+
+
+ /**
+ * The set of listeners to be sent transition lifecycle events.
+ */
+ ArrayList<TransitionListener> mListeners = null;
+
+ /**
+ * 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).
+ */
+ public Transition() {}
+
+ /**
+ * Sets the duration of this transition. By default, there is no duration
+ * (indicated by a negative number), which means that the Animator created by
+ * the transition will have its own specified duration. If the duration of a
+ * Transition is set, that duration will override the Animator duration.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @return This transition object.
+ */
+ public Transition setDuration(long duration) {
+ mDuration = duration;
+ return this;
+ }
+
+ public long getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Sets the startDelay of this transition. By default, there is no delay
+ * (indicated by a negative number), which means that the Animator created by
+ * the transition will have its own specified startDelay. If the delay of a
+ * Transition is set, that delay will override the Animator delay.
+ *
+ * @param startDelay The length of the delay, in milliseconds.
+ */
+ public void setStartDelay(long startDelay) {
+ mStartDelay = startDelay;
+ }
+
+ public long getStartDelay() {
+ return mStartDelay;
+ }
+
+ /**
+ * Sets the interpolator of this transition. By default, the interpolator
+ * is null, which means that the Animator created by the transition
+ * will have its own specified interpolator. If the interpolator of a
+ * Transition is set, that interpolator will override the Animator interpolator.
+ *
+ * @param interpolator The time interpolator used by the transition
+ */
+ public void setInterpolator(TimeInterpolator interpolator) {
+ mInterpolator = interpolator;
+ }
+
+ public TimeInterpolator getInterpolator() {
+ return mInterpolator;
+ }
+
+ /**
+ * 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 run animations on its target
+ * views. The method is called for every applicable target object, which
+ * is stored in the {@link TransitionValues#view} field. When the method
+ * results in an animation needing to be run, the transition will construct
+ * the appropriate {@link Animator} object and return it. The transition
+ * mechanism will apply any applicable duration, startDelay, and interpolator
+ * to that animation and start it. Returning null from the method tells the
+ * transition engine that there is no animation to be played (TransitionGroup
+ * will return null because any applicable animations were started on its child
+ * transitions already and there is no animation to be run on the group itself).
+ *
+ * @param sceneRoot
+ * @param startValues
+ * @param endValues
+ * @return Animator The animation to run.
+ */
+ protected abstract Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues);
+
+ /**
+ * 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 things up at the start of a
+ * Transition. For example, if an overall Transition consists 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 scene for the
+ * delay between that start and the child Transition start time. For
+ * example, a transition that fades an item in may wish to set the starting
+ * alpha value to 0, to avoid it blinking in prior to the transition
+ * 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 in the preplay() method.
+ *
+ * <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>false</code> indicates that
+ * the transition should not run, and there will be no ensuing call to the
+ * {@link #play(ViewGroup, TransitionValues, TransitionValues)} method during
+ * this scene change. The default implementation returns true.</p>
+ *
+ * <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 True if the Transition's {@link #play(ViewGroup,
+ * TransitionValues, TransitionValues) play()} method should be called
+ * during this scene change, false otherwise.
+ */
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ return true;
+ }
+
+ /**
+ * This version of prePlay() is called with the entire set of start/end
+ * values. The implementation in Transition iterates through these lists
+ * and calls {@link #prePlay(ViewGroup, TransitionValues, TransitionValues)}
+ * with each set of start/end values on this transition. The
+ * TransitionGroup subclass overrides this method and delegates it to
+ * each of its children in succession. The intention in splitting
+ * preplay() out from play() is to allow all Transitions in the tree to
+ * set up the appropriate start scene for their target objects prior to
+ * any calls to play(), which is necessary when there is a sequential
+ * Transition, where a child transition which is not the first may want to
+ * set up a target's scene prior to the overall Transition start.
+ *
+ * @hide
+ */
+ protected void prePlay(ViewGroup sceneRoot, HashMap<View, TransitionValues> startValues,
+ SparseArray<TransitionValues> startIdValues,
+ LongSparseArray<TransitionValues> startItemIdValues,
+ HashMap<View, TransitionValues> endValues,
+ SparseArray<TransitionValues> endIdValues,
+ LongSparseArray<TransitionValues> endItemIdValues) {
+ mPlayStartValuesList.clear();
+ mPlayEndValuesList.clear();
+ HashMap<View, TransitionValues> endCopy = new HashMap<View, TransitionValues>(endValues);
+ SparseArray<TransitionValues> endIdCopy =
+ new SparseArray<TransitionValues>(endIdValues.size());
+ for (int i = 0; i < endIdValues.size(); ++i) {
+ int id = endIdValues.keyAt(i);
+ endIdCopy.put(id, endIdValues.valueAt(i));
+ }
+ LongSparseArray<TransitionValues> endItemIdCopy =
+ new LongSparseArray<TransitionValues>(endItemIdValues.size());
+ for (int i = 0; i < endItemIdValues.size(); ++i) {
+ long id = endItemIdValues.keyAt(i);
+ endItemIdCopy.put(id, endItemIdValues.valueAt(i));
+ }
+ // Walk through the start values, playing everything we find
+ // Remove from the end set as we go
+ ArrayList<TransitionValues> startValuesList = new ArrayList<TransitionValues>();
+ ArrayList<TransitionValues> endValuesList = new ArrayList<TransitionValues>();
+ for (View view : startValues.keySet()) {
+ TransitionValues start = null;
+ TransitionValues end = null;
+ boolean isInListView = false;
+ if (view.getParent() instanceof ListView) {
+ isInListView = true;
+ }
+ if (!isInListView) {
+ int id = view.getId();
+ start = startValues.get(view) != null ?
+ startValues.get(view) : startIdValues.get(id);
+ if (endValues.get(view) != null) {
+ end = endValues.get(view);
+ endCopy.remove(view);
+ } else {
+ end = endIdValues.get(id);
+ View removeView = null;
+ for (View viewToRemove : endCopy.keySet()) {
+ if (viewToRemove.getId() == id) {
+ removeView = viewToRemove;
+ }
+ }
+ if (removeView != null) {
+ endCopy.remove(removeView);
+ }
+ }
+ endIdCopy.remove(id);
+ if (isValidTarget(view, id)) {
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ } else {
+ ListView parent = (ListView) view.getParent();
+ if (parent.getAdapter().hasStableIds()) {
+ int position = parent.getPositionForView(view);
+ long itemId = parent.getItemIdAtPosition(position);
+ start = startItemIdValues.get(itemId);
+ endItemIdCopy.remove(itemId);
+ // TODO: deal with targetIDs for itemIDs for ListView items
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ }
+ }
+ int startItemIdCopySize = startItemIdValues.size();
+ for (int i = 0; i < startItemIdCopySize; ++i) {
+ long id = startItemIdValues.keyAt(i);
+ if (isValidTarget(null, id)) {
+ TransitionValues start = startItemIdValues.get(id);
+ TransitionValues end = endItemIdValues.get(id);
+ endItemIdCopy.remove(id);
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ }
+ // Now walk through the remains of the end set
+ for (View view : endCopy.keySet()) {
+ int id = view.getId();
+ if (isValidTarget(view, id)) {
+ TransitionValues start = startValues.get(view) != null ?
+ startValues.get(view) : startIdValues.get(id);
+ TransitionValues end = endCopy.get(view);
+ endIdCopy.remove(id);
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ }
+ int endIdCopySize = endIdCopy.size();
+ for (int i = 0; i < endIdCopySize; ++i) {
+ int id = endIdCopy.keyAt(i);
+ if (isValidTarget(null, id)) {
+ TransitionValues start = startIdValues.get(id);
+ TransitionValues end = endIdCopy.get(id);
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ }
+ int endItemIdCopySize = endItemIdCopy.size();
+ for (int i = 0; i < endItemIdCopySize; ++i) {
+ long id = endItemIdCopy.keyAt(i);
+ // TODO: Deal with targetIDs and itemIDs
+ TransitionValues start = startItemIdValues.get(id);
+ TransitionValues end = endItemIdCopy.get(id);
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ for (int i = 0; i < startValuesList.size(); ++i) {
+ TransitionValues start = startValuesList.get(i);
+ TransitionValues end = endValuesList.get(i);
+ // TODO: what to do about targetIds and itemIds?
+ if (prePlay(sceneRoot, start, end)) {
+ // Note: we've already done the check against targetIDs in these lists
+ mPlayStartValuesList.add(start);
+ mPlayEndValuesList.add(end);
+ }
+ }
+ }
+
+ /**
+ * Internal utility method for checking whether a given view/id
+ * is valid for this transition, where "valid" means that either
+ * the Transition has no target/targetId list (the default, in which
+ * cause the transition should act on all views in the hiearchy), or
+ * the given view is in the target list or the view id is in the
+ * targetId list. If the target parameter is null, then the target list
+ * is not checked (this is in the case of ListView items, where the
+ * views are ignored and only the ids are used).
+ */
+ boolean isValidTarget(View target, long targetId) {
+ if (mTargetIds == null && mTargets == null) {
+ return true;
+ }
+ if (mTargetIds != null) {
+ for (int i = 0; i < mTargetIds.length; ++i) {
+ if (mTargetIds[i] == targetId) {
+ return true;
+ }
+ }
+ }
+ if (target != null && mTargets != null) {
+ for (int i = 0; i < mTargets.length; ++i) {
+ if (mTargets[i] == target) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This version of play() 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)}
+ * with each set of start/end values on this transition. The
+ * TransitionGroup subclass overrides this method and delegates it to
+ * each of its children in succession.
+ *
+ * @hide
+ */
+ protected void play(ViewGroup sceneRoot,
+ final HashMap<View, TransitionValues> startValues,
+ final SparseArray<TransitionValues> startIdValues,
+ final LongSparseArray<TransitionValues> startItemIdValues,
+ final HashMap<View, TransitionValues> endValues,
+ final SparseArray<TransitionValues> endIdValues,
+ final LongSparseArray<TransitionValues> endItemIdValues) {
+
+ startTransition();
+ // Now walk the list of TransitionValues, calling play for each pair
+ for (int i = 0; i < mPlayStartValuesList.size(); ++i) {
+ TransitionValues start = mPlayStartValuesList.get(i);
+ TransitionValues end = mPlayEndValuesList.get(i);
+ startTransition();
+ animate(play(sceneRoot, start, end));
+ }
+ mPlayStartValuesList.clear();
+ mPlayEndValuesList.clear();
+ endTransition();
+ }
+
+ /**
+ * 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
+ * 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 preplay and play() methods to determine what, if any,
+ * animations, should be run.
+ *
+ * @param transitionValues The holder any values that the Transition
+ * wishes to store. Values are stored in the fields of this
+ * TransitionValues object, according to their type, and are keyed from
+ * a String value. For example, to start a view's rotation value,
+ * a Transition might call
+ * <code>transitionValues.floatValues.put("rotation", view.getRotation())
+ * </code>. The target <code>View</code> will already be stored in
+ * the transitionValues structure when this method is called. The other
+ * fields in TransitionValues, e.g. <code>floatValues</code>,
+ * may need to be instantiated if they have not yet been created.
+ */
+ protected abstract void captureValues(TransitionValues transitionValues, boolean start);
+
+ /**
+ * Sets the ids of target views 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
+ * the Transition to only listen for, and act on, views with these IDs.
+ * Views with different IDs, or no IDs whatsoever, will be ignored.
+ *
+ * @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.
+ * Returning the same object makes it easier to chain calls during
+ * construction, such as
+ * <code>transitionGroup.addTransitions(new Fade()).setTargetIds(someId);</code>
+ */
+ public Transition setTargetIds(int... targetIds) {
+ int numTargets = targetIds.length;
+ mTargetIds = new int[numTargets];
+ System.arraycopy(targetIds, 0, mTargetIds, 0, numTargets);
+ return this;
+ }
+
+ /**
+ * Sets the target view instances that this Transition is interested in
+ * animating. By default, there are no targets, and a Transition will
+ * listen for changes on every view in the hierarchy below the sceneRoot
+ * of the Scene being transitioned into. Setting targets constrains
+ * 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}
+ * 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
+ * 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
+ * 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.
+ * Returning the same object makes it easier to chain calls during
+ * construction, such as
+ * <code>transitionGroup.addTransitions(new Fade()).setTargets(someView);</code>
+ */
+ public Transition setTargets(View... targets) {
+ int numTargets = targets.length;
+ mTargets = new View[numTargets];
+ System.arraycopy(targets, 0, mTargets, 0, numTargets);
+ return this;
+ }
+
+ /**
+ * Returns the array of target IDs that this transition limits itself to
+ * tracking and animating. If the array is null for both this method and
+ * {@link #getTargets()}, then this transition is
+ * not limited to specific views, and will handle changes to any views
+ * in the hierarchy of a scene change.
+ *
+ * @return the list of target IDs
+ */
+ public int[] getTargetIds() {
+ return mTargetIds;
+ }
+
+ /**
+ * Returns the array of target views that this transition limits itself to
+ * tracking and animating. If the array is null for both this method and
+ * {@link #getTargetIds()}, then this transition is
+ * not limited to specific views, and will handle changes to any views
+ * in the hierarchy of a scene change.
+ *
+ * @return the list of target views
+ */
+ public View[] getTargets() {
+ return mTargets;
+ }
+
+ /**
+ * Recursive method that captures values for the given view and the
+ * hierarchy underneath it.
+ * @param sceneRoot The root of the view hierarchy being captured
+ * @param start true if this capture is happening before the scene change,
+ * false otherwise
+ */
+ void captureValues(ViewGroup sceneRoot, boolean start) {
+ 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];
+ View view = sceneRoot.findViewById(id);
+ if (view != null) {
+ TransitionValues values = new TransitionValues();
+ values.view = view;
+ captureValues(values, start);
+ if (start) {
+ mStartValues.put(view, values);
+ mStartIdValues.put(id, values);
+ } else {
+ mEndValues.put(view, values);
+ mEndIdValues.put(id, values);
+ }
+ }
+ }
+ }
+ if (mTargets != null) {
+ for (int i = 0; i < mTargets.length; ++i) {
+ View view = mTargets[i];
+ if (view != null) {
+ TransitionValues values = new TransitionValues();
+ values.view = view;
+ captureValues(values, start);
+ if (start) {
+ mStartValues.put(view, values);
+ } else {
+ mEndValues.put(view, values);
+ }
+ }
+ }
+ }
+ } else {
+ captureHierarchy(sceneRoot, start);
+ }
+ }
+
+ /**
+ * Recursive method which captures values for an entire view hierarchy,
+ * starting at some root view. Transitions without targetIDs will use this
+ * method to capture values for all possible views.
+ *
+ * @param view The view for which to capture values. Children of this View
+ * will also be captured, recursively down to the leaf nodes.
+ * @param start true if values are being captured in the start scene, false
+ * otherwise.
+ */
+ private void captureHierarchy(View view, boolean start) {
+ if (view == null) {
+ return;
+ }
+ boolean isListViewItem = false;
+ if (view.getParent() instanceof ListView) {
+ isListViewItem = true;
+ }
+ if (isListViewItem && !((ListView) view.getParent()).getAdapter().hasStableIds()) {
+ // ignore listview children unless we can track them with stable IDs
+ return;
+ }
+ long id;
+ if (!isListViewItem) {
+ id = view.getId();
+ } else {
+ ListView listview = (ListView) view.getParent();
+ int position = listview.getPositionForView(view);
+ id = listview.getItemIdAtPosition(position);
+ view.setHasTransientState(true);
+ }
+ TransitionValues values = new TransitionValues();
+ values.view = view;
+ captureValues(values, start);
+ if (start) {
+ if (!isListViewItem) {
+ mStartValues.put(view, values);
+ mStartIdValues.put((int) id, values);
+ } else {
+ mStartItemIdValues.put(id, values);
+ }
+ } else {
+ if (!isListViewItem) {
+ mEndValues.put(view, values);
+ mEndIdValues.put((int) id, values);
+ } else {
+ mEndItemIdValues.put(id, values);
+ }
+ }
+ if (view instanceof ViewGroup) {
+ ViewGroup parent = (ViewGroup) view;
+ for (int i = 0; i < parent.getChildCount(); ++i) {
+ captureHierarchy(parent.getChildAt(i), start);
+ }
+ }
+ }
+
+ /**
+ * Called by TransitionManager to play the transition. This calls
+ * prePlay() and then play() with the full set of per-view
+ * transitionValues objects
+ */
+ void play(ViewGroup sceneRoot) {
+ // prePlay() 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
+ prePlay(sceneRoot, mStartValues, mStartIdValues, mStartItemIdValues,
+ mEndValues, mEndIdValues, mEndItemIdValues);
+ play(sceneRoot, mStartValues, mStartIdValues, mStartItemIdValues,
+ mEndValues, mEndIdValues, mEndItemIdValues);
+ }
+
+ /**
+ * This is a utility method used by subclasses to handle standard parts of
+ * setting up and running an Animator: it sets the {@link #getDuration()
+ * duration} and the {@link #getStartDelay() startDelay}, starts the
+ * animation, and, when the animator ends, calls {@link #endTransition()}.
+ *
+ * @param animator The Animator to be run during this transition.
+ *
+ * @hide
+ */
+ protected void animate(Animator animator) {
+ // TODO: maybe pass auto-end as a boolean parameter?
+ if (animator == null) {
+ endTransition();
+ } else {
+ if (getDuration() >= 0) {
+ animator.setDuration(getDuration());
+ }
+ if (getStartDelay() >= 0) {
+ animator.setStartDelay(getStartDelay());
+ }
+ if (getInterpolator() != null) {
+ animator.setInterpolator(getInterpolator());
+ }
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ cancelTransition();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endTransition();
+ animation.removeListener(this);
+ }
+ });
+ animator.start();
+ }
+ }
+
+ /**
+ * Subclasses may override to receive notice of when the transition starts.
+ * This is equivalent to listening for the
+ * {@link TransitionListener#onTransitionStart(Transition)} callback.
+ */
+ protected void onTransitionStart() {
+ }
+
+ /**
+ * Subclasses may override to receive notice of when the transition is
+ * canceled. This is equivalent to listening for the
+ * {@link TransitionListener#onTransitionCancel(Transition)} callback.
+ */
+ protected void onTransitionCancel() {
+ }
+
+ /**
+ * Subclasses may override to receive notice of when the transition ends.
+ * This is equivalent to listening for the
+ * {@link TransitionListener#onTransitionEnd(Transition)} callback.
+ */
+ protected void onTransitionEnd() {
+ }
+
+ /**
+ * This method is called automatically by the transition and
+ * TransitionGroup classes prior to a Transition subclass starting;
+ * subclasses should not need to call it directly.
+ *
+ * @hide
+ */
+ protected void startTransition() {
+ if (mNumInstances == 0) {
+ onTransitionStart();
+ if (mListeners != null && mListeners.size() > 0) {
+ ArrayList<TransitionListener> tmpListeners =
+ (ArrayList<TransitionListener>) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onTransitionStart(this);
+ }
+ }
+ }
+ mNumInstances++;
+ }
+
+ /**
+ * This method is called automatically by the Transition and
+ * TransitionGroup classes when a transition finishes, either because
+ * a transition did nothing (returned a null Animator from
+ * {@link Transition#play(ViewGroup, TransitionValues,
+ * TransitionValues)}) or because the transition returned a valid
+ * Animator and endTransition() was called in the onAnimationEnd()
+ * callback of the AnimatorListener.
+ *
+ * @hide
+ */
+ protected void endTransition() {
+ --mNumInstances;
+ if (mNumInstances == 0) {
+ onTransitionEnd();
+ if (mListeners != null && mListeners.size() > 0) {
+ ArrayList<TransitionListener> tmpListeners =
+ (ArrayList<TransitionListener>) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onTransitionEnd(this);
+ }
+ }
+ for (int i = 0; i < mStartItemIdValues.size(); ++i) {
+ TransitionValues tv = mStartItemIdValues.valueAt(i);
+ View v = tv.view;
+ if (v.hasTransientState()) {
+ v.setHasTransientState(false);
+ }
+ }
+ for (int i = 0; i < mEndItemIdValues.size(); ++i) {
+ TransitionValues tv = mEndItemIdValues.valueAt(i);
+ View v = tv.view;
+ if (v.hasTransientState()) {
+ v.setHasTransientState(false);
+ }
+ }
+ mStartValues.clear();
+ mStartIdValues.clear();
+ mStartItemIdValues.clear();
+ mEndValues.clear();
+ mEndIdValues.clear();
+ mEndItemIdValues.clear();
+ }
+ }
+
+ /**
+ * This method cancels a transition that is currently running.
+ * Implementation TBD.
+ */
+ protected void cancelTransition() {
+ // TODO: how does this work with instances?
+ // TODO: this doesn't actually do *anything* yet
+ onTransitionCancel();
+ if (mListeners != null && mListeners.size() > 0) {
+ ArrayList<TransitionListener> tmpListeners =
+ (ArrayList<TransitionListener>) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onTransitionCancel(this);
+ }
+ }
+ }
+
+ /**
+ * Adds a listener to the set of listeners that are sent events through the
+ * life of an animation, such as start, repeat, and end.
+ *
+ * @param listener the listener to be added to the current set of listeners
+ * for this animation.
+ */
+ public void addListener(TransitionListener listener) {
+ if (mListeners == null) {
+ mListeners = new ArrayList<TransitionListener>();
+ }
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener from the set listening to this animation.
+ *
+ * @param listener the listener to be removed from the current set of
+ * listeners for this transition.
+ */
+ public void removeListener(TransitionListener listener) {
+ if (mListeners == null) {
+ return;
+ }
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ mListeners = null;
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ @Override
+ public String toString() {
+ return toString("");
+ }
+
+ String toString(String indent) {
+ String result = indent + getClass().getSimpleName() + "@" +
+ Integer.toHexString(hashCode()) + ": ";
+ result += "dur(" + mDuration + ") ";
+ result += "dly(" + mStartDelay + ") ";
+ result += "interp(" + mInterpolator + ") ";
+ result += "tgts(";
+ if (mTargetIds != null) {
+ for (int i = 0; i < mTargetIds.length; ++i) {
+ if (i > 0) {
+ result += ", ";
+ }
+ result += mTargetIds[i];
+ }
+ }
+ if (mTargets != null) {
+ for (int i = 0; i < mTargets.length; ++i) {
+ if (i > 0) {
+ result += ", ";
+ }
+ result += mTargets[i];
+ }
+ }
+ result += ")";
+ return result;
+ }
+
+ /**
+ * A transition listener receives notifications from a transition.
+ * Notifications indicate transition lifecycle events: when the transition
+ * begins, ends, or is canceled.
+ */
+ public static interface TransitionListener {
+ /**
+ * Notification about the start of the transition.
+ *
+ * @param transition The started transition.
+ */
+ void onTransitionStart(Transition transition);
+
+ /**
+ * Notification about the end of the transition. Canceled transitions
+ * will always notify listeners of both the cancellation and end
+ * events. That is, {@link #onTransitionEnd()} is always called,
+ * regardless of whether the transition was canceled or played
+ * through to completion.
+ *
+ * @param transition The transition which reached its end.
+ */
+ void onTransitionEnd(Transition transition);
+
+ /**
+ * Notification about the cancellation of the transition.
+ *
+ * @param transition The transition which was canceled.
+ */
+ void onTransitionCancel(Transition transition);
+ }
+
+ /**
+ * Utility adapter class to avoid having to override all three methods
+ * whenever someone just wants to listen for a single event.
+ *
+ * @hide
+ * */
+ public static class TransitionListenerAdapter implements TransitionListener {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ }
+
+ @Override
+ public void onTransitionCancel(Transition transition) {
+ }
+ }
+
+}
diff --git a/core/java/android/view/transition/TransitionGroup.java b/core/java/android/view/transition/TransitionGroup.java
new file mode 100644
index 0000000..363872a
--- /dev/null
+++ b/core/java/android/view/transition/TransitionGroup.java
@@ -0,0 +1,292 @@
+/*
+ * 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.view.transition;
+
+import android.animation.Animator;
+import android.util.AndroidRuntimeException;
+import android.util.LongSparseArray;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * 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.
+ */
+public class TransitionGroup extends Transition {
+
+ ArrayList<Transition> mTransitions = new ArrayList<Transition>();
+ private boolean mPlayTogether = true;
+ int mCurrentListeners;
+ boolean mStarted = false;
+
+ /**
+ * A flag used to indicate that the child transitions of this group
+ * should all start at the same time.
+ */
+ public static final int TOGETHER = 0;
+ /**
+ * A flag used to indicate that the child transitions of this group 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;
+
+ /**
+ * 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}.
+ */
+ public TransitionGroup() {
+ }
+
+ /**
+ * Constructs an empty transition group with the specified ordering.
+ *
+ * @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
+ * transitions in sequence.
+ */
+ public void setOrdering(int ordering) {
+ switch (ordering) {
+ case SEQUENTIALLY:
+ mPlayTogether = false;
+ break;
+ case TOGETHER:
+ mPlayTogether = true;
+ break;
+ default:
+ throw new AndroidRuntimeException("Invalid parameter for TransitionGroup " +
+ "ordering: " + ordering);
+ }
+ }
+
+ /**
+ * Adds child transitions to this group. The order of the child transitions
+ * passed in determines the order in which they are started.
+ *
+ * @param transitions A list of child transition to be added to this group.
+ */
+ public void addTransitions(Transition... transitions) {
+ if (transitions != null) {
+ int numTransitions = transitions.length;
+ for (int i = 0; i < numTransitions; ++i) {
+ mTransitions.add(transitions[i]);
+ }
+ }
+ }
+
+ /**
+ * Removes the specified child transition from this group.
+ *
+ * @param transition The transition to be removed.
+ */
+ public void removeTransition(Transition transition) {
+ mTransitions.remove(transition);
+ }
+
+ /**
+ * Sets up listeners for each of the child transitions. This is used to
+ * determine when this transition group is finished (all child transitions
+ * must finish first).
+ */
+ private void setupStartEndListeners() {
+ for (Transition childTransition : mTransitions) {
+ childTransition.addListener(mListener);
+ }
+ mCurrentListeners = mTransitions.size();
+ }
+
+ /**
+ * This listener is used to detect when all child transitions are done, at
+ * which point this transition group is also done.
+ */
+ private TransitionListener mListener = new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ if (!mStarted) {
+ startTransition();
+ mStarted = true;
+ }
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ --mCurrentListeners;
+ if (mCurrentListeners == 0) {
+ // All child trans
+ mStarted = false;
+ endTransition();
+ }
+ transition.removeListener(this);
+ }
+ };
+
+ /**
+ * @hide
+ */
+ @Override
+ protected void prePlay(ViewGroup sceneRoot,
+ HashMap<View, TransitionValues> startValues,
+ SparseArray<TransitionValues> startIdValues,
+ LongSparseArray<TransitionValues> startItemIdValues,
+ HashMap<View, TransitionValues> endValues,
+ SparseArray<TransitionValues> endIdValues,
+ LongSparseArray<TransitionValues> endItemIdValues) {
+ for (Transition childTransition : mTransitions) {
+ childTransition.prePlay(sceneRoot, startValues, startIdValues, startItemIdValues,
+ endValues, endIdValues, endItemIdValues);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ protected void play(ViewGroup sceneRoot,
+ final HashMap<View, TransitionValues> startValues,
+ final SparseArray<TransitionValues> startIdValues,
+ final LongSparseArray<TransitionValues> startItemIdValues,
+ final HashMap<View, TransitionValues> endValues,
+ final SparseArray<TransitionValues> endIdValues,
+ final LongSparseArray<TransitionValues> endItemIdValues) {
+ setupStartEndListeners();
+ final ViewGroup finalSceneRoot = sceneRoot;
+ if (!mPlayTogether) {
+ // Setup sequence with listeners
+ // TODO: Need to add listeners in such a way that we can remove them later if canceled
+ for (int i = 1; i < mTransitions.size(); ++i) {
+ Transition previousTransition = mTransitions.get(i - 1);
+ final Transition nextTransition = mTransitions.get(i);
+ previousTransition.addListener(new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ nextTransition.play(finalSceneRoot,
+ startValues, startIdValues, startItemIdValues,
+ endValues, endIdValues, endItemIdValues);
+ transition.removeListener(this);
+ }
+ });
+ }
+ Transition firstTransition = mTransitions.get(0);
+ if (firstTransition != null) {
+ firstTransition.play(finalSceneRoot, startValues, startIdValues, startItemIdValues,
+ endValues, endIdValues, endItemIdValues);
+ }
+ } else {
+ for (Transition childTransition : mTransitions) {
+ childTransition.play(finalSceneRoot, startValues, startIdValues, startItemIdValues,
+ endValues, endIdValues, endItemIdValues);
+ }
+ }
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot,
+ TransitionValues startValues, TransitionValues endValues) {
+ final View view = (endValues != null) ? endValues.view :
+ (startValues != null) ? startValues.view : null;
+ final int targetId = (view != null) ? view.getId() : -1;
+ // TODO: not sure this is a valid check - what about auto-targets? No need for ids.
+ if (targetId < 0) {
+ return null;
+ }
+ setupStartEndListeners();
+ if (!mPlayTogether) {
+ final ViewGroup finalSceneRoot = sceneRoot;
+ final TransitionValues finalStartValues = startValues;
+ final TransitionValues finalEndValues = endValues;
+ // Setup sequence with listeners
+ // TODO: Need to add listeners in such a way that we can remove them later if canceled
+ for (int i = 1; i < mTransitions.size(); ++i) {
+ Transition previousTransition = mTransitions.get(i - 1);
+ final Transition nextTransition = mTransitions.get(i);
+ previousTransition.addListener(new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ nextTransition.startTransition();
+ if (nextTransition.isValidTarget(view, targetId)) {
+ animate(nextTransition.play(finalSceneRoot, finalStartValues,
+ finalEndValues));
+ } else {
+ nextTransition.endTransition();
+ }
+ }
+ });
+ }
+ Transition firstTransition = mTransitions.get(0);
+ if (firstTransition != null) {
+ firstTransition.startTransition();
+ if (firstTransition.isValidTarget(view, targetId)) {
+ animate(firstTransition.play(finalSceneRoot, finalStartValues, finalEndValues));
+ } else {
+ firstTransition.endTransition();
+ }
+ }
+ } else {
+ for (Transition childTransition : mTransitions) {
+ childTransition.startTransition();
+ if (childTransition.isValidTarget(view, targetId)) {
+ animate(childTransition.play(sceneRoot, startValues, endValues));
+ } else {
+ childTransition.endTransition();
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void captureValues(TransitionValues transitionValues, boolean start) {
+ int targetId = transitionValues.view.getId();
+ for (Transition childTransition : mTransitions) {
+ if (childTransition.isValidTarget(transitionValues.view, targetId)) {
+ childTransition.captureValues(transitionValues, start);
+ }
+ }
+ }
+
+ @Override
+ String toString(String indent) {
+ String result = super.toString(indent);
+ for (int i = 0; i < mTransitions.size(); ++i) {
+ result += "\n" + mTransitions.get(i).toString(indent + " ");
+ }
+ return result;
+ }
+
+}
diff --git a/core/java/android/view/transition/TransitionInflater.java b/core/java/android/view/transition/TransitionInflater.java
new file mode 100644
index 0000000..a5f5836
--- /dev/null
+++ b/core/java/android/view/transition/TransitionInflater.java
@@ -0,0 +1,392 @@
+/*
+ * 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.view.transition;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.util.Xml;
+import android.view.InflateException;
+import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This class inflates scenes and transitions from resource files.
+ */
+public class TransitionInflater {
+
+ // We only need one inflater for any given context. Also, this allows us to associate
+ // ids with unique instances per-Context, used to avoid re-inflating
+ // already-inflated resources into new/different instances
+ private static final HashMap<Context, TransitionInflater> sInflaterMap =
+ new HashMap<Context, TransitionInflater>();
+
+ private Context mContext;
+ // TODO: do we need id maps for transitions and transitionMgrs as well?
+ SparseArray<Scene> mScenes = new SparseArray<Scene>();
+
+ private TransitionInflater(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Obtains the TransitionInflater from the given context.
+ */
+ public static TransitionInflater from(Context context) {
+ TransitionInflater inflater = sInflaterMap.get(context);
+ if (inflater != null) {
+ return inflater;
+ }
+ inflater = new TransitionInflater(context);
+ sInflaterMap.put(context, inflater);
+ return inflater;
+ }
+
+ /**
+ * Loads a {@link Transition} object from a resource
+ *
+ * @param resource The resource id of the transition to load
+ * @return The loaded Transition object
+ * @throws android.content.res.Resources.NotFoundException when the
+ * transition cannot be loaded
+ */
+ public Transition inflateTransition(int resource) {
+ XmlResourceParser parser = mContext.getResources().getXml(resource);
+ try {
+ return createTransitionFromXml(parser, Xml.asAttributeSet(parser), null);
+ } 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();
+ }
+ }
+
+ /**
+ * Loads a {@link TransitionManager} object from a resource
+ *
+ *
+ *
+ * @param resource The resource id of the transition manager to load
+ * @return The loaded TransitionManager object
+ * @throws android.content.res.Resources.NotFoundException when the
+ * transition manager cannot be loaded
+ */
+ public TransitionManager inflateTransitionManager(int resource, ViewGroup sceneRoot) {
+ XmlResourceParser parser = mContext.getResources().getXml(resource);
+ try {
+ return createTransitionManagerFromXml(parser, Xml.asAttributeSet(parser), sceneRoot);
+ } 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();
+ }
+ }
+
+ /**
+ * 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)
+ throws XmlPullParserException, IOException {
+
+ Transition transition = 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) {
+
+ boolean newTransition = false;
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+ if ("fade".equals(name)) {
+ transition = new Fade();
+ newTransition = true;
+ } else if ("move".equals(name)) {
+ transition = new Move();
+ newTransition = true;
+ } else if ("slide".equals(name)) {
+ transition = new Slide();
+ newTransition = true;
+ } else if ("autoTransition".equals(name)) {
+ transition = new AutoTransition();
+ newTransition = true;
+ } else if ("recolor".equals(name)) {
+ transition = new Recolor();
+ newTransition = true;
+ } else if ("transitionGroup".equals(name)) {
+ transition = new TransitionGroup();
+ createTransitionFromXml(parser, attrs, ((TransitionGroup) transition));
+ 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);
+ }
+ }
+ if (transition != null || "targets".equals(name)) {
+ if (newTransition) {
+ loadTransition(transition, attrs);
+ if (transitionGroup != null) {
+ transitionGroup.addTransitions(transition);
+ }
+ }
+ } else {
+ throw new RuntimeException("Unknown scene name: " + parser.getName());
+ }
+ }
+
+ return transition;
+ }
+
+ private void getTargetIDs(XmlPullParser parser,
+ AttributeSet attrs, Transition transition) throws XmlPullParserException, IOException {
+
+ // Make sure we are on a start tag.
+ int type;
+ int depth = parser.getDepth();
+
+ ArrayList<Integer> targetIds = new ArrayList<Integer>();
+ while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ 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);
+ if (id >= 0) {
+ targetIds.add(id);
+ }
+ } else {
+ throw new RuntimeException("Unknown scene name: " + parser.getName());
+ }
+ }
+ 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);
+ }
+ transition.setTargetIds(targetsArray);
+ }
+ }
+
+ private Transition loadTransition(Transition transition, AttributeSet attrs)
+ throws Resources.NotFoundException {
+
+ TypedArray a =
+ mContext.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Transition);
+ long duration = a.getInt(com.android.internal.R.styleable.Transition_duration, -1);
+ if (duration >= 0) {
+ transition.setDuration(duration);
+ }
+ long startOffset = a.getInt(com.android.internal.R.styleable.Transition_startOffset, -1);
+ if (startOffset > 0) {
+ transition.setStartDelay(startOffset);
+ }
+ final int resID =
+ a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
+ if (resID > 0) {
+ transition.setInterpolator(AnimationUtils.loadInterpolator(mContext, resID));
+ }
+ a.recycle();
+ return transition;
+ }
+
+ //
+ // TransitionManager loading
+ //
+
+ private TransitionManager createTransitionManagerFromXml(XmlPullParser parser,
+ AttributeSet attrs, ViewGroup sceneRoot) throws XmlPullParserException, IOException {
+
+ // Make sure we are on a start tag.
+ int type;
+ int depth = parser.getDepth();
+ TransitionManager transitionManager = null;
+
+ 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("transitionManager")) {
+ transitionManager = new TransitionManager();
+ } else if (name.equals("transition") && (transitionManager != null)) {
+ loadTransition(attrs, sceneRoot, transitionManager);
+ } else {
+ throw new RuntimeException("Unknown scene name: " + parser.getName());
+ }
+ }
+ return transitionManager;
+ }
+
+ private void loadTransition(AttributeSet attrs, ViewGroup sceneRoot,
+ TransitionManager transitionManager)
+ throws Resources.NotFoundException {
+
+ TypedArray a = mContext.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.TransitionManager);
+ int transitionId = attrs.getAttributeResourceValue(
+ com.android.internal.R.styleable.TransitionManager_transition, -1);
+ Scene fromScene = null, toScene = null;
+ int fromId = attrs.getAttributeResourceValue(
+ com.android.internal.R.styleable.TransitionManager_fromScene, -1);
+ if (fromId >= 0) fromScene = inflateScene(fromId, sceneRoot);
+ int toId = attrs.getAttributeResourceValue(
+ com.android.internal.R.styleable.TransitionManager_toScene, -1);
+ if (toId >= 0) toScene = inflateScene(toId, sceneRoot);
+ if (transitionId >= 0) {
+ Transition transition = inflateTransition(transitionId);
+ if (transition != null) {
+ if (fromScene != null) {
+ if (toScene == null){
+ throw new RuntimeException("No matching toScene for given fromScene " +
+ "for transition ID " + transitionId);
+ } else {
+ transitionManager.setTransition(fromScene, toScene, transition);
+ }
+ } else if (toId >= 0) {
+ transitionManager.setTransition(toScene, transition);
+ }
+ }
+ }
+ 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/view/transition/TransitionManager.java
new file mode 100644
index 0000000..5a1991c
--- /dev/null
+++ b/core/java/android/view/transition/TransitionManager.java
@@ -0,0 +1,261 @@
+/*
+ * 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.view.transition;
+
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import java.util.HashMap;
+
+/**
+ * This class manages the set of transitions that fire when there is a
+ * change of {@link Scene}. To use the manager, add scenes along with
+ * transition objects with calls to {@link #setTransition(Scene, Transition)}
+ * or {@link #setTransition(Scene, Scene, Transition)}. Setting specific
+ * transitions for scene changes is not required; by default, a Scene change
+ * will use {@link AutoTransition} to do something reasonable for most
+ * situations. Specifying other transitions for particular scene changes is
+ * only necessary if the application wants different transition behavior
+ * in these situations.
+ */
+public class TransitionManager {
+ // TODO: how to handle enter/exit?
+
+ private static final Transition sDefaultTransition = new AutoTransition();
+ private Transition mDefaultTransition = new AutoTransition();
+
+ HashMap<Scene, Transition> mSceneTransitions = new HashMap<Scene, Transition>();
+ HashMap<Scene, HashMap<Scene, Transition>> mScenePairTransitions =
+ new HashMap<Scene, HashMap<Scene, Transition>>();
+
+ /**
+ * Sets the transition to be used for any scene change for which no
+ * other transition is explicitly set. The initial value is
+ * an {@link AutoTransition} instance.
+ *
+ * @param transition The default transition to be used for scene changes.
+ */
+ public void setDefaultTransition(Transition transition) {
+ mDefaultTransition = transition;
+ }
+
+ /**
+ * Gets the current default transition. The initial value is an {@link
+ * AutoTransition} instance.
+ *
+ * @return The current default transition.
+ * @see #setDefaultTransition(Transition)
+ */
+ public Transition getDefaultTransition() {
+ return mDefaultTransition;
+ }
+
+ /**
+ * Sets a specific transition to occur when the given scene is entered.
+ *
+ * @param scene The scene which, when applied, will cause the given
+ * 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}.
+ */
+ public void setTransition(Scene scene, Transition transition) {
+ mSceneTransitions.put(scene, transition);
+ }
+
+ /**
+ * Sets a specific transition to occur when the given pair of scenes is
+ * exited/entered.
+ *
+ * @param fromScene The scene being exited when the given transition will
+ * be run
+ * @param toScene The scene being entered when the given transition will
+ * 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}.
+ */
+ public void setTransition(Scene fromScene, Scene toScene, Transition transition) {
+ HashMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(toScene);
+ if (sceneTransitionMap == null) {
+ sceneTransitionMap = new HashMap<Scene, Transition>();
+ mScenePairTransitions.put(toScene, sceneTransitionMap);
+ }
+ sceneTransitionMap.put(fromScene, transition);
+ }
+
+ /**
+ * Returns the Transition for the given scene being entered. The result
+ * depends not only on the given scene, but also the scene which the
+ * {@link Scene#getSceneRoot() sceneRoot} of the Scene is currently in.
+ *
+ * @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.
+ */
+ 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();
+ if (currScene != null) {
+ HashMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(scene);
+ if (sceneTransitionMap != null) {
+ transition = sceneTransitionMap.get(currScene);
+ if (transition != null) {
+ return transition;
+ }
+ }
+ }
+ }
+ transition = mSceneTransitions.get(scene);
+ return (transition != null) ? transition : new AutoTransition();
+ }
+
+ /**
+ * This is where all of the work of a transition/scene-change is
+ * orchestrated. This method captures the start values for the given
+ * transition, exits the current Scene, enters the new scene, captures
+ * the end values for the transition, and finally plays the
+ * resulting values-populated transition.
+ *
+ * @param scene The scene being entered
+ * @param transition The transition to play for this scene change
+ */
+ private static void changeScene(Scene scene, final Transition transition) {
+
+ final ViewGroup sceneRoot = scene.getSceneRoot();
+
+ // Capture current values
+ if (transition != null) {
+ transition.captureValues(sceneRoot, true);
+ }
+
+ // Notify previous scene that it is being exited
+ Scene previousScene = sceneRoot.getCurrentScene();
+ if (previousScene != null) {
+ previousScene.exit();
+ }
+
+ scene.enter();
+
+ if (transition != null) {
+ final ViewTreeObserver observer = sceneRoot.getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ transition.captureValues(sceneRoot, false);
+ transition.play(sceneRoot);
+ return true;
+ }
+ });
+ }
+ }
+
+ /**
+ * Change to the given scene, using the
+ * appropriate transition for this particular scene change
+ * (as specified to the TransitionManager, or the default
+ * if no such transition exists).
+ *
+ * @param scene The Scene to change to
+ */
+ public void transitionTo(Scene scene) {
+ // Auto transition if there is no transition declared for the Scene, but there is
+ // a root or parent view
+ changeScene(scene, getTransition(scene));
+
+ }
+
+ /**
+ * Static utility method to simply change to the given scene using
+ * the default transition for TransitionManager.
+ *
+ * @param scene The Scene to change to
+ */
+ public static void go(Scene scene) {
+ changeScene(scene, sDefaultTransition);
+ }
+
+ /**
+ * Static utility 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>
+ *
+ * @param scene The Scene to change to
+ * @param transition The transition to use for this scene change. A
+ * value of null causes the scene change to happen with no transition.
+ */
+ public static void go(Scene scene, Transition transition) {
+ changeScene(scene, transition);
+ }
+
+ /**
+ * 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>
+ *
+ * @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);
+ }
+}
diff --git a/core/java/android/view/transition/TransitionValues.java b/core/java/android/view/transition/TransitionValues.java
new file mode 100644
index 0000000..120ace8
--- /dev/null
+++ b/core/java/android/view/transition/TransitionValues.java
@@ -0,0 +1,60 @@
+/*
+ * 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.view.transition;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+
+/**
+ * Data structure which holds cached values for the transition.
+ * The view field is the target which all of the values pertain to.
+ * The values field is a hashmap which holds information for fields
+ * according to names selected by the transitions. These names should
+ * be unique to avoid clobbering values stored by other transitions,
+ * such as the convention project:transition_name:property_name. For
+ * example, the platform might store a property "alpha" in a transition
+ * "Fader" as "android:fader:alpha".
+ *
+ * <p>These values are cached during the
+ * {@link Transition#captureValues(TransitionValues, boolean)}
+ * 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 during the play phase of the scene change,
+ * for {@link Transition#prePlay(ViewGroup, TransitionValues, TransitionValues)} and
+ * for {@link Transition#play(ViewGroup, TransitionValues, TransitionValues)}.</p>
+ */
+public class TransitionValues {
+
+ /**
+ * The View with these values
+ */
+ public View view;
+
+ /**
+ * The set of values tracked by transitions for this scene
+ */
+ public final HashMap<String, Object> values = new HashMap<String, Object>();
+
+ @Override
+ public String toString() {
+ String returnValue = "TransitionValues@" + Integer.toHexString(hashCode()) + ":\n";
+ returnValue += " view = " + view + "\n";
+ returnValue += " values = " + values + "\n";
+ return returnValue;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/view/transition/Visibility.java b/core/java/android/view/transition/Visibility.java
new file mode 100644
index 0000000..a3e6e77
--- /dev/null
+++ b/core/java/android/view/transition/Visibility.java
@@ -0,0 +1,243 @@
+/*
+ * 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.view.transition;
+
+import android.animation.Animator;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * This transition tracks changes to the visibility of target views in the
+ * start and end scenes. Visibility is determined not just by the
+ * {@link View#setVisibility(int)} state of views, but also whether
+ * views exist in the current view hierarchy. The class is intended to be a
+ * 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 more of the methods
+ * {@link #preAppear(ViewGroup, View, int, View, int)},
+ * {@link #preDisappear(ViewGroup, View, int, View, int)},
+ * {@link #appear(ViewGroup, View, int, View, int)}, and
+ * {@link #disappear(ViewGroup, View, int, View, int)}.
+ */
+public abstract class Visibility extends Transition {
+
+ private static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
+ private static final String PROPNAME_PARENT = "android:visibility:parent";
+
+ @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());
+ }
+
+ @Override
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ boolean visibilityChange = false;
+ boolean fadeIn = false;
+ int startVisibility, endVisibility;
+ View startParent, endParent;
+ if (startValues != null) {
+ startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
+ startParent = (View) startValues.values.get(PROPNAME_PARENT);
+ } else {
+ startVisibility = -1;
+ startParent = null;
+ }
+ if (endValues != null) {
+ endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
+ endParent = (View) endValues.values.get(PROPNAME_PARENT);
+ } else {
+ endVisibility = -1;
+ endParent = null;
+ }
+ boolean existenceChange = false;
+ if (startValues != null && endValues != null) {
+ if (startVisibility == endVisibility && startParent == endParent) {
+ return false;
+ } else {
+ if (startVisibility != endVisibility) {
+ if (startVisibility == View.VISIBLE) {
+ fadeIn = false;
+ visibilityChange = true;
+ } else if (endVisibility == View.VISIBLE) {
+ fadeIn = true;
+ visibilityChange = true;
+ }
+ // no visibilityChange if going between INVISIBLE and GONE
+ } else if (startParent != endParent) {
+ existenceChange = true;
+ if (endParent == null) {
+ fadeIn = false;
+ visibilityChange = true;
+ } else if (startParent == null) {
+ fadeIn = true;
+ visibilityChange = true;
+ }
+ }
+ }
+ }
+ if (startValues == null) {
+ existenceChange = true;
+ fadeIn = true;
+ visibilityChange = true;
+ } else if (endValues == null) {
+ existenceChange = true;
+ fadeIn = false;
+ visibilityChange = true;
+ }
+ if (visibilityChange) {
+ if (fadeIn) {
+ return preAppear(sceneRoot, existenceChange ? null : startValues.view,
+ startVisibility, endValues.view, endVisibility);
+ } else {
+ return preDisappear(sceneRoot, startValues.view, startVisibility,
+ existenceChange ? null : endValues.view, endVisibility);
+ }
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ boolean visibilityChange = false;
+ boolean fadeIn = false;
+ int startVisibility, endVisibility;
+ View startParent, endParent;
+ if (startValues != null) {
+ startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
+ startParent = (View) startValues.values.get(PROPNAME_PARENT);
+ } else {
+ startVisibility = -1;
+ startParent = null;
+ }
+ if (endValues != null) {
+ endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
+ endParent = (View) endValues.values.get(PROPNAME_PARENT);
+ } else {
+ endVisibility = -1;
+ endParent = null;
+ }
+ boolean existenceChange = false;
+ if (startValues != null && endValues != null) {
+ if (startVisibility == endVisibility && startParent == endParent) {
+ return null;
+ } else {
+ if (startVisibility != endVisibility) {
+ if (startVisibility == View.VISIBLE) {
+ fadeIn = false;
+ visibilityChange = true;
+ } else if (endVisibility == View.VISIBLE) {
+ fadeIn = true;
+ visibilityChange = true;
+ }
+ // no visibilityChange if going between INVISIBLE and GONE
+ } else if (startParent != endParent) {
+ existenceChange = true;
+ if (endParent == null) {
+ fadeIn = false;
+ visibilityChange = true;
+ } else if (startParent == null) {
+ fadeIn = true;
+ visibilityChange = true;
+ }
+ }
+ }
+ }
+ if (startValues == null) {
+ existenceChange = true;
+ fadeIn = true;
+ visibilityChange = true;
+ } else if (endValues == null) {
+ existenceChange = true;
+ fadeIn = false;
+ visibilityChange = true;
+ }
+ if (visibilityChange) {
+ if (fadeIn) {
+ return appear(sceneRoot, existenceChange ? null : startValues.view, startVisibility,
+ endValues.view, endVisibility);
+ } else {
+ return disappear(sceneRoot, startValues.view, startVisibility,
+ existenceChange ? null : endValues.view, endVisibility);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * The default implementation of this method does nothing. Subclasses
+ * should override if they need to set up anything prior to the
+ * transition starting.
+ *
+ * @param sceneRoot
+ * @param startView
+ * @param startVisibility
+ * @param endView
+ * @param endVisibility
+ * @return
+ */
+ protected boolean preAppear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ return true;
+ }
+
+ /**
+ * The default implementation of this method does nothing. Subclasses
+ * should override if they need to set up anything prior to the
+ * transition starting.
+ * @param sceneRoot
+ * @param startView
+ * @param startVisibility
+ * @param endView
+ * @param endVisibility
+ * @return
+ */
+ protected boolean preDisappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ return true;
+ }
+
+ /**
+ * The default implementation of this method does nothing. Subclasses
+ * should override if they need to do anything when target objects
+ * appear during the scene change.
+ * @param sceneRoot
+ * @param startView
+ * @param startVisibility
+ * @param endView
+ * @param endVisibility
+ */
+ protected Animator appear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) { return null; }
+
+ /**
+ * The default implementation of this method does nothing. Subclasses
+ * should override if they need to do anything when target objects
+ * disappear during the scene change.
+ * @param sceneRoot
+ * @param startView
+ * @param startVisibility
+ * @param endView
+ * @param endVisibility
+ */
+ protected Animator disappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) { return null; }
+
+}
diff --git a/core/java/android/view/transition/package.html b/core/java/android/view/transition/package.html
new file mode 100644
index 0000000..37dc0ec
--- /dev/null
+++ b/core/java/android/view/transition/package.html
@@ -0,0 +1,25 @@
+<html>
+<body>
+<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,
+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>
+
+<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
+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
+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>
+
+</body>
+</html>
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index c7dacf3..a324502 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -2149,7 +2149,8 @@
@Override
public void destroy() {
if (mWebView.getViewRootImpl() != null) {
- Log.e(LOGTAG, "Error: WebView.destroy() called while still attached!");
+ Log.e(LOGTAG, Log.getStackTraceString(
+ new Throwable("Error: WebView.destroy() called while still attached!")));
}
ensureFunctorDetached();
destroyJava();
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 83e2e79..07e66b7 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -2108,6 +2108,8 @@
* RemoteViews. This count cannot change during the life-cycle of a given widget, so this
* parameter should account for the maximum possible number of types that may appear in the
* See {@link Adapter#getViewTypeCount()}.
+ *
+ * @hide
*/
public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 9b12aa0..521ba81 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -141,6 +141,10 @@
public final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory";
+ private static final String LOCK_SCREEN_OWNER_INFO = Settings.Secure.LOCK_SCREEN_OWNER_INFO;
+ private static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
+ Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
+
private final Context mContext;
private final ContentResolver mContentResolver;
private DevicePolicyManager mDevicePolicyManager;
@@ -534,6 +538,22 @@
}
}
+ public void setOwnerInfo(String info, int userId) {
+ setString(LOCK_SCREEN_OWNER_INFO, info, userId);
+ }
+
+ public void setOwnerInfoEnabled(boolean enabled) {
+ setBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, enabled);
+ }
+
+ public String getOwnerInfo(int userId) {
+ return getString(LOCK_SCREEN_OWNER_INFO);
+ }
+
+ public boolean isOwnerInfoEnabled() {
+ return getBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, false);
+ }
+
/**
* Compute the password quality from the given password string.
*/
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index d588a1b..a2120df 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -338,23 +338,11 @@
}
TextLayoutShaper::TextLayoutShaper() {
- init();
-
mBuffer = hb_buffer_create();
}
-void TextLayoutShaper::init() {
- mDefaultTypeface = SkTypeface::CreateFromName(NULL, SkTypeface::kNormal);
-}
-
-void TextLayoutShaper::unrefTypefaces() {
- SkSafeUnref(mDefaultTypeface);
-}
-
TextLayoutShaper::~TextLayoutShaper() {
hb_buffer_destroy(mBuffer);
-
- unrefTypefaces();
}
void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars,
@@ -838,23 +826,27 @@
}
if (baseGlyphCount != 0) {
+ SkTypeface::Style style = SkTypeface::kNormal;
+ if (typeface != NULL) {
+ style = typeface->style();
+ }
typeface = typefaceForScript(paint, typeface, hb_buffer_get_script(mBuffer));
if (!typeface) {
baseGlyphCount = 0;
- typeface = mDefaultTypeface;
- SkSafeRef(typeface);
+ typeface = SkTypeface::CreateFromName(NULL, style);
#if DEBUG_GLYPHS
ALOGD("Using Default Typeface");
#endif
}
} else {
if (!typeface) {
- typeface = mDefaultTypeface;
+ typeface = SkTypeface::CreateFromName(NULL, SkTypeface::kNormal);
#if DEBUG_GLYPHS
- ALOGD("Using Default Typeface");
+ ALOGD("Using Default Typeface (normal style)");
#endif
+ } else {
+ SkSafeRef(typeface);
}
- SkSafeRef(typeface);
}
mShapingPaint.setTypeface(typeface);
@@ -898,8 +890,6 @@
hb_face_destroy(mCachedHBFaces.valueAt(i));
}
mCachedHBFaces.clear();
- unrefTypefaces();
- init();
}
TextLayoutEngine::TextLayoutEngine() {
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index f9b9900..5414a11 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -197,18 +197,10 @@
SkPaint mShapingPaint;
/**
- * Skia default typeface to be returned if we cannot resolve script
- */
- SkTypeface* mDefaultTypeface;
-
- /**
* Cache of Harfbuzz faces
*/
KeyedVector<SkFontID, hb_face_t*> mCachedHBFaces;
- void init();
- void unrefTypefaces();
-
SkTypeface* typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
hb_script_t script);
@@ -228,7 +220,6 @@
hb_face_t* referenceCachedHBFace(SkTypeface* typeface);
bool isComplexScript(hb_script_t script);
-
}; // TextLayoutShaper
/**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4d1291d..156fa56 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -134,6 +134,7 @@
<protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE" />
<protected-broadcast android:name="android.net.conn.DATA_ACTIVITY_CHANGE" />
+ <protected-broadcast android:name="android.net.conn.BACKGROUND_DATA_SETTING_CHANGED" />
<protected-broadcast android:name="android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED" />
<protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" />
@@ -918,7 +919,8 @@
<permission android:name="android.permission.RECORD_AUDIO"
android:permissionGroup="android.permission-group.MICROPHONE"
android:protectionLevel="dangerous"
- android:label="@string/permlab_recordAudio" />
+ android:label="@string/permlab_recordAudio"
+ android:description="@string/permdesc_recordAudio" />
<!-- =========================================== -->
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 8f7529e..9c93531 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Herlaai"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Die bladsy by \"<xliff:g id="TITLE">%s</xliff:g>\" sê:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Navigeer weg van hierdie bladsy?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Raak OK om voort te gaan, of Kanselleer om op die huidige bladsy te bly."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Bevestig"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Wenk: Dubbeltik om in en uit te zoem."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Outovul"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 7f686c9..db6cd18 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -387,7 +387,7 @@
<string name="permdesc_clearAppCache" product="tablet" msgid="8974640871945434565">"በሌሎች መተግበሪያዎች የመሸጎጫ ማውጫዎች ውስጥ ያሉትን ፋይሎች በመሰረዝ መተግበሪያው የጡባዊ ማከማቻ ቦታ ነጻ እንዲያስለቅቅ ያስችለዋል። ይሄ ሌሎች መተግበሪያዎች ውሂባቸውን ዳግም ማምጣት ስላለባቸው ይበልጥ ዘግየት ብለው እንዲጀምሩ ሊያደርጋቸው ይችላል።"</string>
<string name="permdesc_clearAppCache" product="default" msgid="2459441021956436779">"በሌሎች መተግበሪያዎች የመሸጎጫ ማውጫዎች ውስጥ ያሉትን ፋይሎች በመሰረዝ መተግበሪያው የስልክ ማከማቻ ቦታ ነጻ እንዲያስለቅቅ ያስችለዋል። ይሄ ሌሎች መተግበሪያዎች ውሂባቸውን ዳግም ማምጣት ስላለባቸው ይበልጥ ዘግየት ብለው እንዲጀምሩ ሊያደርጋቸው ይችላል።"</string>
<string name="permlab_movePackage" msgid="3289890271645921411">"የመተግበሪያ ሃብቶችን አንቀሳቅስ"</string>
- <string name="permdesc_movePackage" msgid="319562217778244524">"ከውስጣዊ ወደ ውጫዊ ሚዲያ እና በተገላቢጦሽ የመተግበሪያ ሃብቶችን ለማንቀሳቀስ ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
+ <string name="permdesc_movePackage" msgid="319562217778244524">"ከውስጣዊ ወደ ውጫዊ ማህደረ መረጃ እና በተገላቢጦሽ የመተግበሪያ ሃብቶችን ለማንቀሳቀስ ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
<string name="permlab_readLogs" msgid="6615778543198967614">"ወሳኝ የማስታወሻ ውሂብ አንብብ"</string>
<string name="permdesc_readLogs" product="tablet" msgid="82061313293455151">" ከስርዓቱ የተለያዩ የማስታወሻ ፋይሎች ውስጥ ለማንበብ ለመተግበሪያ ይፈቅዳሉ። ይህ ስለ ጡባዊ ተኮህ ምን እያደረክበት እንደሆነ የግላዊ ወይም የግል መረጃን ጨምሮ ጠቅላላ መረጃ ለማግኘት ይፈቅዳል።"</string>
<string name="permdesc_readLogs" product="default" msgid="2063438140241560443">" ከስርዓቱ የተለያዩ የማስታወሻ ፋይሎች ውስጥ ለማንበብ ለመተግበሪያይፈቅዳሉ። ይህ ስለ ስልክህ ምን እያደረክበት እንደሆነ የግላዊ ወይም የግል መረጃን ጨምሮ ጠቅላላ መረጃ ለማግኘት ይፈቅዳል።"</string>
@@ -403,9 +403,9 @@
<string name="permlab_setPreferredApplications" msgid="8463181628695396391">"ተመራጭ መተግበሪያዎች አዘጋጅ"</string>
<string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"ተመራጭ መተግበሪያዎችህን ለመቀየር ለመተግበሪያው ይፈቅዳሉ፡፡ ከአንተ የግል ውሂብ ለመሰብሰብ ያሉትን መተግበሪያዎች በመላክ፤ በመሄድ ላይ ያሉ መተግበሪያዎችን ተንኮል አዘል መተግበሪያዎች በዝምታ ሊለውጡ ይችላሉ፡፡"</string>
<string name="permlab_writeSettings" msgid="2226195290955224730">"የስርዓት ቅንብሮችን አስተካክል"</string>
- <string name="permdesc_writeSettings" msgid="7775723441558907181">"የስርዓት ቅንጅቶችን ውሂብ ለመቀየር ለመተግበሪያው ይፈቅዳሉ፡፡ ተንኮል አዘል መተግበሪያዎች የስርዓትህን አወቃቀር ብልሹ ሊያደርጉት ይችላሉ፡፡"</string>
+ <string name="permdesc_writeSettings" msgid="7775723441558907181">"የስርዓት ቅንብሮችን ውሂብ ለመቀየር ለመተግበሪያው ይፈቅዳሉ። ተንኮል አዘል መተግበሪያዎች የስርዓትዎን አወቃቀር ብልሹ ሊያደርጉት ይችላሉ።"</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"የስርዓት ቅንብሮችንደህንነት ቀይር"</string>
- <string name="permdesc_writeSecureSettings" msgid="8159535613020137391">"የስርዓቱን ደህንነቱ የተጠበቀ ቅንጅቶችን ውሂብ ለመቀየር ለመተግበሪያው ይፈቅዳሉ፡፡ለመደበኛ ትግበራዎች አያስፈልግም።"</string>
+ <string name="permdesc_writeSecureSettings" msgid="8159535613020137391">"የስርዓቱን ደህንነቱ የተጠበቀ ቅንብሮችን ውሂብ ለመቀየር ለመተግበሪያው ይፈቅዳሉ። ለመደበኛ ትግበራዎች አያስፈልግም።"</string>
<string name="permlab_writeGservices" msgid="2149426664226152185">"የGoogle አገልግሎቶች ካርታን ቀይር"</string>
<string name="permdesc_writeGservices" msgid="1287309437638380229">"ትግበራ የGoogle ካርታ አገልግሎቶችን ለመቀየር ይፈቅዳል።ለመደበኛ ትግበራዎች ጥቅም አይደለም።"</string>
<string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"መነሻ ላይ አሂድ"</string>
@@ -541,8 +541,8 @@
<string name="permlab_accountManagerService" msgid="4829262349691386986">"እንደ አውርድአዸራጅአገልግሎት"</string>
<string name="permdesc_accountManagerService" msgid="1948455552333615954">" ወደ መለያ አረጋጋጮች ጥሪ ለማድረግ ለመተግበሪያ ይፈቅዳሉ።"</string>
<string name="permlab_getAccounts" msgid="1086795467760122114">"መሣሪያው ላይ ያሉ መለያዎችን አግኝ"</string>
- <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"መተግበሪያው በጡባዊ ተኮው የሚታወቁትን መለያዎች ዝርዝር እንዲያገኝ ይፈቅድለታል። ይህ በጫንካቸው ማናቸውም መተግበሪያዎች የተፈጠሩ መለያዎችን ሊያጠቃልል ይችላል።"</string>
- <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"መተግበሪያው በስልኩ የሚታወቁትን መለያዎች ዝርዝር እንዲያገኝ ይፈቅድለታል። ይህ በጫንካቸው ማናቸውም መተግበሪያዎች የተፈጠሩ መለያዎችን ሊያጠቃልል ይችላል።"</string>
+ <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"መተግበሪያው በጡባዊ ተኮው የሚታወቁትን መለያዎች ዝርዝር እንዲያገኝ ይፈቅድለታል። ይህ በጫንዋቸው ማናቸውም መተግበሪያዎች የተፈጠሩ መለያዎችን ሊያጠቃልል ይችላል።"</string>
+ <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"መተግበሪያው በስልኩ የሚታወቁትን መለያዎች ዝርዝር እንዲያገኝ ይፈቅድለታል። ይህ በጫንዋቸው ማናቸውም መተግበሪያዎች የተፈጠሩ መለያዎችን ሊያጠቃልል ይችላል።"</string>
<string name="permlab_authenticateAccounts" msgid="5265908481172736933">"መለያዎችን ፍጠርና የይለፍ ቃላትን አስቀምጥ"</string>
<string name="permdesc_authenticateAccounts" msgid="5472124296908977260">"የመለያ አረጋጋጭ መለያ መናጅ ችሎታን ለመጠቀም፣ መለያ መፍጠር እና የይለፍ ቃሎችን ለማግኘት እና ለማቀናጀት አክሎ ለመተግበሪያው ይፈቅዳሉ ።"</string>
<string name="permlab_manageAccounts" msgid="4983126304757177305">"መለያዎችን አክል ወይም አስወግድ"</string>
@@ -554,7 +554,7 @@
<string name="permlab_createNetworkSockets" msgid="8018758136404323658">"ሙሉ የአውታረ መረብ መዳረሻ"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"መተግበሪያው የአውታረ መረብ መሰኪያዎችን እንዲፈጥር እና ብጁ የአውታረ መረብ ፕሮቶኮሎችን እንዲጠቀም ይፈቅድለታል። አሳሹ እና ሌሎች መተግበሪያዎች ውሂብ ወደ በይነመረብ የመላኪያ መንገዶችን ስለሚያቀርቡውሂብ ወደ በይነመረብ ለመላክ ይህ ፍቃድ አያስፈልግም።"</string>
<string name="permlab_writeApnSettings" msgid="505660159675751896">"የአውታረ መረብ ቅንብሮች እና ትራፊክ ለውጥ/ አቋርጥ"</string>
- <string name="permdesc_writeApnSettings" msgid="5333798886412714193">"የአውታረ መረብ ቅንጅቶችን ለመለወጥ እና ለማቋረጥ እና ሁሉንም የአውታረ መረብ ትራፊክ ለመመርመር፤ለምሳሌ ወኪል እና የማንኛውም APN ወደብ ለመለወጥ ለመተግበሪያው ይፈቅዳሉ፡፡ ተንኮል አዘል መተግበሪያዎች ሊቆጣጠሩ፣ አቅጣጫ ሊያስቀይሩ ፣ ወይም ያለአንተ እውቅና የአውታረ መረብ ፓኬቶችን ሊቀይሩ ይችላሉ፡፡"</string>
+ <string name="permdesc_writeApnSettings" msgid="5333798886412714193">"የአውታረ መረብ ቅንብሮችን ለመለወጥ እና ለማቋረጥ እና ሁሉንም የአውታረ መረብ ትራፊክ ለመመርመር፤ለምሳሌ ወኪል እና የማንኛውም APN ወደብ ለመለወጥ ለመተግበሪያው ይፈቅዳሉ። ተንኮል አዘል መተግበሪያዎች ሊቆጣጠሩ፣ አቅጣጫ ሊያስቀይሩ ፣ ወይም ያለእርስዎ እውቅና የአውታረ መረብ ፓኬቶችን ሊቀይሩ ይችላሉ።"</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"የአውታረ መረብ ተያያዥነትን ለውጥ"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"የእውታረ መረቡን ግንኙነት ሁኔታ ለመለወጥ ለመተግበሪያው ይፈቅዳሉ።"</string>
<string name="permlab_changeTetherState" msgid="5952584964373017960">"የተያያዘ ግንኙነት ለውጥ"</string>
@@ -601,7 +601,7 @@
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"ጥበቃ ወደሚደረግለት ማከማቻ ያለ መዳረሻን ፈትሽ"</string>
<string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"መተግበሪያው በወደፊት መሳሪዎች ላይ ለሚኖር የUSB ማህደረ ትውስታ ፈቃድ እንዲሞክር ይፈቅድለታል።"</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"መተግበሪያው በወደፊት መሳሪዎች ላይ ለሚኖረው SD ካርድ ፈቃድ እንዲሞክር ይፈቅድለታል።"</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"የUSB ማከማቻህን ይዘቶች ቀይር ወይም ሰርዝ"</string>
+ <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"የUSB ማከማቻዎን ይዘቶች ይቀይሩ ወይም ይሰርዙ"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"የSD ካርድህን ይዘቶች ቀይር ወይም ሰርዝ"</string>
<string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"ወደ USB ማህደረ ትውስታው ለመፃፍ ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"መተግበሪያውን ወደ SD ካርድ ለመፃፍ ይፈቅዳል።"</string>
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"ድጋሚ አስነሳ"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"በ«<xliff:g id="TITLE">%s</xliff:g>» ያለው ገጽ ይህን ይላል፦"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"ጃቫስክሪፕት"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"ከዚህ ገፅ ወጣ ብሎ ይዳስ? "\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n" ለመቀጠል እሺ ፣ወይም የአሁኑ ገፅ ላይ ለመቆየት ይቅር ምረጥ።"</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"አረጋግጥ"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"ጠቃሚ ምክር፦ ለማጉላት እና ለማሳነስ ሁለቴ-መታ አድርግ።"</string>
<string name="autofill_this_form" msgid="4616758841157816676">"ራስ ሙላ"</string>
@@ -1174,20 +1181,20 @@
<string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"አዲስ፦ "</font></string>
<string name="perms_description_app" msgid="5139836143293299417">"በ<xliff:g id="APP_NAME">%1$s</xliff:g> የቀረበ።"</string>
<string name="no_permissions" msgid="7283357728219338112">"ምንም ፍቃዶች አይጠየቁም"</string>
- <string name="perm_costs_money" msgid="4902470324142151116">"ይህ ገንዘብ ሊያስወጣዎብዎት ይችላል"</string>
+ <string name="perm_costs_money" msgid="4902470324142151116">"ይህ ገንዘብ ሊያስወጣዎት ይችላል"</string>
<string name="usb_storage_activity_title" msgid="4465055157209648641">"USB ብዙ ማከማቻ"</string>
<string name="usb_storage_title" msgid="5901459041398751495">"USB ተያይዟል"</string>
<string name="usb_storage_message" product="nosdcard" msgid="3308538094316477839">"ከኮምፒዩተርህ ጋር በUSB በኩል አገናኝተሃል። በኮምፒዩተርህ እና በAndroid SD ማከማቻህ መካከል ፋይሎች ለመቅዳት ከፈለግህ ከዚህ በታች ያለውን አዝራር ንካ።"</string>
<string name="usb_storage_message" product="default" msgid="805351000446037811">"ከኮምፒዩተርህ ጋር በUSB በኩል አገናኝተሃል። በኮምፒዩተርህ እና በAndroid SD ማከማቻህ መካከል ፋይሎች ለመቅዳት ከፈለግህ ከዚህ በታች ያለውን አዝራር ንካ።"</string>
<string name="usb_storage_button_mount" msgid="1052259930369508235">"የUSB ማከማቻ አብራ"</string>
- <string name="usb_storage_error_message" product="nosdcard" msgid="3017045217365540658">"የUSB ማከማቻህን ለUSB ብዙማከማቻ መጠቀም ችግር አለ።"</string>
+ <string name="usb_storage_error_message" product="nosdcard" msgid="3017045217365540658">"የUSB ማከማቻዎን ለUSB ብዙ ማከማቻ መጠቀም ችግር አለ።"</string>
<string name="usb_storage_error_message" product="default" msgid="2876018512716970313">"የ SD ካርድህን ለUSB ብዙማከማቻ መጠቀም ችግር አለ።"</string>
<string name="usb_storage_notification_title" msgid="8175892554757216525">"USB ተያይዟል"</string>
<string name="usb_storage_notification_message" msgid="939822783828183763">"ፋይሎችን ከ/ወደ ኮምፒዩተርህ ለመቅዳት ንካ።"</string>
<string name="usb_storage_stop_notification_title" msgid="2336058396663516017">"የUSB ማከማቻ አጥፋ"</string>
<string name="usb_storage_stop_notification_message" msgid="1656852098555623822">"የUSB ማከማቻ ለማጥፋት ንካ።"</string>
<string name="usb_storage_stop_title" msgid="660129851708775853">"USB ማከማቻ በጥቅም ላይ"</string>
- <string name="usb_storage_stop_message" product="nosdcard" msgid="4264025280777219521">"USB ማከማቻን ከማጥፋትህ በፊት፤ የAndroid USB ማከማቻህን ከኮምውተርህ ንቀል (\"አውጣ\")።"</string>
+ <string name="usb_storage_stop_message" product="nosdcard" msgid="4264025280777219521">"USB ማከማቻን ከማጥፋትዎ በፊት፤ የAndroid USB ማከማቻዎን ከኮምውተርዎ ይንቀሉ (\"ያውጡ\")።"</string>
<string name="usb_storage_stop_message" product="default" msgid="8043969782460613114">"የUSB ማከማቻ ከማጥፋት በፊት የAndroid SD ካርድህን ከኮምፒዩተርህ ላይ ንቀል(“አውጣ”)።"</string>
<string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"USB ማከማቻ አጥፋ"</string>
<string name="usb_storage_stop_error_message" msgid="1970374898263063836">"የ USB ማከማቻ ለማጥፋት ችግር ነበር። USB አስተናጋጅ መንቀልህን አረጋግጥ፤ ከዛም እንደገና ሞክር።"</string>
@@ -1382,7 +1389,7 @@
<string name="storage_usb" msgid="3017954059538517278">"የUSB ማከማቻ"</string>
<string name="extract_edit_menu_button" msgid="8940478730496610137">"አርትዕ"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"የውሂብ አጠቃቀም ማስጠንቀቂየ"</string>
- <string name="data_usage_warning_body" msgid="2814673551471969954">"ቅንጅቶችን እና አጠቃቀምን ለማየት ንካ።"</string>
+ <string name="data_usage_warning_body" msgid="2814673551471969954">"ቅንብሮችን እና አጠቃቀምን ለማየት ይንኩ።"</string>
<string name="data_usage_3g_limit_title" msgid="7093334419518706686">"2G-3G ውሂብ ቦዝኗል"</string>
<string name="data_usage_4g_limit_title" msgid="7636489436819470761">"4G ውሂብ ቦዝኗል"</string>
<string name="data_usage_mobile_limit_title" msgid="7869402519391631884">"የተንቀሳቃሽ ውሂብ ቦዝኗል"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 004faf3..d68a546 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"إعادة تشغيل"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"تعرض الصفحة في \"<xliff:g id="TITLE">%s</xliff:g>\":"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"جافا سكريبت"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"هل تريد الانتقال بعيدًا عن هذه الصفحة؟"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"المس \"موافق\" للمتابعة، أو \"إلغاء\" للبقاء في الصفحة الحالية."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"تأكيد"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"نصيحة: اضغط مرتين للتكبير والتصغير."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"ملء تلقائي"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 22c20b8..7456890 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Перазагрузіць"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"На старонцы з адрасам <xliff:g id="TITLE">%s</xliff:g> вызначана:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Пакiнуць гэту старонку?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Націсніце \"OK\", каб працягнуць, або \"Адмена\", каб застацца на бягучай старонцы."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Пацвердзіць"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Падказка: двойчы націсніце, каб павялічыць або паменшыць."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Аўтазапаўненне"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 8cdd28c..79558d2 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Рестартиране"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Страницата на адрес „<xliff:g id="TITLE">%s</xliff:g>“ съобщава:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Искате ли да напуснете тази страница?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Докоснете „OK“, за да продължите, или „Отказ“, за да останете на нея."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Потвърждение"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Съвет: Докоснете двукратно, за да увеличите или намалите мащаба."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Автопоп."</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index a68437b..0a19f6c 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Reinicia"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"La pàgina de \"<xliff:g id="TITLE">%s</xliff:g>\" diu:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Vols sortir d\'aquesta pàgina?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Selecciona D\'acord per continuar o Cancel·la per seguir a la pàgina actual."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Confirma"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Consell: Pica dos cops per ampliar i per reduir."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Em. aut."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 8d1fa5d..9ab8b91 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -155,7 +155,7 @@
<string name="global_action_lock" msgid="2844945191792119712">"Zámek obrazovky"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"Vypnout"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Hlášení chyb"</string>
- <string name="bugreport_title" msgid="2667494803742548533">"Zaznamenat zprávu o chybě"</string>
+ <string name="bugreport_title" msgid="2667494803742548533">"Vytvořit chybové hlášení"</string>
<string name="bugreport_message" msgid="398447048750350456">"Shromažďuje informace o aktuálním stavu zařízení. Tyto informace je následně možné poslat v e-mailové zprávě, chvíli však potrvá, než bude hlášení o chybě připraveno k odeslání. Buďte prosím trpěliví."</string>
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Tichý režim"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Zvuk je VYPNUTÝ."</string>
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Restartovat"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Stránka <xliff:g id="TITLE">%s</xliff:g> uvádí:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Chcete opustit tuto stránku?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Chcete-li pokračovat, dotkněte se možnosti OK. Chcete-li zůstat na aktuální stránce, dotkněte se možnosti Zrušit."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Potvrdit"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Tip: Dvojitým klepnutím můžete zobrazení přiblížit nebo oddálit."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Aut.vyp."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 36f4b7f..cda47d9 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -156,7 +156,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Sluk"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Fejlrapport"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Lav fejlrapport"</string>
- <string name="bugreport_message" msgid="398447048750350456">"Der indsamles oplysninger om din enheds aktuelle status, der efterfølgende sendes i en e-mail. Der går lidt tid, fra fejlrapporten påbegyndes, til den er klar til at blive sendt. Tak for tålmodigheden."</string>
+ <string name="bugreport_message" msgid="398447048750350456">"Der indsamles oplysninger om din enheds aktuelle status, der efterfølgende sendes i en e-mail. Der går lidt tid fra, at fejlrapporten påbegyndes, til den er klar til at blive sendt. Tak for tålmodigheden."</string>
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Lydløs"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Lyden er slået FRA"</string>
<string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"Lyden er TIL"</string>
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Genstart"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"På siden på \"<xliff:g id="TITLE">%s</xliff:g>\" står der:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"Javascript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Vil du gå væk fra denne side?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Tryk på OK for at fortsætte eller Annuller for at blive på den aktuelle side."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Bekræft"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Tip! Dobbeltklik for at zoome ind eller ud."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Autofyld"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index cedbb01..b667f53 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Neustart"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Auf der Seite \"<xliff:g id="TITLE">%s</xliff:g>\" steht:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Diese Seite verlassen?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Tippen Sie zum Fortfahren auf \"OK\" oder tippen Sie auf \"Abbrechen\", um auf der aktuellen Seite zu bleiben."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Bestätigen"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Tipp: Zum Vergrößern und Verkleinern zweimal tippen"</string>
<string name="autofill_this_form" msgid="4616758841157816676">"AutoFill"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 16db147..bd47858 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Επανεκκίνηση"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Η σελίδα στον τίτλο \"<xliff:g id="TITLE">%s</xliff:g>\" λέει:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Απομάκρυνση από αυτή τη σελίδα;"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Αγγίξτε το στοιχείο \"OK\" για συνέχεια ή \"Ακύρωση\" για παραμονή στην τρέχουσα σελίδα."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Επιβεβαίωση"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Συμβουλή: Πατήστε δύο φορές για μεγέθυνση και σμίκρυνση."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Αυτόματη συμπλήρωση"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 905cee3..ff6cf0e 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Reboot"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"The page at \"<xliff:g id="TITLE">%s</xliff:g>\" says:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Navigate away from this page?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Touch OK to continue or Cancel to stay on the current page."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Confirm"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Tip: double-tap to zoom in and out."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Auto-fill"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 7f3c0db..6c264a4 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -773,7 +773,7 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Presiona el Menú para desbloquear o realizar una llamada de emergencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Presionar Menú para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Dibujar el patrón de desbloqueo"</string>
- <string name="lockscreen_emergency_call" msgid="5347633784401285225">"Llamada de emergencia"</string>
+ <string name="lockscreen_emergency_call" msgid="5347633784401285225">"Realizar llamada de emergencia"</string>
<string name="lockscreen_return_to_call" msgid="5244259785500040021">"Regresar a llamada"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Vuelve a intentarlo."</string>
@@ -783,7 +783,7 @@
<string name="lockscreen_charged" msgid="321635745684060624">"Cargado"</string>
<string name="lockscreen_battery_short" msgid="4477264849386850266">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Conecta tu cargador."</string>
- <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"No hay tarjeta SIM."</string>
+ <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Sin tarjeta SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"No hay tarjeta SIM en el tablet."</string>
<string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"No hay tarjeta SIM en el dispositivo."</string>
<string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Inserta una tarjeta SIM."</string>
@@ -795,7 +795,7 @@
<string name="lockscreen_transport_pause_description" msgid="7659088786780128001">"Botón Pausa"</string>
<string name="lockscreen_transport_play_description" msgid="5888422938351019426">"Botón Reproducir"</string>
<string name="lockscreen_transport_stop_description" msgid="4562318378766987601">"Botón Detener"</string>
- <string name="emergency_calls_only" msgid="6733978304386365407">"Sólo llamadas de emergencia"</string>
+ <string name="emergency_calls_only" msgid="6733978304386365407">"Solo llamadas de emergencia"</string>
<string name="lockscreen_network_locked_message" msgid="143389224986028501">"Red bloqueada"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"La tarjeta SIM está bloqueada con PUK."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consulta la guía del usuario o comunícate con el servicio de atención al cliente."</string>
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Reiniciar"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"La página \"<xliff:g id="TITLE">%s</xliff:g>\" dice:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"¿Deseas salir de esta página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Toca Aceptar para continuar o Cancelar para permanecer en la página actual."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Consejo: Toca dos veces para acercar y alejar la imagen."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Autocompletar"</string>
@@ -1439,7 +1446,7 @@
<string name="wifi_display_notification_title" msgid="2223050649240326557">"Se conectó la pantalla inalámbrica"</string>
<string name="wifi_display_notification_message" msgid="4498802012464170685">"Esta pantalla se muestra en otro dispositivo."</string>
<string name="wifi_display_notification_disconnect" msgid="6183754463561153372">"Desconectar"</string>
- <string name="kg_emergency_call_label" msgid="684946192523830531">"Llamada de emergencia"</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Realizar llamada de emergencia"</string>
<string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"¿Olvidaste el patrón?"</string>
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Patrón incorrecto"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Contraseña incorrecta"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 2b7b476..1b5a024 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Reiniciar"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"La página \"<xliff:g id="TITLE">%s</xliff:g>\" dice:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"¿Quieres salir de esta página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Toca Aceptar para continuar o Cancelar para permanecer en la página actual."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Sugerencia: toca dos veces para ampliar o reducir el contenido."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Autocompletar"</string>
@@ -1195,7 +1202,7 @@
<string name="dlg_confirm_kill_storage_users_text" msgid="5100428757107469454">"Si activas el almacenamiento USB, se detendrán algunas aplicaciones que estás usando y es posible que no estén disponibles hasta que lo desactives."</string>
<string name="dlg_error_title" msgid="7323658469626514207">"Error de funcionamiento de USB"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Aceptar"</string>
- <string name="usb_mtp_notification_title" msgid="3699913097391550394">"Conectado como disp. multimedia"</string>
+ <string name="usb_mtp_notification_title" msgid="3699913097391550394">"Conectado como dispositivo multimedia"</string>
<string name="usb_ptp_notification_title" msgid="1960817192216064833">"Conectado como una cámara"</string>
<string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"Conectado como instalador"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a un accesorio USB"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 821c899..5679201 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Taaskäivita"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Leht „<xliff:g id="TITLE">%s</xliff:g>” ütleb järgmist."</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Kas soovite sellelt lehelt lahkuda?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Jätkamiseks puudutage valikut OK, praegusele lehele jäämiseks valikut Tühista."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Kinnita"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Vihje: suurendamiseks ja vähendamiseks puudutage kaks korda."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Automaatne täitmine"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index a8f06d9..fff4c7a 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -622,7 +622,7 @@
<string name="permlab_accessNotifications" msgid="7673416487873432268">"اعلانهای دسترسی"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"به برنامه اجازه میدهد به بازیابی، بررسی و پاک کردن اعلانها از جمله موارد پست شده توسط سایر برنامهها بپردازد."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"اتصال به یک سرویس شنونده اعلان"</string>
- <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"به دارنده اجازه میدهد به یک رابط سطح بالای سرویس شنونده اعلان متصل شود. این هرگز برای برنامههای عادی ضروری نیست."</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"به دارنده اجازه میدهد به یک رابط سطح بالای سرویس شنونده اعلان متصل شود. هرگز نباید برای برنامههای عادی لازم شود."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین رمز ورود"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"طول و نویسههای مجاز در گذرواژههای بازکردن قفل صفحه را کنترل کنید."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"نمایش تلاشهای قفل گشایی صفحه"</string>
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"راهاندازی مجدد"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"صفحه در \"<xliff:g id="TITLE">%s</xliff:g>\" میگوید:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"جاوا اسکریپت"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"از این صفحه خارج میشوید؟"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"برای ادامه روی تأیید و برای ماندن در همین صفحه روی لغو کلیک کنید."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"تأیید"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"نکته: برای بزرگنمایی و کوچکنمایی، دو بار ضربه بزنید."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"تکمیل خودکار"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index d968b5f..553e0c1 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Käynnistä uudelleen"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Sivu <xliff:g id="TITLE">%s</xliff:g> sanoo:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Siirrytäänkö pois tältä sivulta?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Valitse OK, jos haluat jatkaa, tai Peruuta, jos et halua siirtyä pois sivulta."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Vahvista"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Vinkki: lähennä ja loitonna kaksoisnapauttamalla."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Aut. täyttö"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 9d3fe35..bb7df82 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Redémarrer"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"La page \"<xliff:g id="TITLE">%s</xliff:g>\" indique :"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Voulez-vous quitter cette page ?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Appuyez sur \"OK\" pour continuer ou \"Annuler\" pour rester sur la page actuelle."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Confirmer"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Conseil : Appuyez deux fois pour faire un zoom avant ou arrière."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Saisie auto"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 89f13e0..9840400 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"रीबूट करें"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"\'<xliff:g id="TITLE">%s</xliff:g>\' पर यह पृष्ठ दर्शाता है:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"इस पृष्ठ से दूर नेविगेट करें?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"जारी रखने के लिए ठीक को चुनें, या वर्तमान पृष्ठ पर रहने के लिए रद्द करें को चुनें."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"पुष्टि करें"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"युक्ति: ज़ूम इन और आउट करने के लिए डबल-टैप करें."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"स्वत: भरण"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index fcbdeb5..d90717f 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Ponovno pokreni"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Stranica na adresi \"<xliff:g id="TITLE">%s</xliff:g>\" sadrži sljedeće:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Želite otići s ove lokacije?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Za nastavak dodirnite U redu, a za ostanak na trenutačnoj stranici dodirnite Odustani."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Potvrdi"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Savjet: Dvaput dotaknite za povećavanje i smanjivanje."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Aut.pop."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 684cf67..db629af 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Újraindítás"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"A \"<xliff:g id="TITLE">%s</xliff:g>\" címen található oldal szerint:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Elhagyja ezt az oldalt?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"A folytatáshoz érintse meg az OK, a jelenlegi oldalon maradáshoz a Mégse lehetőséget."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Megerősítés"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Tipp: érintse meg kétszer a nagyításhoz és kicsinyítéshez."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Kitöltés"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 694a6f7..9ec11e4 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Mulai ulang"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Laman pada \"<xliff:g id="TITLE">%s</xliff:g>\" menyatakan:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Beranjak dari laman ini?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Sentuh Oke untuk melanjutkan atau Batal untuk tetap pada laman ini."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Konfirmasi"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Kiat: Ketuk dua kali untuk memperbesar dan memperkecil."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Isiotomatis"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index b145504..d25f82a 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -440,16 +440,16 @@
<string name="permlab_writeCalendar" msgid="8438874755193825647">"aggiunta o modifica di eventi di calendario e invio di email agli ospiti a insaputa dei proprietari"</string>
<string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Consente all\'applicazione di aggiungere, rimuovere, modificare gli eventi che puoi modificare sul tablet, inclusi quelli di amici o colleghi. Ciò potrebbe consentire all\'applicazione di inviare messaggi apparentemente provenienti dai proprietari del calendario o di modificare eventi all\'insaputa dei proprietari."</string>
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Consente all\'applicazione di aggiungere, rimuovere, modificare gli eventi che puoi modificare sul telefono, inclusi quelli di amici o colleghi. Ciò potrebbe consentire all\'applicazione di inviare messaggi apparentemente provenienti dai proprietari del calendario o di modificare eventi all\'insaputa dei proprietari."</string>
- <string name="permlab_accessMockLocation" msgid="8688334974036823330">"fonti di localizzazione fittizie per test"</string>
- <string name="permdesc_accessMockLocation" msgid="5808711039482051824">"Crea fonti di localizzazione fittizie per i test o installa un nuovo fornitore di posizione. Ciò consente all\'applicazione di ignorare la posizione e/o lo stato restituito da altre fonti di localizzazione, come il GPS o fornitori di posizione."</string>
+ <string name="permlab_accessMockLocation" msgid="8688334974036823330">"fonti di geolocalizzazione fittizie per test"</string>
+ <string name="permdesc_accessMockLocation" msgid="5808711039482051824">"Crea fonti di geolocalizzazione fittizie per i test o installa un nuovo fornitore di posizione. Ciò consente all\'applicazione di ignorare la posizione e/o lo stato restituito da altre fonti di geolocalizzazione, come il GPS o fornitori di posizione."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accesso a comandi aggiuntivi del provider di localizz."</string>
- <string name="permdesc_accessLocationExtraCommands" msgid="5945166642335800763">"Consente all\'applicazione di accedere a ulteriori comandi del fornitore di posizione. Ciò potrebbe consentire all\'applicazione di interferire con il funzionamento del GPS o di altre fonti di localizzazione."</string>
+ <string name="permdesc_accessLocationExtraCommands" msgid="5945166642335800763">"Consente all\'applicazione di accedere a ulteriori comandi del fornitore di posizione. Ciò potrebbe consentire all\'applicazione di interferire con il funzionamento del GPS o di altre fonti di geolocalizzazione."</string>
<string name="permlab_installLocationProvider" msgid="6578101199825193873">"autorizzazione a installare un provider di localizzazione"</string>
- <string name="permdesc_installLocationProvider" msgid="9066146120470591509">"Crea fonti di localizzazione fittizie per i test o installa un nuovo fornitore di posizione. Ciò consente all\'applicazione di ignorare la posizione e/o lo stato restituito da altre fonti di localizzazione, come il GPS o fornitori di posizione."</string>
+ <string name="permdesc_installLocationProvider" msgid="9066146120470591509">"Crea fonti di geolocalizzazione fittizie per i test o installa un nuovo fornitore di posizione. Ciò consente all\'applicazione di ignorare la posizione e/o lo stato restituito da altre fonti di geolocalizzazione, come il GPS o fornitori di posizione."</string>
<string name="permlab_accessFineLocation" msgid="1191898061965273372">"posizione precisa (GPS e basata sulla rete)"</string>
- <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Consente all\'applicazione di ottenere la tua posizione esatta utilizzando il sistema GPS (Global Positioning System) o fonti di localizzazione delle reti come ripetitori di telefonia mobile e Wi-Fi. Questi servizi di localizzazione devono essere attivi e disponibili sul dispositivo per poter essere utilizzati dall\'applicazione. Le applicazioni potrebbero utilizzare questa autorizzazione per stabilire la tua posizione e potrebbero consumare ulteriore batteria."</string>
+ <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Consente all\'applicazione di ottenere la tua posizione esatta utilizzando il sistema GPS (Global Positioning System) o fonti di geolocalizzazione delle reti come ripetitori di telefonia mobile e Wi-Fi. Questi servizi di localizzazione devono essere attivi e disponibili sul dispositivo per poter essere utilizzati dall\'applicazione. Le applicazioni potrebbero utilizzare questa autorizzazione per stabilire la tua posizione e potrebbero consumare ulteriore batteria."</string>
<string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"posizione approssimativa (basata sulla rete)"</string>
- <string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Consente all\'applicazione di ottenere la tua posizione approssimativa. Questa posizione viene ottenuta da servizi di localizzazione utilizzando fonti di localizzazione delle reti come ripetitori di telefonia mobile e Wi-Fi. Questi servizi di localizzazione devono essere attivi e disponibili sul dispositivo per poter essere utilizzati dall\'applicazione. Le applicazioni potrebbero utilizzare questa autorizzazione per stabilire la tua posizione approssimativa."</string>
+ <string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Consente all\'applicazione di ottenere la tua posizione approssimativa. Questa posizione viene ottenuta da servizi di localizzazione utilizzando fonti di geolocalizzazione delle reti come ripetitori di telefonia mobile e Wi-Fi. Questi servizi di localizzazione devono essere attivi e disponibili sul dispositivo per poter essere utilizzati dall\'applicazione. Le applicazioni potrebbero utilizzare questa autorizzazione per stabilire la tua posizione approssimativa."</string>
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"accesso a SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Consente all\'applicazione l\'utilizzo di funzioni di basso livello SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"lettura buffer di frame"</string>
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Riavvia"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"La pagina all\'indirizzo \"<xliff:g id="TITLE">%s</xliff:g>\" indica:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Uscire da questa pagina?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Tocca OK per continuare o Annulla per rimanere nella pagina corrente."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Conferma"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Suggerimento. Tocca due volte per aumentare e diminuire lo zoom."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Compilazione autom."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 7556103..c9cee96 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"אתחל מחדש"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"בדף שבכתובת \'<xliff:g id="TITLE">%s</xliff:g>\' כתוב כך:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"לנווט אל מחוץ לדף זה?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"גע באפשרות \'אישור\' כדי להמשיך, או \'ביטול\' כדי להישאר בדף הנוכחי."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"אשר"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"טיפ: הקש פעמיים כדי להגדיל ולהקטין."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"מילוי אוטומטי"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index f104bfc..18e00df 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"再起動"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"<xliff:g id="TITLE">%s</xliff:g> のページ:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"このページから移動しますか?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"移動する場合は[OK]、現在のページに留まる場合は[キャンセル]をタップしてください。"</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"確認"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"ヒント: ダブルタップで拡大/縮小できます。"</string>
<string name="autofill_this_form" msgid="4616758841157816676">"自動入力"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index c89e9a0..cd078f6 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"다시 부팅"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"\'<xliff:g id="TITLE">%s</xliff:g>\' 페이지 내용:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"자바스크립트"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"다른 페이지를 탐색하시겠습니까?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"계속하려면 \'확인\'을 터치하고 현재 페이지에 그대로 있으려면 \'취소\'를 터치하세요."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"확인"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"도움말: 확대/축소하려면 두 번 탭합니다."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"자동완성"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index d73f037..f6b92f7 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Pakartotinai įkelti"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Puslapyje šiuo adresu: <xliff:g id="TITLE">%s</xliff:g>, teigiama:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Išeiti iš šio puslapio?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Palieskite „Gerai“, jei norite tęsti, arba palieskite „Atšaukti“, jei norite likti dabartiniame puslapyje."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Patvirtinti"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Patarimas: palieskite dukart, kad padidintumėte ar sumažintumėte mastelį."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Automatinis pildymas"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index b51e291..3df0713 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Atsāknēt"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Lapā <xliff:g id="TITLE">%s</xliff:g> ir teikts:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Vai doties prom no šīs lapas?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Pieskarieties Labi, lai turpinātu, vai Atcelt, lai paliktu pašreizējā lapā."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Apstiprināt"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Padoms. Divreiz pieskarieties, lai tuvinātu un tālinātu."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Automātiskā aizpilde"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index c19d8c5..5fd4bde 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"But semula"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Halaman di \'<xliff:g id="TITLE">%s</xliff:g>\' berkata:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Navigasi keluar dari halaman ini?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Sentuh OK untuk meneruskan, atau Batal untuk terus berada di halaman semasa."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Sahkan"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Petua: Ketik dua kali untuk mengezum masuk dan keluar."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Auto isi"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 82d3bf0..3866612 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Omstart"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Siden på «<xliff:g id="TITLE">%s</xliff:g>» sier:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Vil du navigere bort fra denne siden?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Trykk på OK for å fortsette eller på Avbryt for å bli værende på siden."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Bekreft"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Tips: Dobbelttrykk for å zoome inn og ut."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Autofyll"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index eb421ca..5ac5c3d 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Opnieuw opstarten"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"De pagina op \'<xliff:g id="TITLE">%s</xliff:g>\' meldt het volgende:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Wilt u deze pagina verlaten?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Kies \'OK\' om door te gaan of \'Annuleren\' om op de huidige pagina te blijven."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Bevestigen"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Tip: dubbeltik om in en uit te zoomen."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Autom. aanvullen"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index be314a3..abb5f3a 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Uruchom ponownie"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Komunikat ze strony pod adresem „<xliff:g id="TITLE">%s</xliff:g>”:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Czy opuścić tę stronę?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Dotknij OK, aby kontynuować, lub Anuluj, aby pozostać na tej stronie."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Potwierdź"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Wskazówka: dotknij dwukrotnie, aby powiększyć lub pomniejszyć."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Autouzupełnianie"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 3385bcd..80d3c78 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -144,7 +144,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Campainha ativada"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"A encerrar..."</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"O seu tablet irá encerrar."</string>
- <string name="shutdown_confirm" product="default" msgid="649792175242821353">"O seu telefone irá encerrar."</string>
+ <string name="shutdown_confirm" product="default" msgid="649792175242821353">"O seu telefone será encerrado."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Pretende encerrar?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Reiniciar no modo de segurança"</string>
<string name="reboot_safemode_confirm" msgid="55293944502784668">"Pretende reiniciar no modo de segurança? Se sim, irá desativar todas as aplicações de terceiros instaladas. Estas serão restauradas quando reiniciar novamente."</string>
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Reiniciar"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"A página em \"<xliff:g id="TITLE">%s</xliff:g>\" indica:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Navegar para outra página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Toque em OK para continuar ou Cancelar para permanecer na página atual."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Sugestão: toque duas vezes para aumentar ou diminuir o zoom."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Preenchimento Automático"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 2a2490c..3e4f70b 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -160,9 +160,9 @@
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Modo silencioso"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Som DESATIVADO"</string>
<string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"O som está ATIVADO"</string>
- <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo de avião"</string>
- <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modo de avião ATIVADO"</string>
- <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modo de avião DESATIVADO"</string>
+ <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo avião"</string>
+ <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modo avião ATIVADO"</string>
+ <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modo avião DESATIVADO"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo de segurança"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Reiniciar"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"A página em \"<xliff:g id="TITLE">%s</xliff:g>\" diz:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Deseja sair desta página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Selecione OK para continuar ou Cancelar para permanecer na página atual."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Dica: toque duas vezes para aumentar e diminuir o zoom."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Preench. aut."</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index 848c882..27a2bb9 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -1398,7 +1398,13 @@
<!-- no translation found for js_dialog_title (1987483977834603872) -->
<skip />
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <!-- no translation found for js_dialog_before_unload (730366588032430474) -->
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
<skip />
<string name="save_password_label" msgid="6860261758665825069">"Confermar"</string>
<!-- no translation found for double_tap_toast (4595046515400268881) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 7914a4a..56ce802 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -622,7 +622,7 @@
<string name="permlab_accessNotifications" msgid="7673416487873432268">"accesare notificări"</string>
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite aplicației să recupereze, să examineze și să șteargă notificări, inclusiv pe cele postate de alte aplicații."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"conectare la un serviciu de citire a notificărilor"</string>
- <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite proprietarului să se conecteze la interfața de nivel superior a unui serviciu de citire a notificărilor. Nu ar trebui să fie niciodată necesară pentru aplicațiile obișnuite."</string>
+ <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite proprietarului să se conecteze la interfața de nivel superior a unui serviciu de citire a notificărilor. În mod normal aplicațiile nu ar trebui să aibă nevoie de această permisiune."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Setaţi reguli pentru parolă"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Stabiliţi lungimea şi tipul de caractere permise în parolele pentru deblocarea ecranului."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitorizaţi încercările de deblocare a ecranului"</string>
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Reporniţi"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"La pagina de la „<xliff:g id="TITLE">%s</xliff:g>” apare:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Doriţi să părăsiţi această pagină?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Apăsaţi pe OK pentru a continua sau pe Anulaţi pentru a rămâne pe pagina curentă."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Confirmaţi"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Sfat: măriţi şi micşoraţi prin dublă atingere."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Automat"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 7ae1751..86c655d 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Перезагрузка"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Подтвердите действие на <xliff:g id="TITLE">%s</xliff:g>"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Покинуть эту страницу?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Нажмите \"ОК\", чтобы продолжить, или \"Отмена\", чтобы остаться."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Подтвердите"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Совет: нажмите дважды, чтобы увеличить и уменьшить масштаб."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Автозаполнение"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 0f46bb9..53029bd7 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Reštartovať"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Stránka „<xliff:g id="TITLE">%s</xliff:g>“ uvádza:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Chcete opustiť túto stránku?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Ak chcete pokračovať, dotknite sa tlačidla OK. Ak chcete zostať na stránke, dotknite sa tlačidla Zrušiť."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Potvrdiť"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Tip: Dvojitým klepnutím môžete zobrazenie priblížiť alebo oddialiť."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Aut.dop."</string>
@@ -1051,7 +1058,7 @@
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nedostatok ukladacieho priestoru"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektoré systémové funkcie nemusia fungovať"</string>
<string name="app_running_notification_title" msgid="8718335121060787914">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je spustená"</string>
- <string name="app_running_notification_text" msgid="4653586947747330058">"Dotknutím sa zobrazíte viac informácií alebo zastavíte aplikáciu."</string>
+ <string name="app_running_notification_text" msgid="4653586947747330058">"Dotykom si zobrazíte viac informácií alebo zastavíte aplikáciu."</string>
<string name="ok" msgid="5970060430562524910">"OK"</string>
<string name="cancel" msgid="6442560571259935130">"Zrušiť"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 55ac6a2..72503bf 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Znova zaženi"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Na strani na »<xliff:g id="TITLE">%s</xliff:g>« piše:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Ali se želite premakniti s te strani"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Če želite nadaljevati, se dotaknite »V redu«, če želite ostati na trenutni strani, izberite »Prekliči«."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Potrdi"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Nasvet: Tapnite dvakrat, če želite povečati ali pomanjšati."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Samoizp."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index f0cadc2..ffa0af1 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Поново покрени"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"На страници на адреси „<xliff:g id="TITLE">%s</xliff:g>“ пише следеће:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Желите ли да напустите ову страницу?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Додирните Потврди да бисте наставили или Откажи да бисте остали на тренутно отвореној страници."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Потврда"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Савет: Додирните двапут да бисте увећали и умањили приказ."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Аутом. поп."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 7510b89..0da1033 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Starta om"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"På sidan på <xliff:g id="TITLE">%s</xliff:g> står det:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Vill du lämna den här den här sidan?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Tryck på OK om du vill fortsätta eller på Avbryt om du vill vara kvar på den aktuella sidan."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Bekräfta"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Tips! Dubbelknacka om du vill zooma in eller ut."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Autofyll"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index a44a069..b685cc5 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -138,7 +138,7 @@
<string name="turn_on_radio" msgid="3912793092339962371">"Washa mtandao-hewa"</string>
<string name="turn_off_radio" msgid="8198784949987062346">"Zima pasiwaya"</string>
<string name="screen_lock" msgid="799094655496098153">"Funga skrini"</string>
- <string name="power_off" msgid="4266614107412865048">"Nishati imezimwa"</string>
+ <string name="power_off" msgid="4266614107412865048">"Zima simu"</string>
<string name="silent_mode_silent" msgid="319298163018473078">"Programu ya milio imezimwa"</string>
<string name="silent_mode_vibrate" msgid="7072043388581551395">"Mtetemo wa programu ya milio"</string>
<string name="silent_mode_ring" msgid="8592241816194074353">"Programu ya milio imewashwa"</string>
@@ -153,7 +153,7 @@
<string name="global_actions" product="tablet" msgid="408477140088053665">"Chaguo za kompyuta ndogo"</string>
<string name="global_actions" product="default" msgid="2406416831541615258">"Chaguo za simu"</string>
<string name="global_action_lock" msgid="2844945191792119712">"Funga skrini"</string>
- <string name="global_action_power_off" msgid="4471879440839879722">"Nishati imezimwa"</string>
+ <string name="global_action_power_off" msgid="4471879440839879722">"Zima simu"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Ripoti ya hitilafu"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Chukua ripoti ya hitilafu"</string>
<string name="bugreport_message" msgid="398447048750350456">"Hii itakusanya maelezo kuhusu hali yako ya sasa ya kifaa, ili kutuma ujumbe wa barua pepe. Itachukua muda mfupi kuanza ripoti ya hitilafu mpaka itakapokuwa tayari kutumwa; tafadhali vumilia."</string>
@@ -519,7 +519,7 @@
<string name="permlab_wakeLock" product="default" msgid="573480187941496130">"zuia simu dhidi ya kulala"</string>
<string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"Inaruhusu programu kuzuia kompyuta kibao kwenda kulala."</string>
<string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"Inaruhusu programu kuzuia simu isiende kulala."</string>
- <string name="permlab_devicePower" product="tablet" msgid="2787034722616350417">"Washa au zima kompyuta ndogo"</string>
+ <string name="permlab_devicePower" product="tablet" msgid="2787034722616350417">"Washa au zima kompyuta kibao"</string>
<string name="permlab_devicePower" product="default" msgid="4928622470980943206">"washa au zima simu"</string>
<string name="permdesc_devicePower" product="tablet" msgid="6689862878984631831">"Inaruhusu programu kuwasha au kuzima kompyuta kibao."</string>
<string name="permdesc_devicePower" product="default" msgid="6037057348463131032">"Inaruhusu programu kuwasha au kuzima simu."</string>
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Washa tena"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Ukurasa ulio \"<xliff:g id="TITLE">%s</xliff:g>\" unasema:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"HatiJava"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Toka kwa ukurasa huu?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Gusa Sawa ili kuendelea, au Ghairi ili kubaki kwenye ukurasa wa sasa."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Thibitisha"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Kidokezo: Gonga mara mbili ili kukuza ndani na nje."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Kujaza kiotomatiki"</string>
@@ -1060,7 +1067,7 @@
<string name="loading" msgid="7933681260296021180">"Inapakia…"</string>
<string name="capital_on" msgid="1544682755514494298">"Washa"</string>
<string name="capital_off" msgid="6815870386972805832">"ZIMA"</string>
- <string name="whichApplication" msgid="4533185947064773386">"Kamilisha kitendo kwa kutumia"</string>
+ <string name="whichApplication" msgid="4533185947064773386">"Kamilisha kitendo ukitumia"</string>
<string name="alwaysUse" msgid="4583018368000610438">"Tumia kama chaguo-msingi la kitendo hiki."</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"Futa chaguo-msingi katika mipangilio ya Mfumo > Apps > iliyopakuliwa."</string>
<string name="chooseActivity" msgid="7486876147751803333">"Chagua kitendo"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index e8eda68..e72b516 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"รีบูต"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"หน้าเว็บที่ \"<xliff:g id="TITLE">%s</xliff:g>\" ระบุว่า:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"ไปจากหน้าเว็บนี้หรือไม่"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"แตะ \"ตกลง\" เพื่อทำต่อ หรือ \"ยกเลิก\" เพื่ออยู่ที่หน้าเว็บปัจจุบัน"</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"ยืนยัน"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"เคล็ดลับ: แตะสองครั้งเพื่อขยายและย่อ"</string>
<string name="autofill_this_form" msgid="4616758841157816676">"ป้อนอัตโนมัติ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 93075b9..5a3a305 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"I-reboot"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Isinasaad ng pahina sa \"<xliff:g id="TITLE">%s</xliff:g>\" na:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Mag-navigate palayo mula sa pahinang ito?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Pindutin ang OK upang magpatuloy, o Kanselahin upang manatili sa kasalukuyang pahina."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Kumpirmahin"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Tip: Mag-double tap upang mag-zoom in at out."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Autofill"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 226d54d..2e9c305 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Yeniden başlat"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"\"<xliff:g id="TITLE">%s</xliff:g>\" adresindeki sayfada şunlar belirtiliyor:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Bu sayfadan ayrılıyor musunuz?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Devam etmek için Tamam\'ı, mevcut sayfada kalmak için İptal\'i tıklayın."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Onayla"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"İpucu: Yakınlaştırmak ve uzaklaştırmak için iki kez hafifçe vurun."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Otomatik Doldur"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index f5b9508..6e03610 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Перезав."</string>
<string name="js_dialog_title" msgid="1987483977834603872">"На сторінці за адресою \"<xliff:g id="TITLE">%s</xliff:g>\" написано:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"Javascript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Перейти з цієї сторінки?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Торкніться \"OK\", щоб продовжити, або \"Скасувати\", щоб залишитися на поточній сторінці."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Підтверд."</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Порада: двічі торкніться для збільшення чи зменшення."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Автозап."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 4198f43..783fc48 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Khởi động lại"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Trang tại \"<xliff:g id="TITLE">%s</xliff:g>\" cho biết:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Điều hướng khỏi trang này?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Chạm OK để tiếp tục hoặc Hủy để ở lại trang hiện tại."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Xác nhận"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Mẹo: Nhấn đúp để phóng to và thu nhỏ."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Tự động điền"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index f9bf43f..0f1e42f 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"重新启动"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"网址为“<xliff:g id="TITLE">%s</xliff:g>”的网页显示:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"要从此页面导航至其他页面吗?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"触摸“确定”继续,或触摸“取消”留在当前页面。"</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"确认"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"提示:点按两次可放大或缩小。"</string>
<string name="autofill_this_form" msgid="4616758841157816676">"自动填充"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index b96e6b3..9b0cb6d 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"重新開機"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"「<xliff:g id="TITLE">%s</xliff:g>」網頁指出:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"離開這個頁面?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"輕觸 [確定] 離開這個頁面,或輕觸 [取消] 停留在這個頁面。"</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"確認"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"提示:輕按兩下即可縮放。"</string>
<string name="autofill_this_form" msgid="4616758841157816676">"自動填入功能"</string>
@@ -1477,7 +1484,7 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,系統就會要求您透過電子郵件帳戶解除手機的鎖定狀態。"\n\n"請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"移除"</string>
- <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"要將音量調高到建議等級以上嗎?"\n"長時間聆聽偏高音量可能會損害您的聽力。"</string>
+ <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"要將音量調高到建議等級以上嗎?"\n"長時間聆聽高音量可能會損害您的聽力。"</string>
<string name="continue_to_enable_accessibility" msgid="1626427372316070258">"持續用兩指按住即可啟用協助工具。"</string>
<string name="accessibility_enabled" msgid="1381972048564547685">"協助工具已啟用。"</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"協助工具已取消。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index eeb9165..41bd5eb 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -864,7 +864,14 @@
<string name="factorytest_reboot" msgid="6320168203050791643">"Qalisa kabusha"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"Ikhasi eliku <xliff:g id="TITLE">%s</xliff:g> lithi:"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"i-JavaScript"</string>
- <string name="js_dialog_before_unload" msgid="730366588032430474">"Phuma kuleli khasi? "\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n" Thinta KULUNGILE ukuqhubeka, noma Khansela ukuhlala kuleli khasi."</string>
+ <!-- no translation found for js_dialog_before_unload_title (2619376555525116593) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_positive_button (3112752010600484130) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload_negative_button (5614861293026099715) -->
+ <skip />
+ <!-- no translation found for js_dialog_before_unload (3468816357095378590) -->
+ <skip />
<string name="save_password_label" msgid="6860261758665825069">"Qinisekisa"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Ithiphu: thepha kabili ukusondeza ngaphandle nangaphakathi."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Ukugcwalisa Ngokuzenzakalelayo"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8c7a374..939d435 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4378,6 +4378,28 @@
</declare-styleable>
<!-- ========================== -->
+ <!-- State class attributes -->
+ <!-- ========================== -->
+ <eat-comment />
+
+ <declare-styleable name="Scene">
+ <attr name="layout" />
+ </declare-styleable>
+
+ <declare-styleable name="Transition">
+ <attr name="duration" />
+ <attr name="startOffset" />
+ <attr name="interpolator" />
+ <attr name="targetID" format="reference" />
+ </declare-styleable>
+
+ <declare-styleable name="TransitionManager">
+ <attr name="transition" format="reference" />
+ <attr name="fromScene" format="reference" />
+ <attr name="toScene" format="reference" />
+ </declare-styleable>
+
+ <!-- ========================== -->
<!-- ValueAnimator class attributes -->
<!-- ========================== -->
<eat-comment />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cc50d8a..84e300a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -345,6 +345,12 @@
A value of -1 means no change in orientation by default. -->
<integer name="config_carDockRotation">-1</integer>
+ <!-- The number of degrees to rotate the display when the device has HDMI connected
+ but is not in a dock. A value of -1 means no change in orientation by default.
+ Use -1 except on older devices whose Hardware Composer HAL does not
+ provide full support for multiple displays. -->
+ <integer name="config_undockedHdmiRotation">-1</integer>
+
<!-- Control the default UI mode type to use when there is no other type override
happening. One of the following values (See Configuration.java):
1 UI_MODE_TYPE_NORMAL
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 03d7207..1f088a5 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2058,5 +2058,9 @@
<eat-comment />
<public type="attr" name="keyset" />
+ <public type="attr" name="targetID" />
+ <public type="attr" name="fromScene" />
+ <public type="attr" name="toScene" />
+ <public type="attr" name="transition" />
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ae02d73..f85abf9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1385,6 +1385,7 @@
<java-symbol type="integer" name="config_screenBrightnessSettingDefault" />
<java-symbol type="integer" name="config_screenBrightnessDim" />
<java-symbol type="integer" name="config_shutdownBatteryTemperature" />
+ <java-symbol type="integer" name="config_undockedHdmiRotation" />
<java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
<java-symbol type="layout" name="am_compat_mode_dialog" />
<java-symbol type="layout" name="launch_warning" />
diff --git a/data/keyboards/Vendor_0079_Product_0011.kl b/data/keyboards/Vendor_0079_Product_0011.kl
new file mode 100644
index 0000000..2ae2a01
--- /dev/null
+++ b/data/keyboards/Vendor_0079_Product_0011.kl
@@ -0,0 +1,23 @@
+# 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.
+
+# Classic NES Controller
+
+key 289 BUTTON_A
+key 290 BUTTON_B
+key 297 BUTTON_START
+key 296 BUTTON_SELECT
+
+axis 0x00 HAT_X
+axis 0x01 HAT_Y
diff --git a/data/keyboards/Vendor_045e_Product_028e.kl b/data/keyboards/Vendor_045e_Product_028e.kl
index 99f046a..ca6fa59 100644
--- a/data/keyboards/Vendor_045e_Product_028e.kl
+++ b/data/keyboards/Vendor_045e_Product_028e.kl
@@ -22,9 +22,9 @@
key 308 BUTTON_Y
key 310 BUTTON_L1
key 311 BUTTON_R1
-key 314 BUTTON_SELECT
+key 314 BUTTON_BACK
key 315 BUTTON_START
-key 316 BUTTON_MODE
+key 316 BUTTON_HOME
key 317 BUTTON_THUMBL
key 318 BUTTON_THUMBR
diff --git a/data/keyboards/Vendor_046d_Product_c219.kl b/data/keyboards/Vendor_046d_Product_c219.kl
new file mode 100644
index 0000000..431dd03
--- /dev/null
+++ b/data/keyboards/Vendor_046d_Product_c219.kl
@@ -0,0 +1,35 @@
+# 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.
+
+# Logitech Logitech Cordless RumblePad 2
+
+key 305 BUTTON_A
+key 306 BUTTON_B
+key 304 BUTTON_X
+key 307 BUTTON_Y
+key 308 BUTTON_L1
+key 309 BUTTON_R1
+key 310 BUTTON_L2
+key 311 BUTTON_R2
+key 313 BUTTON_START
+key 312 BUTTON_BACK
+key 314 BUTTON_THUMBL
+key 315 BUTTON_THUMBR
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x02 Z
+axis 0x05 RZ
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/data/keyboards/Vendor_046d_Product_c21f.kl b/data/keyboards/Vendor_046d_Product_c21f.kl
new file mode 100644
index 0000000..981d864
--- /dev/null
+++ b/data/keyboards/Vendor_046d_Product_c21f.kl
@@ -0,0 +1,36 @@
+# 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.
+
+# Logitech Wireless Gamepad F710
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 315 BUTTON_START
+key 314 BUTTON_BACK
+key 316 BUTTON_HOME
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x03 Z
+axis 0x04 RZ
+axis 0x05 RTRIGGER
+axis 0x02 LTRIGGER
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/data/keyboards/Vendor_054c_Product_0268.kl b/data/keyboards/Vendor_054c_Product_0268.kl
index f8ac6a3..62c5f4d5 100644
--- a/data/keyboards/Vendor_054c_Product_0268.kl
+++ b/data/keyboards/Vendor_054c_Product_0268.kl
@@ -23,10 +23,10 @@
key 0x120 BUTTON_SELECT
key 0x123 BUTTON_START
-key 0x12f BUTTON_A
-key 0x12c BUTTON_B
-key 0x12e BUTTON_X
-key 0x12d BUTTON_Y
+key 0x12e BUTTON_A
+key 0x12d BUTTON_B
+key 0x12f BUTTON_X
+key 0x12c BUTTON_Y
key 0x12a BUTTON_L1
key 0x12b BUTTON_R1
key 0x128 BUTTON_L2
@@ -35,7 +35,7 @@
key 0x122 BUTTON_THUMBR
# PS key
-key 0x2d0 BUTTON_1
+key 0x2d0 BUTTON_HOME
# Left Analog Stick
axis 0x00 X
diff --git a/data/keyboards/Vendor_0583_Product_2060.kl b/data/keyboards/Vendor_0583_Product_2060.kl
new file mode 100644
index 0000000..92c8a14
--- /dev/null
+++ b/data/keyboards/Vendor_0583_Product_2060.kl
@@ -0,0 +1,27 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ION GO PAD
+
+key 288 BUTTON_A
+key 289 BUTTON_B
+key 290 BUTTON_X
+key 291 BUTTON_Y
+key 294 BUTTON_L1
+key 295 BUTTON_R1
+key 292 BUTTON_L2
+key 293 BUTTON_R2
+
+axis 0x00 HAT_X
+axis 0x01 HAT_Y
diff --git a/data/keyboards/Vendor_0a5c_Product_8502.kl b/data/keyboards/Vendor_0a5c_Product_8502.kl
new file mode 100644
index 0000000..0084969
--- /dev/null
+++ b/data/keyboards/Vendor_0a5c_Product_8502.kl
@@ -0,0 +1,33 @@
+# 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.
+
+# Snakebyte
+
+key 289 BUTTON_A
+key 290 BUTTON_B
+key 288 BUTTON_X
+key 291 BUTTON_Y
+key 292 BUTTON_L1
+key 293 BUTTON_R1
+key 294 BUTTON_L2
+key 295 BUTTON_R2
+key 297 BUTTON_START
+key 296 BUTTON_SELECT
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x02 Z
+axis 0x05 RZ
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/data/keyboards/Vendor_1038_Product_1412.kl b/data/keyboards/Vendor_1038_Product_1412.kl
new file mode 100644
index 0000000..551b0bd
--- /dev/null
+++ b/data/keyboards/Vendor_1038_Product_1412.kl
@@ -0,0 +1,31 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Steelseries Free
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 315 BUTTON_START
+key 316 BUTTON_SELECT
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x02 Z
+axis 0x05 RZ
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/data/keyboards/Vendor_12bd_Product_d015.kl b/data/keyboards/Vendor_12bd_Product_d015.kl
new file mode 100644
index 0000000..557d62f
--- /dev/null
+++ b/data/keyboards/Vendor_12bd_Product_d015.kl
@@ -0,0 +1,27 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Hitgaming SNES Retro
+
+key 306 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 304 BUTTON_Y
+key 308 BUTTON_L1
+key 309 BUTTON_R1
+key 313 BUTTON_START
+key 312 BUTTON_SELECT
+
+axis 0x00 HAT_X
+axis 0x01 HAT_Y
diff --git a/data/keyboards/Vendor_1689_Product_fd00.kl b/data/keyboards/Vendor_1689_Product_fd00.kl
new file mode 100644
index 0000000..6ce14ed
--- /dev/null
+++ b/data/keyboards/Vendor_1689_Product_fd00.kl
@@ -0,0 +1,38 @@
+# 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.
+
+# Razer Onza Tournament Edition
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 307 BUTTON_L1
+key 308 BUTTON_R1
+key 315 BUTTON_START
+key 314 BUTTON_BACK
+key 316 BUTTON_HOME
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+key 706 DPAD_UP
+key 705 DPAD_RIGHT
+key 707 DPAD_DOWN
+key 704 DPAD_LEFT
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x03 Z
+axis 0x04 RZ
+axis 0x05 RTRIGGER
+axis 0x02 LTRIGGER
diff --git a/data/keyboards/Vendor_1689_Product_fd01.kl b/data/keyboards/Vendor_1689_Product_fd01.kl
new file mode 100644
index 0000000..8144515
--- /dev/null
+++ b/data/keyboards/Vendor_1689_Product_fd01.kl
@@ -0,0 +1,36 @@
+# 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.
+
+# Razer Xbox 360 Gamepad
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 308 BUTTON_R1
+key 315 BUTTON_START
+key 314 BUTTON_BACK
+key 316 BUTTON_HOME
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x03 Z
+axis 0x04 RZ
+axis 0x05 RTRIGGER
+axis 0x02 LTRIGGER
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/data/keyboards/Vendor_1689_Product_fe00.kl b/data/keyboards/Vendor_1689_Product_fe00.kl
new file mode 100644
index 0000000..90fe4af
--- /dev/null
+++ b/data/keyboards/Vendor_1689_Product_fe00.kl
@@ -0,0 +1,36 @@
+# 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.
+
+# Razer Sabertooth Elite
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 315 BUTTON_START
+key 314 BUTTON_BACK
+key 316 BUTTON_HOME
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x03 Z
+axis 0x04 RZ
+axis 0x05 RTRIGGER
+axis 0x02 LTRIGGER
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/data/keyboards/Vendor_1bad_Product_f016.kl b/data/keyboards/Vendor_1bad_Product_f016.kl
new file mode 100644
index 0000000..b72fd5ce
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f016.kl
@@ -0,0 +1,36 @@
+# 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.
+
+# Madcatz Gamepad
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 315 BUTTON_START
+key 314 BUTTON_BACK
+key 316 BUTTON_HOME
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x03 Z
+axis 0x04 RZ
+axis 0x05 RTRIGGER
+axis 0x02 LTRIGGER
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/data/keyboards/Vendor_1bad_Product_f023.kl b/data/keyboards/Vendor_1bad_Product_f023.kl
new file mode 100644
index 0000000..c1588b2
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f023.kl
@@ -0,0 +1,35 @@
+# 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.
+
+# Mad Catz MLG GamePad for Xbox 360
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 315 BUTTON_START
+key 314 BUTTON_BACK
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x03 Z
+axis 0x04 RZ
+axis 0x05 RTRIGGER
+axis 0x02 LTRIGGER
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/data/keyboards/Vendor_1bad_Product_f027.kl b/data/keyboards/Vendor_1bad_Product_f027.kl
new file mode 100644
index 0000000..ea0aa7a
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f027.kl
@@ -0,0 +1,36 @@
+# 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.
+
+# MadCatz FPS Pro
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 315 BUTTON_START
+key 314 BUTTON_BACK
+key 316 BUTTON_HOME
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x03 Z
+axis 0x04 RZ
+axis 0x05 RTRIGGER
+axis 0x02 LTRIGGER
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/data/keyboards/Vendor_1bad_Product_f036.kl b/data/keyboards/Vendor_1bad_Product_f036.kl
new file mode 100644
index 0000000..8cd906a
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f036.kl
@@ -0,0 +1,36 @@
+# 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.
+
+# MadCatz Generic XBox Controller
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 315 BUTTON_START
+key 314 BUTTON_BACK
+key 316 BUTTON_HOME
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x03 Z
+axis 0x04 RZ
+axis 0x05 RTRIGGER
+axis 0x02 LTRIGGER
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/data/keyboards/Vendor_1d79_Product_0009.kl b/data/keyboards/Vendor_1d79_Product_0009.kl
new file mode 100644
index 0000000..78fe2cd
--- /dev/null
+++ b/data/keyboards/Vendor_1d79_Product_0009.kl
@@ -0,0 +1,36 @@
+# 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.
+
+# Nyko Playpad / Playpad Pro
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 315 BUTTON_START
+key 158 BUTTON_BACK
+key 172 BUTTON_HOME
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x02 Z
+axis 0x05 RZ
+axis 0x09 RTRIGGER
+axis 0x0a LTRIGGER
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/data/keyboards/Vendor_2378_Product_100a.kl b/data/keyboards/Vendor_2378_Product_100a.kl
new file mode 100644
index 0000000..d9cd171
--- /dev/null
+++ b/data/keyboards/Vendor_2378_Product_100a.kl
@@ -0,0 +1,35 @@
+# 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.
+
+# OnLive, Inc. OnLive Wireless Controller
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+key 315 BUTTON_START
+key 314 BUTTON_SELECT
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+axis 0x00 X
+axis 0x01 Y
+axis 0x03 Z
+axis 0x04 RZ
+axis 0x05 RTRIGGER
+axis 0x02 LTRIGGER
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/docs/html/images/mediadrm_decryption_sequence.png b/docs/html/images/mediadrm_decryption_sequence.png
new file mode 100644
index 0000000..2bd95ea
--- /dev/null
+++ b/docs/html/images/mediadrm_decryption_sequence.png
Binary files differ
diff --git a/docs/html/images/mediadrm_overview.png b/docs/html/images/mediadrm_overview.png
new file mode 100644
index 0000000..dd66bce
--- /dev/null
+++ b/docs/html/images/mediadrm_overview.png
Binary files differ
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index ad7edb1..c277c24 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -237,6 +237,8 @@
}
bool mergeAllowed() {
+ if (!state.mMatrix.isPureTranslate()) return false;
+
// checks that we're unclipped, and srcover
const Rect& opBounds = state.mBounds;
return fabs(opBounds.getWidth() - mLocalBounds.getWidth()) < 0.1 &&
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 6872278..31fbc4a 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -29,25 +29,69 @@
import android.util.Log;
/**
- * MediaDrm can be used in conjunction with {@link android.media.MediaCrypto}
- * to obtain keys for decrypting protected media data.
- *
- * Crypto schemes are assigned 16 byte UUIDs,
- * the method {@link #isCryptoSchemeSupported} can be used to query if a given
- * scheme is supported on the device.
- *
+ * MediaDrm can be used to obtain keys for decrypting protected media streams, in
+ * conjunction with {@link android.media.MediaCrypto}. The MediaDrm APIs
+ * are designed to support the ISO/IEC 23001-7: Common Encryption standard, but
+ * may also be used to implement other encryption schemes.
+ * <p>
+ * Encrypted content is prepared using an encryption server and stored in a content
+ * library. The encrypted content is streamed or downloaded from the content library to
+ * client devices via content servers. Licenses to view the content are obtained from
+ * a License Server.
+ * <p>
+ * <p><img src="../../../images/mediadrm_overview.png"
+ * alt="MediaDrm Overview diagram"
+ * border="0" /></p>
+ * <p>
+ * Keys are requested from the license server using a key request. The key
+ * response is delivered to the client app, which provides the response to the
+ * MediaDrm API.
+ * <p>
+ * A Provisioning server may be required to distribute device-unique credentials to
+ * the devices.
+ * <p>
+ * Enforcing requirements related to the number of devices that may play content
+ * simultaneously can be performed either through key renewal or using the secure
+ * stop methods.
+ * <p>
+ * The following sequence diagram shows the interactions between the objects
+ * involved while playing back encrypted content:
+ * <p>
+ * <p><img src="../../../images/mediadrm_decryption_sequence.png"
+ * alt="MediaDrm Overview diagram"
+ * border="0" /></p>
+ * <p>
+ * The app first constructs {@link android.media.MediaExtractor} and
+ * {@link android.media.MediaCodec} objects. It accesses the DRM-scheme-identifying UUID,
+ * typically from metadata in the content, and uses this UUID to construct an instance
+ * of a MediaDrm object that is able to support the DRM scheme required by the content.
+ * Crypto schemes are assigned 16 byte UUIDs. The method {@link #isCryptoSchemeSupported}
+ * can be used to query if a given scheme is supported on the device.
+ * <p>
+ * The app calls {@link #openSession} to generate a sessionId that will uniquely identify
+ * the session in subsequent interactions. The app next uses the MediaDrm object to
+ * obtain a key request message and send it to the license server, then provide
+ * the server's response to the MediaDrm object.
+ * <p>
+ * Once the app has a sessionId, it can construct a MediaCrypto object from the UUID and
+ * sessionId. The MediaCrypto object is registered with the MediaCodec in the
+ * {@link MediaCodec.#configure} method to enable the codec to decrypt content.
+ * <p>
+ * When the app has constructed {@link android.media.MediaExtractor},
+ * {@link android.media.MediaCodec} and {@link android.media.MediaCrypto} objects,
+ * it proceeds to pull samples from the extractor and queue them into the decoder. For
+ * encrypted content, the samples returned from the extractor remain encrypted, they
+ * are only decrypted when the samples are delivered to the decoder.
+ * <p>
* <a name="Callbacks"></a>
* <h3>Callbacks</h3>
- * <p>Applications may want to register for informational events in order
- * to be informed of some internal state update during playback or streaming.
+ * <p>Applications should register for informational events in order
+ * to be informed of key state updates during playback or streaming.
* Registration for these events is done via a call to
- * {@link #setOnEventListener(OnInfoListener)}setOnInfoListener,
- * In order to receive the respective callback
- * associated with this listener, applications are required to create
+ * {@link #setOnEventListener}. In order to receive the respective
+ * callback associated with this listener, applications are required to create
* MediaDrm objects on a thread with its own Looper running (main UI
* thread by default has a Looper running).
- *
- * @hide -- don't expose yet
*/
public final class MediaDrm {
@@ -116,7 +160,7 @@
/**
* Interface definition for a callback to be invoked when a drm event
- * occurs.
+ * occurs
*/
public interface OnEventListener
{
@@ -132,10 +176,30 @@
void onEvent(MediaDrm md, byte[] sessionId, int event, int extra, byte[] data);
}
- public static final int MEDIA_DRM_EVENT_PROVISION_REQUIRED = 1;
- public static final int MEDIA_DRM_EVENT_KEY_REQUIRED = 2;
- public static final int MEDIA_DRM_EVENT_KEY_EXPIRED = 3;
- public static final int MEDIA_DRM_EVENT_VENDOR_DEFINED = 4;
+ /**
+ * This event type indicates that the app needs to request a certificate from
+ * the provisioning server. The request message data is obtained using
+ * {@link #getProvisionRequest}
+ */
+ public static final int EVENT_PROVISION_REQUIRED = 1;
+
+ /**
+ * This event type indicates that the app needs to request keys from a license
+ * server. The request message data is obtained using {@link #getKeyRequest}.
+ */
+ public static final int EVENT_KEY_REQUIRED = 2;
+
+ /**
+ * This event type indicates that the licensed usage duration for keys in a session
+ * has expired. The keys are no longer valid.
+ */
+ public static final int EVENT_KEY_EXPIRED = 3;
+
+ /**
+ * This event may indicate some specific vendor-defined condition, see your
+ * DRM provider documentation for details
+ */
+ public static final int EVENT_VENDOR_DEFINED = 4;
private static final int DRM_EVENT = 200;
@@ -183,7 +247,7 @@
}
/*
- * Called from native code when an interesting event happens. This method
+ * This method is called from native code when an event occurs. This method
* just uses the EventHandler system to post the event back to the main app thread.
* We use a weak reference to the original MediaPlayer object so that the native
* code is safe from the object disappearing from underneath it. (This is
@@ -203,89 +267,117 @@
}
/**
- * Open a new session with the MediaDrm object. A session ID is returned.
+ * Open a new session with the MediaDrm object. A session ID is returned.
*/
- public native byte[] openSession() throws MediaDrmException;
+ public native byte[] openSession();
/**
- * Close a session on the MediaDrm object that was previously opened
- * with {@link #openSession}.
+ * Close a session on the MediaDrm object that was previously opened
+ * with {@link #openSession}.
*/
- public native void closeSession(byte[] sessionId) throws MediaDrmException;
+ public native void closeSession(byte[] sessionId);
- public static final int MEDIA_DRM_KEY_TYPE_STREAMING = 1;
- public static final int MEDIA_DRM_KEY_TYPE_OFFLINE = 2;
- public static final int MEDIA_DRM_KEY_TYPE_RELEASE = 3;
+ /**
+ * This key request type species that the keys will be for online use, they will
+ * not be saved to the device for subsequent use when the device is not connected
+ * to a network.
+ */
+ public static final int KEY_TYPE_STREAMING = 1;
- public final class KeyRequest {
- public KeyRequest() {}
- public byte[] data;
- public String defaultUrl;
+ /**
+ * This key request type specifies that the keys will be for offline use, they
+ * will be saved to the device for use when the device is not connected to a network.
+ */
+ public static final int KEY_TYPE_OFFLINE = 2;
+
+ /**
+ * This key request type specifies that previously saved offline keys should be released.
+ */
+ public static final int KEY_TYPE_RELEASE = 3;
+
+ /**
+ * Contains the opaque data an app uses to request keys from a license server
+ */
+ public final static class KeyRequest {
+ KeyRequest() {}
+
+ /**
+ * Get the opaque message data
+ */
+ public byte[] getData() { return mData; }
+
+ /**
+ * Get the default URL to use when sending the key request message to a
+ * server, if known. The app may prefer to use a different license
+ * server URL from other sources.
+ */
+ public String getDefaultUrl() { return mDefaultUrl; }
+
+ private byte[] mData;
+ private String mDefaultUrl;
};
/**
* A key request/response exchange occurs between the app and a license server
* to obtain or release keys used to decrypt encrypted content.
+ * <p>
* getKeyRequest() is used to obtain an opaque key request byte array that is
* delivered to the license server. The opaque key request byte array is returned
* in KeyRequest.data. The recommended URL to deliver the key request to is
* returned in KeyRequest.defaultUrl.
- *
+ * <p>
* After the app has received the key request response from the server,
* it should deliver to the response to the DRM engine plugin using the method
* {@link #provideKeyResponse}.
*
* @param scope may be a sessionId or a keySetId, depending on the specified keyType.
- * When the keyType is MEDIA_DRM_KEY_TYPE_STREAMING or MEDIA_DRM_KEY_TYPE_OFFLINE,
+ * When the keyType is KEY_TYPE_STREAMING or KEY_TYPE_OFFLINE,
* scope should be set to the sessionId the keys will be provided to. When the keyType
- * is MEDIA_DRM_KEY_TYPE_RELEASE, scope should be set to the keySetId of the keys
+ * is KEY_TYPE_RELEASE, scope should be set to the keySetId of the keys
* being released. Releasing keys from a device invalidates them for all sessions.
* @param init container-specific data, its meaning is interpreted based on the
* mime type provided in the mimeType parameter. It could contain, for example,
* the content ID, key ID or other data obtained from the content metadata that is
* required in generating the key request. init may be null when keyType is
- * MEDIA_DRM_KEY_TYPE_RELEASE.
+ * KEY_TYPE_RELEASE.
* @param mimeType identifies the mime type of the content
* @param keyType specifes the type of the request. The request may be to acquire
* keys for streaming or offline content, or to release previously acquired
* keys, which are identified by a keySetId.
-
* @param optionalParameters are included in the key request message to
* allow a client application to provide additional message parameters to the server.
*/
public native KeyRequest getKeyRequest(byte[] scope, byte[] init,
String mimeType, int keyType,
- HashMap<String, String> optionalParameters)
- throws MediaDrmException;
+ HashMap<String, String> optionalParameters);
+
/**
* A key response is received from the license server by the app, then it is
* provided to the DRM engine plugin using provideKeyResponse. The byte array
* returned is a keySetId that can be used to later restore the keys to a new
- * session with the method {@link restoreKeys}, enabling offline key use.
+ * session with the method {@link #restoreKeys}, enabling offline key use.
*
* @param sessionId the session ID for the DRM session
* @param response the byte array response from the server
*/
- public native byte[] provideKeyResponse(byte[] sessionId, byte[] response)
- throws MediaDrmException;
+ public native byte[] provideKeyResponse(byte[] sessionId, byte[] response);
/**
* Restore persisted offline keys into a new session. keySetId identifies the
- * keys to load, obtained from a prior call to {@link provideKeyResponse}.
+ * keys to load, obtained from a prior call to {@link #provideKeyResponse}.
*
* @param sessionId the session ID for the DRM session
* @param keySetId identifies the saved key set to restore
*/
- public native void restoreKeys(byte[] sessionId, byte[] keySetId)
- throws MediaDrmException;
+ public native void restoreKeys(byte[] sessionId, byte[] keySetId);
/**
* Remove the current keys from a session.
*
* @param sessionId the session ID for the DRM session
*/
- public native void removeKeys(byte[] sessionId) throws MediaDrmException;
+ public native void removeKeys(byte[] sessionId);
/**
* Request an informative description of the key status for the session. The status is
@@ -296,25 +388,41 @@
*
* @param sessionId the session ID for the DRM session
*/
- public native HashMap<String, String> queryKeyStatus(byte[] sessionId)
- throws MediaDrmException;
+ public native HashMap<String, String> queryKeyStatus(byte[] sessionId);
- public final class ProvisionRequest {
- public ProvisionRequest() {}
- public byte[] data;
- public String defaultUrl;
+ /**
+ * Contains the opaque data an app uses to request a certificate from a provisioning
+ * server
+ */
+ public final static class ProvisionRequest {
+ ProvisionRequest() {}
+
+ /**
+ * Get the opaque message data
+ */
+ public byte[] getData() { return mData; }
+
+ /**
+ * Get the default URL to use when sending the provision request
+ * message to a server, if known. The app may prefer to use a different
+ * provisioning server URL obtained from other sources.
+ */
+ public String getDefaultUrl() { return mDefaultUrl; }
+
+ private byte[] mData;
+ private String mDefaultUrl;
}
/**
* A provision request/response exchange occurs between the app and a provisioning
* server to retrieve a device certificate. If provisionining is required, the
- * MEDIA_DRM_EVENT_PROVISION_REQUIRED event will be sent to the event handler.
+ * EVENT_PROVISION_REQUIRED event will be sent to the event handler.
* getProvisionRequest is used to obtain the opaque provision request byte array that
* should be delivered to the provisioning server. The provision request byte array
* is returned in ProvisionRequest.data. The recommended URL to deliver the provision
* request to is returned in ProvisionRequest.defaultUrl.
*/
- public native ProvisionRequest getProvisionRequest() throws MediaDrmException;
+ public native ProvisionRequest getProvisionRequest();
/**
* After a provision response is received by the app, it is provided to the DRM
@@ -323,92 +431,91 @@
* @param response the opaque provisioning response byte array to provide to the
* DRM engine plugin.
*/
- public native void provideProvisionResponse(byte[] response)
- throws MediaDrmException;
+ public native void provideProvisionResponse(byte[] response);
/**
- * A means of enforcing the contractual requirement for a concurrent stream limit
- * per subscriber across devices is provided via SecureStop. SecureStop is a means
- * of securely monitoring the lifetime of sessions. Since playback on a device can
- * be interrupted due to reboot, power failure, etc. a means of persisting the
- * lifetime information on the device is needed.
- *
- * A signed version of the sessionID is written to persistent storage on the device
- * when each MediaCrypto object is created. The sessionID is signed by the device
- * private key to prevent tampering.
- *
+ * A means of enforcing limits on the number of concurrent streams per subscriber
+ * across devices is provided via SecureStop. This is achieved by securely
+ * monitoring the lifetime of sessions.
+ * <p>
+ * Information from the server related to the current playback session is written
+ * to persistent storage on the device when each MediaCrypto object is created.
+ * <p>
* In the normal case, playback will be completed, the session destroyed and the
- * Secure Stops will be queried. The App queries secure stops and forwards the
+ * Secure Stops will be queried. The app queries secure stops and forwards the
* secure stop message to the server which verifies the signature and notifies the
* server side database that the session destruction has been confirmed. The persisted
* record on the client is only removed after positive confirmation that the server
* received the message using releaseSecureStops().
*/
- public native List<byte[]> getSecureStops() throws MediaDrmException;
+ public native List<byte[]> getSecureStops();
/**
* Process the SecureStop server response message ssRelease. After authenticating
- * the message, remove the SecureStops identiied in the response.
+ * the message, remove the SecureStops identified in the response.
*
* @param ssRelease the server response indicating which secure stops to release
*/
- public native void releaseSecureStops(byte[] ssRelease)
- throws MediaDrmException;
+ public native void releaseSecureStops(byte[] ssRelease);
/**
- * Read a DRM engine plugin property value, given the property name string. There are
- * several forms of property access functions, depending on the data type returned.
- *
+ * String property name: identifies the maker of the DRM engine plugin
+ */
+ public static final String PROPERTY_VENDOR = "vendor";
+
+ /**
+ * String property name: identifies the version of the DRM engine plugin
+ */
+ public static final String PROPERTY_VERSION = "version";
+
+ /**
+ * String property name: describes the DRM engine plugin
+ */
+ public static final String PROPERTY_DESCRIPTION = "description";
+
+ /**
+ * String property name: a comma-separated list of cipher and mac algorithms
+ * supported by CryptoSession. The list may be empty if the DRM engine
+ * plugin does not support CryptoSession operations.
+ */
+ public static final String PROPERTY_ALGORITHM = "algorithm";
+
+ /**
+ * Read a DRM engine plugin String property value, given the property name string.
+ * <p>
* Standard fields names are:
- * vendor String - identifies the maker of the DRM engine plugin
- * version String - identifies the version of the DRM engine plugin
- * description String - describes the DRM engine plugin
- * deviceUniqueId byte[] - The device unique identifier is established during device
- * provisioning and provides a means of uniquely identifying
- * each device
- * algorithms String - a comma-separate list of cipher and mac algorithms supported
- * by CryptoSession. The list may be empty if the DRM engine
- * plugin does not support CryptoSession operations.
+ * {@link #PROPERTY_VENDOR}, {@link #PROPERTY_VERSION},
+ * {@link #PROPERTY_DESCRIPTION}, {@link #PROPERTY_ALGORITHM}
*/
- public native String getPropertyString(String propertyName)
- throws MediaDrmException;
+ public native String getPropertyString(String propertyName);
- public native byte[] getPropertyByteArray(String propertyName)
- throws MediaDrmException;
/**
- * Write a DRM engine plugin property value. There are several forms of
- * property setting functions, depending on the data type being set.
+ * The device unique identifier is established during device provisioning and
+ * provides a means of uniquely identifying each device
*/
- public native void setPropertyString(String propertyName, String value)
- throws MediaDrmException;
-
- public native void setPropertyByteArray(String propertyName, byte[] value)
- throws MediaDrmException;
+ public static final String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
/**
- * In addition to supporting decryption of DASH Common Encrypted Media, the
- * MediaDrm APIs provide the ability to securely deliver session keys from
- * an operator's session key server to a client device, based on the factory-installed
- * root of trust, and provide the ability to do encrypt, decrypt, sign and verify
- * with the session key on arbitrary user data.
- *
- * The CryptoSession class implements generic encrypt/decrypt/sign/verify methods
- * based on the established session keys. These keys are exchanged using the
- * getKeyRequest/provideKeyResponse methods.
- *
- * Applications of this capability could include securing various types of
- * purchased or private content, such as applications, books and other media,
- * photos or media delivery protocols.
- *
- * Operators can create session key servers that are functionally similar to a
- * license key server, except that instead of receiving license key requests and
- * providing encrypted content keys which are used specifically to decrypt A/V media
- * content, the session key server receives session key requests and provides
- * encrypted session keys which can be used for general purpose crypto operations.
+ * Read a DRM engine plugin byte array property value, given the property name string.
+ * <p>
+ * Standard fields names are {@link #PROPERTY_DEVICE_UNIQUE_ID}
*/
+ public native byte[] getPropertyByteArray(String propertyName);
+
+
+ /**
+ * Set a DRM engine plugin String property value.
+ */
+ public native void setPropertyString(String propertyName, String value);
+
+ /**
+ * Set a DRM engine plugin byte array property value.
+ */
+ public native void setPropertyByteArray(String propertyName, byte[] value);
+
private static final native void setCipherAlgorithmNative(MediaDrm drm, byte[] sessionId,
String algorithm);
@@ -429,61 +536,112 @@
byte[] keyId, byte[] message,
byte[] signature);
+ /**
+ * In addition to supporting decryption of DASH Common Encrypted Media, the
+ * MediaDrm APIs provide the ability to securely deliver session keys from
+ * an operator's session key server to a client device, based on the factory-installed
+ * root of trust, and then perform encrypt, decrypt, sign and verify operations
+ * with the session key on arbitrary user data.
+ * <p>
+ * The CryptoSession class implements generic encrypt/decrypt/sign/verify methods
+ * based on the established session keys. These keys are exchanged using the
+ * getKeyRequest/provideKeyResponse methods.
+ * <p>
+ * Applications of this capability could include securing various types of
+ * purchased or private content, such as applications, books and other media,
+ * photos or media delivery protocols.
+ * <p>
+ * Operators can create session key servers that are functionally similar to a
+ * license key server, except that instead of receiving license key requests and
+ * providing encrypted content keys which are used specifically to decrypt A/V media
+ * content, the session key server receives session key requests and provides
+ * encrypted session keys which can be used for general purpose crypto operations.
+ * <p>
+ * A CryptoSession is obtained using {@link #getCryptoSession}
+ */
public final class CryptoSession {
private MediaDrm mDrm;
private byte[] mSessionId;
- /**
- * Construct a CryptoSession which can be used to encrypt, decrypt,
- * sign and verify messages or data using the session keys established
- * for the session using methods {@link getKeyRequest} and
- * {@link provideKeyResponse} using a session key server.
- *
- * @param sessionId the session ID for the session containing keys
- * to be used for encrypt, decrypt, sign and/or verify
- *
- * @param cipherAlgorithm the algorithm to use for encryption and
- * decryption ciphers. The algorithm string conforms to JCA Standard
- * Names for Cipher Transforms and is case insensitive. For example
- * "AES/CBC/PKCS5Padding".
- *
- * @param macAlgorithm the algorithm to use for sign and verify
- * The algorithm string conforms to JCA Standard Names for Mac
- * Algorithms and is case insensitive. For example "HmacSHA256".
- *
- * The list of supported algorithms for a DRM engine plugin can be obtained
- * using the method {@link getPropertyString("algorithms")}
- */
-
- public CryptoSession(MediaDrm drm, byte[] sessionId,
- String cipherAlgorithm, String macAlgorithm)
- throws MediaDrmException {
+ CryptoSession(MediaDrm drm, byte[] sessionId,
+ String cipherAlgorithm, String macAlgorithm)
+ {
mSessionId = sessionId;
mDrm = drm;
setCipherAlgorithmNative(drm, sessionId, cipherAlgorithm);
setMacAlgorithmNative(drm, sessionId, macAlgorithm);
}
+ /**
+ * Encrypt data using the CryptoSession's cipher algorithm
+ *
+ * @param keyid specifies which key to use
+ * @param input the data to encrypt
+ * @param iv the initialization vector to use for the cipher
+ */
public byte[] encrypt(byte[] keyid, byte[] input, byte[] iv) {
return encryptNative(mDrm, mSessionId, keyid, input, iv);
}
+ /**
+ * Decrypt data using the CryptoSessions's cipher algorithm
+ *
+ * @param keyid specifies which key to use
+ * @param input the data to encrypt
+ * @param iv the initialization vector to use for the cipher
+ */
public byte[] decrypt(byte[] keyid, byte[] input, byte[] iv) {
return decryptNative(mDrm, mSessionId, keyid, input, iv);
}
+ /**
+ * Sign data using the CryptoSessions's mac algorithm.
+ *
+ * @param keyid specifies which key to use
+ * @param message the data for which a signature is to be computed
+ */
public byte[] sign(byte[] keyid, byte[] message) {
return signNative(mDrm, mSessionId, keyid, message);
}
+
+ /**
+ * Verify a signature using the CryptoSessions's mac algorithm. Return true
+ * if the signatures match, false if they do no.
+ *
+ * @param keyid specifies which key to use
+ * @param message the data to verify
+ * @param signature the reference signature which will be compared with the
+ * computed signature
+ */
public boolean verify(byte[] keyid, byte[] message, byte[] signature) {
return verifyNative(mDrm, mSessionId, keyid, message, signature);
}
};
+ /**
+ * Obtain a CryptoSession object which can be used to encrypt, decrypt,
+ * sign and verify messages or data using the session keys established
+ * for the session using methods {@link #getKeyRequest} and
+ * {@link #provideKeyResponse} using a session key server.
+ *
+ * @param sessionId the session ID for the session containing keys
+ * to be used for encrypt, decrypt, sign and/or verify
+ * @param cipherAlgorithm the algorithm to use for encryption and
+ * decryption ciphers. The algorithm string conforms to JCA Standard
+ * Names for Cipher Transforms and is case insensitive. For example
+ * "AES/CBC/NoPadding".
+ * @param macAlgorithm the algorithm to use for sign and verify
+ * The algorithm string conforms to JCA Standard Names for Mac
+ * Algorithms and is case insensitive. For example "HmacSHA256".
+ * <p>
+ * The list of supported algorithms for a DRM engine plugin can be obtained
+ * using the method {@link #getPropertyString} with the property name
+ * "algorithms".
+ */
public CryptoSession getCryptoSession(byte[] sessionId,
String cipherAlgorithm,
String macAlgorithm)
- throws MediaDrmException {
+ {
return new CryptoSession(this, sessionId, cipherAlgorithm, macAlgorithm);
}
@@ -495,8 +653,7 @@
public native final void release();
private static native final void native_init();
- private native final void native_setup(Object mediadrm_this, byte[] uuid)
- throws MediaDrmException;
+ private native final void native_setup(Object mediadrm_this, byte[] uuid);
private native final void native_finalize();
diff --git a/media/java/android/media/MediaDrmException.java b/media/java/android/media/MediaDrmException.java
index 6f81f90..d6f5ff4 100644
--- a/media/java/android/media/MediaDrmException.java
+++ b/media/java/android/media/MediaDrmException.java
@@ -19,8 +19,6 @@
/**
* Exception thrown if MediaDrm object could not be instantiated for
* whatever reason.
- *
- * @hide -- don't expose yet
*/
public final class MediaDrmException extends Exception {
public MediaDrmException(String detailMessage) {
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 4a5e82e..61a0134 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -999,7 +999,7 @@
if (mEventHandler != null) {
// signal new client
mEventHandler.removeMessages(MSG_NEW_INTERNAL_CLIENT_GEN);
- mEventHandler.dispatchMessage(
+ mEventHandler.sendMessage(
mEventHandler.obtainMessage(MSG_NEW_INTERNAL_CLIENT_GEN,
/*arg1*/ generationId, /*arg2, ignored*/ 0));
// send the information
@@ -1007,12 +1007,12 @@
mEventHandler.removeMessages(MSG_REQUEST_METADATA);
mEventHandler.removeMessages(MSG_REQUEST_TRANSPORTCONTROL);
mEventHandler.removeMessages(MSG_REQUEST_ARTWORK);
- mEventHandler.dispatchMessage(
+ mEventHandler.sendMessage(
mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE));
- mEventHandler.dispatchMessage(
+ mEventHandler.sendMessage(
mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL));
- mEventHandler.dispatchMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA));
- mEventHandler.dispatchMessage(mEventHandler.obtainMessage(MSG_REQUEST_ARTWORK));
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA));
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_ARTWORK));
}
}
@@ -1020,7 +1020,7 @@
// only post messages, we can't block here
if (mEventHandler != null) {
mEventHandler.removeMessages(MSG_NEW_CURRENT_CLIENT_GEN);
- mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
MSG_NEW_CURRENT_CLIENT_GEN, clientGeneration, 0/*ignored*/));
}
}
@@ -1028,7 +1028,7 @@
public void plugRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
// only post messages, we can't block here
if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
MSG_PLUG_DISPLAY, w, h, rcd));
}
}
@@ -1036,7 +1036,7 @@
public void unplugRemoteControlDisplay(IRemoteControlDisplay rcd) {
// only post messages, we can't block here
if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
MSG_UNPLUG_DISPLAY, rcd));
}
}
@@ -1044,7 +1044,7 @@
public void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h) {
// only post messages, we can't block here
if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
MSG_UPDATE_DISPLAY_ARTWORK_SIZE, w, h, rcd));
}
}
@@ -1053,7 +1053,7 @@
// only post messages, we can't block here
if (mEventHandler != null) {
mEventHandler.removeMessages(MSG_SEEK_TO);
- mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
MSG_SEEK_TO, generationId /* arg1 */, 0 /* arg2, ignored */,
new Long(timeMs)));
}
@@ -1145,6 +1145,7 @@
break;
case MSG_SEEK_TO:
onSeekTo(msg.arg1, ((Long)msg.obj).longValue());
+ break;
default:
Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");
}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index c32ba9d..d9d466e 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -458,22 +458,22 @@
"(Ljava/lang/Object;IILjava/lang/Object;)V");
jfieldID field;
- GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_PROVISION_REQUIRED", "I");
+ GET_STATIC_FIELD_ID(field, clazz, "EVENT_PROVISION_REQUIRED", "I");
gEventTypes.kEventProvisionRequired = env->GetStaticIntField(clazz, field);
- GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_KEY_REQUIRED", "I");
+ GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_REQUIRED", "I");
gEventTypes.kEventKeyRequired = env->GetStaticIntField(clazz, field);
- GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_KEY_EXPIRED", "I");
+ GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_EXPIRED", "I");
gEventTypes.kEventKeyExpired = env->GetStaticIntField(clazz, field);
- GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_VENDOR_DEFINED", "I");
+ GET_STATIC_FIELD_ID(field, clazz, "EVENT_VENDOR_DEFINED", "I");
gEventTypes.kEventVendorDefined = env->GetStaticIntField(clazz, field);
FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
- GET_FIELD_ID(gFields.keyRequest.data, clazz, "data", "[B");
- GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "defaultUrl", "Ljava/lang/String;");
+ GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B");
+ GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest");
- GET_FIELD_ID(gFields.provisionRequest.data, clazz, "data", "[B");
- GET_FIELD_ID(gFields.provisionRequest.defaultUrl, clazz, "defaultUrl", "Ljava/lang/String;");
+ GET_FIELD_ID(gFields.provisionRequest.data, clazz, "mData", "[B");
+ GET_FIELD_ID(gFields.provisionRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
FIND_CLASS(clazz, "java/util/ArrayList");
GET_METHOD_ID(gFields.arraylist.init, clazz, "<init>", "()V");
diff --git a/packages/Keyguard/res/values-es-rUS/strings.xml b/packages/Keyguard/res/values-es-rUS/strings.xml
index 4e1c3cd..272bc9a 100644
--- a/packages/Keyguard/res/values-es-rUS/strings.xml
+++ b/packages/Keyguard/res/values-es-rUS/strings.xml
@@ -91,7 +91,7 @@
<string name="description_direction_left" msgid="7207478719805562165">"Desliza el dedo hacia la izquierda para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="description_direction_right" msgid="8034433242579600980">"Desliza el dedo hacia la derecha para <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
<string name="user_switched" msgid="3768006783166984410">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="kg_emergency_call_label" msgid="684946192523830531">"Llamada de emergencia"</string>
+ <string name="kg_emergency_call_label" msgid="684946192523830531">"Realizar llamada de emergencia"</string>
<string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"¿Olvidaste el patrón?"</string>
<string name="kg_wrong_pattern" msgid="1850806070801358830">"Patrón incorrecto"</string>
<string name="kg_wrong_password" msgid="2333281762128113157">"Contraseña incorrecta"</string>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
index 194162a..ad59c02 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
@@ -23,18 +23,25 @@
import android.content.Context;
import android.os.BatteryManager;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.Slog;
import android.view.View;
import android.widget.TextView;
import libcore.util.MutableInt;
import java.lang.ref.WeakReference;
+
+import com.android.internal.widget.LockPatternUtils;
+
/***
* Manages a number of views inside of the given layout. See below for a list of widgets.
*/
@@ -54,6 +61,8 @@
static final int SECURITY_MESSAGE_DURATION = 5000;
protected static final int FADE_DURATION = 750;
+ private static final String TAG = "KeyguardMessageArea";
+
// are we showing battery information?
boolean mShowingBatteryInfo = false;
@@ -79,6 +88,9 @@
CharSequence mMessage;
boolean mShowingMessage;
+ private CharSequence mSeparator;
+ private LockPatternUtils mLockPatternUtils;
+
Runnable mClearMessageRunnable = new Runnable() {
@Override
public void run() {
@@ -153,8 +165,6 @@
}
};
- private CharSequence mSeparator;
-
public KeyguardMessageArea(Context context) {
this(context, null);
}
@@ -162,6 +172,8 @@
public KeyguardMessageArea(Context context, AttributeSet attrs) {
super(context, attrs);
+ mLockPatternUtils = new LockPatternUtils(context);
+
// This is required to ensure marquee works
setSelected(true);
@@ -225,11 +237,12 @@
String getOwnerInfo() {
ContentResolver res = getContext().getContentResolver();
- final boolean ownerInfoEnabled = Settings.Secure.getIntForUser(res,
- Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
- return ownerInfoEnabled && !mShowingMessage ?
- Settings.Secure.getStringForUser(res, Settings.Secure.LOCK_SCREEN_OWNER_INFO,
- UserHandle.USER_CURRENT) : null;
+ String info = null;
+ final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled();
+ if (ownerInfoEnabled && !mShowingMessage) {
+ info = mLockPatternUtils.getOwnerInfo(mLockPatternUtils.getCurrentUser());
+ }
+ return info;
}
private CharSequence getChargeInfo(MutableInt icon) {
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 0789b29..49d689d 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -131,7 +131,7 @@
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roaming"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
<string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"Wi-Fi"</string>
- <string name="accessibility_no_sim" msgid="8274017118472455155">"No hay tarjeta SIM."</string>
+ <string name="accessibility_no_sim" msgid="8274017118472455155">"Sin tarjeta SIM"</string>
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Conexión mediante Bluetooth"</string>
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo avión"</string>
<!-- String.format failed for translation -->
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index a14ee38..2262204 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -133,7 +133,7 @@
<string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"Wi-Fi"</string>
<string name="accessibility_no_sim" msgid="8274017118472455155">"Sem SIM."</string>
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Tethering Bluetooth."</string>
- <string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo de avião."</string>
+ <string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo avião."</string>
<!-- String.format failed for translation -->
<!-- no translation found for accessibility_battery_level (7451474187113371965) -->
<skip />
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 3552f2e..d5c8282 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -40,7 +40,7 @@
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Mipangilio"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Mtandao-Hewa"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Hali ya Ndege"</string>
- <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"Zungusha otomatiki skrini"</string>
+ <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"Skrini ijizungushe kiotomatiki"</string>
<string name="status_bar_settings_mute_label" msgid="554682549917429396">"PUUZA"</string>
<string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"KIOTOMATIKI"</string>
<string name="status_bar_settings_notifications" msgid="397146176280905137">"Arifa"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 9146ccd..2c25236 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -30,6 +30,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -54,7 +55,10 @@
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- mDisconnectedReceiver = new UsbDisconnectedReceiver(this);
+ if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) {
+ mDisconnectedReceiver = new UsbDisconnectedReceiver(this);
+ }
+
Intent intent = getIntent();
String fingerprints = intent.getStringExtra("fingerprints");
mKey = intent.getStringExtra("key");
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 0f719db..2aa3e38 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -257,8 +257,9 @@
int mLidOpenRotation;
int mCarDockRotation;
int mDeskDockRotation;
- int mHdmiRotation;
- boolean mHdmiRotationLock;
+ int mUndockedHdmiRotation;
+ int mDemoHdmiRotation;
+ boolean mDemoHdmiRotationLock;
int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
int mUserRotation = Surface.ROTATION_0;
@@ -871,6 +872,8 @@
com.android.internal.R.integer.config_carDockRotation);
mDeskDockRotation = readRotation(
com.android.internal.R.integer.config_deskDockRotation);
+ mUndockedHdmiRotation = readRotation(
+ com.android.internal.R.integer.config_undockedHdmiRotation);
mCarDockEnablesAccelerometer = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_carDockEnablesAccelerometer);
mDeskDockEnablesAccelerometer = mContext.getResources().getBoolean(
@@ -1046,11 +1049,11 @@
// For demo purposes, allow the rotation of the HDMI display to be controlled.
// By default, HDMI locks rotation to landscape.
if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
- mHdmiRotation = mPortraitRotation;
+ mDemoHdmiRotation = mPortraitRotation;
} else {
- mHdmiRotation = mLandscapeRotation;
+ mDemoHdmiRotation = mLandscapeRotation;
}
- mHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
+ mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
}
@Override
@@ -4323,10 +4326,17 @@
// enable 180 degree rotation while docked.
preferredRotation = mDeskDockEnablesAccelerometer
? sensorRotation : mDeskDockRotation;
- } else if (mHdmiPlugged && mHdmiRotationLock) {
- // Ignore sensor when plugged into HDMI.
+ } else if (mHdmiPlugged && mDemoHdmiRotationLock) {
+ // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.
// Note that the dock orientation overrides the HDMI orientation.
- preferredRotation = mHdmiRotation;
+ preferredRotation = mDemoHdmiRotation;
+ } else if (mHdmiPlugged && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
+ && mUndockedHdmiRotation >= 0) {
+ // Ignore sensor when plugged into HDMI and an undocked orientation has
+ // been specified in the configuration (only for legacy devices without
+ // full multi-display support).
+ // Note that the dock orientation overrides the HDMI orientation.
+ preferredRotation = mUndockedHdmiRotation;
} else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
// Application just wants to remain locked in the last rotation.
preferredRotation = lastRotation;
@@ -5105,7 +5115,8 @@
pw.print(" mSeascapeRotation="); pw.println(mSeascapeRotation);
pw.print(prefix); pw.print("mPortraitRotation="); pw.print(mPortraitRotation);
pw.print(" mUpsideDownRotation="); pw.println(mUpsideDownRotation);
- pw.print(prefix); pw.print("mHdmiRotation="); pw.print(mHdmiRotation);
- pw.print(" mHdmiRotationLock="); pw.println(mHdmiRotationLock);
+ pw.print(prefix); pw.print("mDemoHdmiRotation="); pw.print(mDemoHdmiRotation);
+ pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock);
+ pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation);
}
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index d19433e..8315ac4 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1600,7 +1600,7 @@
ApplicationInfo ai = mIPackageManager.getApplicationInfo(imm.getPackageName(),
PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
mSettings.getCurrentUserId());
- if (ai.enabledSetting
+ if (ai != null && ai.enabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
mIPackageManager.setApplicationEnabledSetting(imm.getPackageName(),
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
diff --git a/services/java/com/android/server/LockSettingsService.java b/services/java/com/android/server/LockSettingsService.java
index f8e9ff7..349dba5 100644
--- a/services/java/com/android/server/LockSettingsService.java
+++ b/services/java/com/android/server/LockSettingsService.java
@@ -19,6 +19,11 @@
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+
+import static android.content.Context.USER_SERVICE;
+import static android.Manifest.permission.READ_PROFILE;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
@@ -27,8 +32,10 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Secure;
+import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.Slog;
@@ -40,6 +47,7 @@
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
+import java.util.List;
/**
* Keeps the lock pattern/password data and related settings for each user.
@@ -80,23 +88,52 @@
private void migrateOldData() {
try {
- if (getString("migrated", null, 0) != null) {
- // Already migrated
- return;
+ // These Settings moved before multi-user was enabled, so we only have to do it for the
+ // root user.
+ if (getString("migrated", null, 0) == null) {
+ final ContentResolver cr = mContext.getContentResolver();
+ for (String validSetting : VALID_SETTINGS) {
+ String value = Settings.Secure.getString(cr, validSetting);
+ if (value != null) {
+ setString(validSetting, value, 0);
+ }
+ }
+ // No need to move the password / pattern files. They're already in the right place.
+ setString("migrated", "true", 0);
+ Slog.i(TAG, "Migrated lock settings to new location");
}
- final ContentResolver cr = mContext.getContentResolver();
- for (String validSetting : VALID_SETTINGS) {
- String value = Settings.Secure.getString(cr, validSetting);
- if (value != null) {
- setString(validSetting, value, 0);
+ // These Settings changed after multi-user was enabled, hence need to be moved per user.
+ if (getString("migrated_user_specific", null, 0) == null) {
+ final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+ final ContentResolver cr = mContext.getContentResolver();
+ List<UserInfo> users = um.getUsers();
+ for (int user = 0; user < users.size(); user++) {
+ int userId = users.get(user).getUserHandle().getIdentifier();
+ for (String perUserSetting : MIGRATE_SETTINGS_PER_USER) {
+ // Handle Strings
+ String value = Settings.Secure.getStringForUser(cr, perUserSetting, userId);
+ if (value != null) {
+ setString(perUserSetting, value, userId);
+ Settings.Secure.putStringForUser(cr, perUserSetting, "", userId);
+ continue;
+ }
+
+ // Handle integers
+ try {
+ int ivalue = Settings.Secure.getIntForUser(cr, perUserSetting, userId);
+ setLong(perUserSetting, ivalue, userId);
+ Settings.Secure.putIntForUser(cr, perUserSetting, 0, userId);
+ } catch (SettingNotFoundException e) {
+ }
+ }
}
+ // No need to move the password / pattern files. They're already in the right place.
+ setString("migrated_user_specific", "true", 0);
+ Slog.i(TAG, "Migrated per-user lock settings to new location");
}
- // No need to move the password / pattern files. They're already in the right place.
- setString("migrated", "true", 0);
- Slog.i(TAG, "Migrated lock settings to new location");
} catch (RemoteException re) {
- Slog.e(TAG, "Unable to migrate old data");
+ Slog.e(TAG, "Unable to migrate old data", re);
}
}
@@ -108,6 +145,19 @@
mContext.checkCallingOrSelfPermission(PERMISSION);
}
+ private final void checkReadPermission(String requestedKey, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ for (int i = 0; i < READ_PROFILE_PROTECTED_SETTINGS.length; i++) {
+ String key = READ_PROFILE_PROTECTED_SETTINGS[i];
+ if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(READ_PROFILE)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("uid=" + callingUid
+ + " needs permission " + READ_PROFILE + " to read "
+ + requestedKey + " for user " + userId);
+ }
+ }
+ }
+
@Override
public void setBoolean(String key, boolean value, int userId) throws RemoteException {
checkWritePermission(userId);
@@ -131,7 +181,7 @@
@Override
public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
- //checkReadPermission(userId);
+ checkReadPermission(key, userId);
String value = readFromDb(key, null, userId);
return TextUtils.isEmpty(value) ?
@@ -140,7 +190,7 @@
@Override
public long getLong(String key, long defaultValue, int userId) throws RemoteException {
- //checkReadPermission(userId);
+ checkReadPermission(key, userId);
String value = readFromDb(key, null, userId);
return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
@@ -148,7 +198,7 @@
@Override
public String getString(String key, String defaultValue, int userId) throws RemoteException {
- //checkReadPermission(userId);
+ checkReadPermission(key, userId);
return readFromDb(key, defaultValue, userId);
}
@@ -388,5 +438,13 @@
Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
Secure.LOCK_PATTERN_VISIBLE,
Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
- };
+ };
+
+ private static final String[] MIGRATE_SETTINGS_PER_USER = new String[] {
+ Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
+ Secure.LOCK_SCREEN_OWNER_INFO
+ };
+
+ // These are protected with a read permission
+ private static final String[] READ_PROFILE_PROTECTED_SETTINGS = MIGRATE_SETTINGS_PER_USER;
}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index fa18e76..3bebf91 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -1676,8 +1676,12 @@
.getSystemService(Context.AUDIO_SERVICE);
// sound
+
+ // should we use the default notification sound? (indicated either by DEFAULT_SOUND
+ // or because notification.sound is pointing at Settings.System.NOTIFICATION_SOUND)
final boolean useDefaultSound =
- (notification.defaults & Notification.DEFAULT_SOUND) != 0;
+ (notification.defaults & Notification.DEFAULT_SOUND) != 0
+ || Settings.System.DEFAULT_NOTIFICATION_URI.equals(notification.sound);
Uri soundUri = null;
boolean hasValidSound = false;
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 1f3ac96..7a5f55a 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1160,7 +1160,9 @@
boolean setInputFilter = false;
AccessibilityInputFilter inputFilter = null;
synchronized (mLock) {
- if (userState.mIsAccessibilityEnabled) {
+ // Accessibility enabled means at least one service is enabled.
+ if (userState.mIsAccessibilityEnabled
+ || userState.mIsDisplayMagnificationEnabled) {
if (!mHasInputFilter) {
mHasInputFilter = true;
if (mInputFilter == null) {
@@ -1174,7 +1176,8 @@
if (userState.mIsDisplayMagnificationEnabled) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
}
- if (userState.mIsTouchExplorationEnabled) {
+ // Touch exploration without accessibility makes no sense.
+ if (userState.mIsAccessibilityEnabled && userState.mIsTouchExplorationEnabled) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION;
}
mInputFilter.setEnabledFeatures(flags);
diff --git a/services/java/com/android/server/am/NativeCrashListener.java b/services/java/com/android/server/am/NativeCrashListener.java
index 0688c50..de439d7 100644
--- a/services/java/com/android/server/am/NativeCrashListener.java
+++ b/services/java/com/android/server/am/NativeCrashListener.java
@@ -43,13 +43,14 @@
class NativeCrashListener extends Thread {
static final String TAG = "NativeCrashListener";
static final boolean DEBUG = false;
+ static final boolean MORE_DEBUG = DEBUG && false;
// Must match the path defined in debuggerd.c.
static final String DEBUGGERD_SOCKET_PATH = "/data/system/ndebugsocket";
// Use a short timeout on socket operations and abandon the connection
// on hard errors
- static final long SOCKET_TIMEOUT_MILLIS = 1000; // 1 second
+ static final long SOCKET_TIMEOUT_MILLIS = 2000; // 2 seconds
final ActivityManagerService mAm;
@@ -124,9 +125,9 @@
InetSocketAddress peer = new InetSocketAddress();
FileDescriptor peerFd = null;
try {
- if (DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");
+ if (MORE_DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");
peerFd = Libcore.os.accept(serverFd, peer);
- if (DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
+ if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
if (peerFd != null) {
// Only the superuser is allowed to talk to us over this socket
StructUcred credentials =
@@ -145,7 +146,12 @@
if (peerFd != null) {
try {
Libcore.os.write(peerFd, ackSignal, 0, 1);
- } catch (Exception e) { /* we don't care about failures here */ }
+ } catch (Exception e) {
+ /* we don't care about failures here */
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Exception writing ack: " + e.getMessage());
+ }
+ }
}
}
}
@@ -183,7 +189,7 @@
// Read the crash report from the debuggerd connection
void consumeNativeCrashData(FileDescriptor fd) {
- if (DEBUG) Slog.i(TAG, "debuggerd connected");
+ if (MORE_DEBUG) Slog.i(TAG, "debuggerd connected");
final byte[] buf = new byte[4096];
final ByteArrayOutputStream os = new ByteArrayOutputStream(4096);
@@ -218,7 +224,7 @@
// get some data
bytes = Libcore.os.read(fd, buf, 0, buf.length);
if (bytes > 0) {
- if (DEBUG) {
+ if (MORE_DEBUG) {
String s = new String(buf, 0, bytes, "UTF-8");
Slog.v(TAG, "READ=" + bytes + "> " + s);
}
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index df90a56..11c6dab 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -88,8 +88,13 @@
private static final String TAG_ENTRY = "entry";
private static final String TAG_VALUE = "value";
private static final String ATTR_KEY = "key";
+ private static final String ATTR_VALUE_TYPE = "type";
private static final String ATTR_MULTIPLE = "m";
+ private static final String ATTR_TYPE_STRING_ARRAY = "sa";
+ private static final String ATTR_TYPE_STRING = "s";
+ private static final String ATTR_TYPE_BOOLEAN = "b";
+
private static final String USER_INFO_DIR = "system" + File.separator + "users";
private static final String USER_LIST_FILENAME = "userlist.xml";
private static final String USER_PHOTO_FILENAME = "photo.png";
@@ -965,7 +970,12 @@
}
@Override
- public List<RestrictionEntry> getApplicationRestrictions(String packageName, int userId) {
+ public Bundle getApplicationRestrictions(String packageName) {
+ return getApplicationRestrictionsForUser(packageName, UserHandle.getCallingUserId());
+ }
+
+ @Override
+ public Bundle getApplicationRestrictionsForUser(String packageName, int userId) {
if (UserHandle.getCallingUserId() != userId
|| !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
checkManageUsersPermission("Only system can get restrictions for other users/apps");
@@ -977,7 +987,7 @@
}
@Override
- public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries,
+ public void setApplicationRestrictions(String packageName, Bundle restrictions,
int userId) {
if (UserHandle.getCallingUserId() != userId
|| !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
@@ -985,7 +995,7 @@
}
synchronized (mPackagesLock) {
// Write the restrictions to XML
- writeApplicationRestrictionsLocked(packageName, entries, userId);
+ writeApplicationRestrictionsLocked(packageName, restrictions, userId);
}
}
@@ -1001,9 +1011,9 @@
}
}
- private List<RestrictionEntry> readApplicationRestrictionsLocked(String packageName,
+ private Bundle readApplicationRestrictionsLocked(String packageName,
int userId) {
- final ArrayList<RestrictionEntry> entries = new ArrayList<RestrictionEntry>();
+ final Bundle restrictions = new Bundle();
final ArrayList<String> values = new ArrayList<String>();
FileInputStream fis = null;
@@ -1023,12 +1033,13 @@
if (type != XmlPullParser.START_TAG) {
Slog.e(LOG_TAG, "Unable to read restrictions file "
+ restrictionsFile.getBaseFile());
- return entries;
+ return restrictions;
}
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
String key = parser.getAttributeValue(null, ATTR_KEY);
+ String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE);
String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE);
if (multiple != null) {
int count = Integer.parseInt(multiple);
@@ -1041,14 +1052,13 @@
}
String [] valueStrings = new String[values.size()];
values.toArray(valueStrings);
- Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + valueStrings);
- RestrictionEntry entry = new RestrictionEntry(key, valueStrings);
- entries.add(entry);
+ restrictions.putStringArray(key, valueStrings);
+ } else if (ATTR_TYPE_BOOLEAN.equals(valType)) {
+ restrictions.putBoolean(key, Boolean.parseBoolean(
+ parser.nextText().trim()));
} else {
String value = parser.nextText().trim();
- Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + value);
- RestrictionEntry entry = new RestrictionEntry(key, value);
- entries.add(entry);
+ restrictions.putString(key, value);
}
}
}
@@ -1063,11 +1073,11 @@
}
}
}
- return entries;
+ return restrictions;
}
private void writeApplicationRestrictionsLocked(String packageName,
- List<RestrictionEntry> entries, int userId) {
+ Bundle restrictions, int userId) {
FileOutputStream fos = null;
AtomicFile restrictionsFile = new AtomicFile(
new File(Environment.getUserSystemDirectory(userId),
@@ -1084,18 +1094,24 @@
serializer.startTag(null, TAG_RESTRICTIONS);
- for (RestrictionEntry entry : entries) {
+ for (String key : restrictions.keySet()) {
+ Object value = restrictions.get(key);
serializer.startTag(null, TAG_ENTRY);
- serializer.attribute(null, ATTR_KEY, entry.getKey());
- if (entry.getSelectedString() != null || entry.getAllSelectedStrings() == null) {
- String value = entry.getSelectedString();
- serializer.text(value != null ? value : "");
+ serializer.attribute(null, ATTR_KEY, key);
+
+ if (value instanceof Boolean) {
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN);
+ serializer.text(value.toString());
+ } else if (value == null || value instanceof String) {
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING);
+ serializer.text(value != null ? (String) value : "");
} else {
- String[] values = entry.getAllSelectedStrings();
+ serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING_ARRAY);
+ String[] values = (String[]) value;
serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length));
- for (String value : values) {
+ for (String choice : values) {
serializer.startTag(null, TAG_VALUE);
- serializer.text(value != null ? value : "");
+ serializer.text(choice != null ? choice : "");
serializer.endTag(null, TAG_VALUE);
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index d9a9d0b..d21b111 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -5592,6 +5592,7 @@
ScreenRotationAnimation.createRotationMatrix(rot, dw, dh, matrix);
matrix.postTranslate(-FloatMath.ceil(frame.left*scale), -FloatMath.ceil(frame.top*scale));
Canvas canvas = new Canvas(bm);
+ canvas.drawColor(0xFF000000);
canvas.drawBitmap(rawss, matrix, null);
canvas.setBitmap(null);
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 31e01c0..dbd48d9 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -93,21 +93,21 @@
}
/**
- * @return Network Id 0..65535
+ * @return Network Id 0..65535, Integer.MAX_VALUE if unknown
*/
public int getNetworkId() {
return mNetworkId;
}
/**
- * @return System Id 0..32767
+ * @return System Id 0..32767, Integer.MAX_VALUE if unknown
*/
public int getSystemId() {
return mSystemId;
}
/**
- * @return Base Station Id 0..65535
+ * @return Base Station Id 0..65535, Integer.MAX_VALUE if unknown
*/
public int getBasestationId() {
return mBasestationId;
@@ -118,7 +118,7 @@
* specified in 3GPP2 C.S0005-A v6.0. It is represented in units
* of 0.25 seconds and ranges from -2592000 to 2592000, both
* values inclusive (corresponding to a range of -180
- * to +180 degrees).
+ * to +180 degrees). Integer.MAX_VALUE if unknown.
*/
public int getLongitude() {
return mLongitude;
@@ -129,7 +129,7 @@
* specified in 3GPP2 C.S0005-A v6.0. It is represented in units
* of 0.25 seconds and ranges from -1296000 to 1296000, both
* values inclusive (corresponding to a range of -90
- * to +90 degrees).
+ * to +90 degrees). Integer.MAX_VALUE if unknown.
*/
public int getLatitude() {
return mLatitude;
@@ -162,7 +162,7 @@
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("CellIdentitiyCdma:{");
+ StringBuilder sb = new StringBuilder("CellIdentityCdma:{");
sb.append(" mNetworkId="); sb.append(mNetworkId);
sb.append(" mSystemId="); sb.append(mSystemId);
sb.append(" mBasestationId="); sb.append(mBasestationId);
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 98113e7..6f8cc91 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -21,7 +21,7 @@
import android.telephony.Rlog;
/**
- * CellIdentity to represent a unique GSM or UMTS cell
+ * CellIdentity to represent a unique GSM cell
*/
public final class CellIdentityGsm implements Parcelable {
@@ -35,10 +35,7 @@
// 16-bit Location Area Code, 0..65535
private final int mLac;
// 16-bit GSM Cell Identity described in TS 27.007, 0..65535
- // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455
private final int mCid;
- // 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511
- private final int mPsc;
/**
* @hide
@@ -48,7 +45,6 @@
mMnc = Integer.MAX_VALUE;
mLac = Integer.MAX_VALUE;
mCid = Integer.MAX_VALUE;
- mPsc = Integer.MAX_VALUE;
}
/**
* public constructor
@@ -56,16 +52,14 @@
* @param mnc 2 or 3-digit Mobile Network Code, 0..999
* @param lac 16-bit Location Area Code, 0..65535
* @param cid 16-bit GSM Cell Identity or 28-bit UMTS Cell Identity
- * @param psc 9-bit UMTS Primary Scrambling Code
*
* @hide
*/
- public CellIdentityGsm (int mcc, int mnc, int lac, int cid, int psc) {
+ public CellIdentityGsm (int mcc, int mnc, int lac, int cid) {
mMcc = mcc;
mMnc = mnc;
mLac = lac;
mCid = cid;
- mPsc = psc;
}
private CellIdentityGsm(CellIdentityGsm cid) {
@@ -73,7 +67,6 @@
mMnc = cid.mMnc;
mLac = cid.mLac;
mCid = cid.mCid;
- mPsc = cid.mPsc;
}
CellIdentityGsm copy() {
@@ -81,21 +74,21 @@
}
/**
- * @return 3-digit Mobile Country Code, 0..999
+ * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
*/
public int getMcc() {
return mMcc;
}
/**
- * @return 2 or 3-digit Mobile Network Code, 0..999
+ * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
*/
public int getMnc() {
return mMnc;
}
/**
- * @return 16-bit Location Area Code, 0..65535
+ * @return 16-bit Location Area Code, 0..65535, Integer.MAX_VALUE if unknown
*/
public int getLac() {
return mLac;
@@ -104,27 +97,24 @@
/**
* @return CID
* Either 16-bit GSM Cell Identity described
- * in TS 27.007, 0..65535
- * or 28-bit UMTS Cell Identity described
- * in TS 25.331, 0..268435455
+ * in TS 27.007, 0..65535, Integer.MAX_VALUE if unknown
*/
public int getCid() {
return mCid;
}
/**
- * @return 9-bit UMTS Primary Scrambling Code described in
- * TS 25.331, 0..511
+ * @return Integer.MAX_VALUE, undefined for GSM
*/
+ @Deprecated
public int getPsc() {
- return mPsc;
+ return Integer.MAX_VALUE;
}
@Override
public int hashCode() {
int primeNum = 31;
- return (mMcc * primeNum) + (mMnc * primeNum) + (mLac * primeNum) + (mCid * primeNum) +
- (mPsc * primeNum);
+ return (mMcc * primeNum) + (mMnc * primeNum) + (mLac * primeNum) + (mCid * primeNum);
}
@Override
@@ -135,8 +125,7 @@
return mMcc == o.mMcc &&
mMnc == o.mMnc &&
mLac == o.mLac &&
- mCid == o.mCid &&
- mPsc == o.mPsc;
+ mCid == o.mCid;
} catch (ClassCastException e) {
return false;
}
@@ -147,12 +136,11 @@
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("CellIdentitiyGsm:{");
+ StringBuilder sb = new StringBuilder("CellIdentityGsm:{");
sb.append(" mMcc=").append(mMcc);
sb.append(" mMnc=").append(mMnc);
sb.append(" mLac=").append(mLac);
sb.append(" mCid=").append(mCid);
- sb.append(" mPsc=").append(mPsc);
sb.append("}");
return sb.toString();
@@ -172,7 +160,6 @@
dest.writeInt(mMnc);
dest.writeInt(mLac);
dest.writeInt(mCid);
- dest.writeInt(mPsc);
}
/** Construct from Parcel, type has already been processed */
@@ -181,7 +168,6 @@
mMnc = in.readInt();
mLac = in.readInt();
mCid = in.readInt();
- mPsc = in.readInt();
if (DBG) log("CellIdentityGsm(Parcel): " + toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 86924bd..13b39c9 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -81,35 +81,35 @@
}
/**
- * @return 3-digit Mobile Country Code, 0..999
+ * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
*/
public int getMcc() {
return mMcc;
}
/**
- * @return 2 or 3-digit Mobile Network Code, 0..999
+ * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
*/
public int getMnc() {
return mMnc;
}
/**
- * @return 28-bit Cell Identity
+ * @return 28-bit Cell Identity, Integer.MAX_VALUE if unknown
*/
public int getCi() {
return mCi;
}
/**
- * @return Physical Cell Id 0..503
+ * @return Physical Cell Id 0..503, Integer.MAX_VALUE if unknown
*/
public int getPci() {
return mPci;
}
/**
- * @return 16-bit Tracking Area Code
+ * @return 16-bit Tracking Area Code, Integer.MAX_VALUE if unknown
*/
public int getTac() {
return mTac;
@@ -142,7 +142,7 @@
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("CellIdentitiyLte:{");
+ StringBuilder sb = new StringBuilder("CellIdentityLte:{");
sb.append(" mMcc="); sb.append(mMcc);
sb.append(" mMnc="); sb.append(mMnc);
sb.append(" mCi="); sb.append(mCi);
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
new file mode 100644
index 0000000..2f8fa42
--- /dev/null
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -0,0 +1,205 @@
+/*
+ * 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.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+/**
+ * CellIdentity to represent a unique UMTS cell
+ */
+public final class CellIdentityWcdma implements Parcelable {
+
+ private static final String LOG_TAG = "CellIdentityWcdma";
+ private static final boolean DBG = false;
+
+ // 3-digit Mobile Country Code, 0..999
+ private final int mMcc;
+ // 2 or 3-digit Mobile Network Code, 0..999
+ private final int mMnc;
+ // 16-bit Location Area Code, 0..65535
+ private final int mLac;
+ // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455
+ private final int mCid;
+ // 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511
+ private final int mPsc;
+
+ /**
+ * @hide
+ */
+ public CellIdentityWcdma() {
+ mMcc = Integer.MAX_VALUE;
+ mMnc = Integer.MAX_VALUE;
+ mLac = Integer.MAX_VALUE;
+ mCid = Integer.MAX_VALUE;
+ mPsc = Integer.MAX_VALUE;
+ }
+ /**
+ * public constructor
+ * @param mcc 3-digit Mobile Country Code, 0..999
+ * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+ * @param lac 16-bit Location Area Code, 0..65535
+ * @param cid 28-bit UMTS Cell Identity
+ * @param psc 9-bit UMTS Primary Scrambling Code
+ *
+ * @hide
+ */
+ public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc) {
+ mMcc = mcc;
+ mMnc = mnc;
+ mLac = lac;
+ mCid = cid;
+ mPsc = psc;
+ }
+
+ private CellIdentityWcdma(CellIdentityWcdma cid) {
+ mMcc = cid.mMcc;
+ mMnc = cid.mMnc;
+ mLac = cid.mLac;
+ mCid = cid.mCid;
+ mPsc = cid.mPsc;
+ }
+
+ CellIdentityWcdma copy() {
+ return new CellIdentityWcdma(this);
+ }
+
+ /**
+ * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
+ */
+ public int getMcc() {
+ return mMcc;
+ }
+
+ /**
+ * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
+ */
+ public int getMnc() {
+ return mMnc;
+ }
+
+ /**
+ * @return 16-bit Location Area Code, 0..65535, Integer.MAX_VALUE if unknown
+ */
+ public int getLac() {
+ return mLac;
+ }
+
+ /**
+ * @return CID
+ * 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, Integer.MAX_VALUE if unknown
+ */
+ public int getCid() {
+ return mCid;
+ }
+
+ /**
+ * @return 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511, Integer.MAX_VALUE
+ * if unknown
+ */
+ public int getPsc() {
+ return mPsc;
+ }
+
+ @Override
+ public int hashCode() {
+ int primeNum = 31;
+ return (mMcc * primeNum) + (mMnc * primeNum) + (mLac * primeNum) + (mCid * primeNum) +
+ (mPsc * primeNum);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (super.equals(other)) {
+ try {
+ CellIdentityWcdma o = (CellIdentityWcdma)other;
+ return mMcc == o.mMcc &&
+ mMnc == o.mMnc &&
+ mLac == o.mLac &&
+ mCid == o.mCid &&
+ mPsc == o.mPsc;
+ } catch (ClassCastException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("CellIdentityWcdma:{");
+ sb.append(" mMcc=").append(mMcc);
+ sb.append(" mMnc=").append(mMnc);
+ sb.append(" mLac=").append(mLac);
+ sb.append(" mCid=").append(mCid);
+ sb.append(" mPsc=").append(mPsc);
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ dest.writeInt(mMcc);
+ dest.writeInt(mMnc);
+ dest.writeInt(mLac);
+ dest.writeInt(mCid);
+ dest.writeInt(mPsc);
+ }
+
+ /** Construct from Parcel, type has already been processed */
+ private CellIdentityWcdma(Parcel in) {
+ mMcc = in.readInt();
+ mMnc = in.readInt();
+ mLac = in.readInt();
+ mCid = in.readInt();
+ mPsc = in.readInt();
+ if (DBG) log("CellIdentityWcdma(Parcel): " + toString());
+ }
+
+ /** Implement the Parcelable interface */
+ @SuppressWarnings("hiding")
+ public static final Creator<CellIdentityWcdma> CREATOR =
+ new Creator<CellIdentityWcdma>() {
+ @Override
+ public CellIdentityWcdma createFromParcel(Parcel in) {
+ return new CellIdentityWcdma(in);
+ }
+
+ @Override
+ public CellIdentityWcdma[] newArray(int size) {
+ return new CellIdentityWcdma[size];
+ }
+ };
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index fe3c68b..bfa0942 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -31,6 +31,8 @@
protected static final int TYPE_CDMA = 2;
/** @hide */
protected static final int TYPE_LTE = 3;
+ /** @hide */
+ protected static final int TYPE_WCDMA = 4;
// Type to distinguish where time stamp gets recorded.
@@ -201,6 +203,7 @@
case TYPE_GSM: return CellInfoGsm.createFromParcelBody(in);
case TYPE_CDMA: return CellInfoCdma.createFromParcelBody(in);
case TYPE_LTE: return CellInfoLte.createFromParcelBody(in);
+ case TYPE_WCDMA: return CellInfoWcdma.createFromParcelBody(in);
default: throw new RuntimeException("Bad CellInfo Parcel");
}
}
diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java
new file mode 100644
index 0000000..0615702
--- /dev/null
+++ b/telephony/java/android/telephony/CellInfoWcdma.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+/**
+ * Immutable cell information from a point in time.
+ */
+public final class CellInfoWcdma extends CellInfo implements Parcelable {
+
+ private static final String LOG_TAG = "CellInfoWcdma";
+ private static final boolean DBG = false;
+
+ private CellIdentityWcdma mCellIdentityWcdma;
+ private CellSignalStrengthWcdma mCellSignalStrengthWcdma;
+
+ /** @hide */
+ public CellInfoWcdma() {
+ super();
+ mCellIdentityWcdma = new CellIdentityWcdma();
+ mCellSignalStrengthWcdma = new CellSignalStrengthWcdma();
+ }
+
+ /** @hide */
+ public CellInfoWcdma(CellInfoWcdma ci) {
+ super(ci);
+ this.mCellIdentityWcdma = ci.mCellIdentityWcdma.copy();
+ this.mCellSignalStrengthWcdma = ci.mCellSignalStrengthWcdma.copy();
+ }
+
+ public CellIdentityWcdma getCellIdentity() {
+ return mCellIdentityWcdma;
+ }
+ /** @hide */
+ public void setCellIdentity(CellIdentityWcdma cid) {
+ mCellIdentityWcdma = cid;
+ }
+
+ public CellSignalStrengthWcdma getCellSignalStrength() {
+ return mCellSignalStrengthWcdma;
+ }
+ /** @hide */
+ public void setCellSignalStrength(CellSignalStrengthWcdma css) {
+ mCellSignalStrengthWcdma = css;
+ }
+
+ /**
+ * @return hash code
+ */
+ @Override
+ public int hashCode() {
+ return super.hashCode() + mCellIdentityWcdma.hashCode() + mCellSignalStrengthWcdma.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!super.equals(other)) {
+ return false;
+ }
+ try {
+ CellInfoWcdma o = (CellInfoWcdma) other;
+ return mCellIdentityWcdma.equals(o.mCellIdentityWcdma)
+ && mCellSignalStrengthWcdma.equals(o.mCellSignalStrengthWcdma);
+ } catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("CellInfoWcdma:{");
+ sb.append(super.toString());
+ sb.append(" ").append(mCellIdentityWcdma);
+ sb.append(" ").append(mCellSignalStrengthWcdma);
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags, TYPE_WCDMA);
+ mCellIdentityWcdma.writeToParcel(dest, flags);
+ mCellSignalStrengthWcdma.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Construct a CellInfoWcdma object from the given parcel
+ * where the token is already been processed.
+ */
+ private CellInfoWcdma(Parcel in) {
+ super(in);
+ mCellIdentityWcdma = CellIdentityWcdma.CREATOR.createFromParcel(in);
+ mCellSignalStrengthWcdma = CellSignalStrengthWcdma.CREATOR.createFromParcel(in);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<CellInfoWcdma> CREATOR = new Creator<CellInfoWcdma>() {
+ @Override
+ public CellInfoWcdma createFromParcel(Parcel in) {
+ in.readInt(); // Skip past token, we know what it is
+ return createFromParcelBody(in);
+ }
+
+ @Override
+ public CellInfoWcdma[] newArray(int size) {
+ return new CellInfoWcdma[size];
+ }
+ };
+
+ /** @hide */
+ protected static CellInfoWcdma createFromParcelBody(Parcel in) {
+ return new CellInfoWcdma(in);
+ }
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 2c36344..d27fcec 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -21,7 +21,7 @@
import android.telephony.Rlog;
/**
- * LTE signal strength related information.
+ * GSM signal strength related information.
*/
public final class CellSignalStrengthGsm extends CellSignalStrength implements Parcelable {
@@ -30,7 +30,7 @@
private static final int GSM_SIGNAL_STRENGTH_GREAT = 12;
private static final int GSM_SIGNAL_STRENGTH_GOOD = 8;
- private static final int GSM_SIGNAL_STRENGTH_MODERATE = 8;
+ private static final int GSM_SIGNAL_STRENGTH_MODERATE = 5;
private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
@@ -67,7 +67,8 @@
/**
* Initialize all the values
*
- * @param SignalStrength
+ * @param ss SignalStrength as ASU value
+ * @param ber is Bit Error Rate
*
* @hide
*/
@@ -139,7 +140,7 @@
}
/**
- * Get the LTE signal level as an asu value between 0..97, 99 is unknown
+ * Get the signal level as an asu value between 0..31, 99 is unknown
* Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
*/
@Override
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
new file mode 100644
index 0000000..b94b01d
--- /dev/null
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+/**
+ * Wcdma signal strength related information.
+ */
+public final class CellSignalStrengthWcdma extends CellSignalStrength implements Parcelable {
+
+ private static final String LOG_TAG = "CellSignalStrengthWcdma";
+ private static final boolean DBG = false;
+
+ private static final int WCDMA_SIGNAL_STRENGTH_GREAT = 12;
+ private static final int WCDMA_SIGNAL_STRENGTH_GOOD = 8;
+ private static final int WCDMA_SIGNAL_STRENGTH_MODERATE = 5;
+
+ private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
+ private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+
+ /**
+ * Empty constructor
+ *
+ * @hide
+ */
+ public CellSignalStrengthWcdma() {
+ setDefaultValues();
+ }
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public CellSignalStrengthWcdma(int ss, int ber) {
+ initialize(ss, ber);
+ }
+
+ /**
+ * Copy constructors
+ *
+ * @param s Source SignalStrength
+ *
+ * @hide
+ */
+ public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) {
+ copyFrom(s);
+ }
+
+ /**
+ * Initialize all the values
+ *
+ * @param ss SignalStrength as ASU value
+ * @param ber is Bit Error Rate
+ *
+ * @hide
+ */
+ public void initialize(int ss, int ber) {
+ mSignalStrength = ss;
+ mBitErrorRate = ber;
+ }
+
+ /**
+ * @hide
+ */
+ protected void copyFrom(CellSignalStrengthWcdma s) {
+ mSignalStrength = s.mSignalStrength;
+ mBitErrorRate = s.mBitErrorRate;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public CellSignalStrengthWcdma copy() {
+ return new CellSignalStrengthWcdma(this);
+ }
+
+ /** @hide */
+ @Override
+ public void setDefaultValues() {
+ mSignalStrength = Integer.MAX_VALUE;
+ mBitErrorRate = Integer.MAX_VALUE;
+ }
+
+ /**
+ * Get signal level as an int from 0..4
+ */
+ @Override
+ public int getLevel() {
+ int level;
+
+ // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+ // asu = 0 (-113dB or less) is very weak
+ // signal, its better to show 0 bars to the user in such cases.
+ // asu = 99 is a special case, where the signal strength is unknown.
+ int asu = mSignalStrength;
+ if (asu <= 2 || asu == 99) level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (asu >= WCDMA_SIGNAL_STRENGTH_GREAT) level = SIGNAL_STRENGTH_GREAT;
+ else if (asu >= WCDMA_SIGNAL_STRENGTH_GOOD) level = SIGNAL_STRENGTH_GOOD;
+ else if (asu >= WCDMA_SIGNAL_STRENGTH_MODERATE) level = SIGNAL_STRENGTH_MODERATE;
+ else level = SIGNAL_STRENGTH_POOR;
+ if (DBG) log("getLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the signal strength as dBm
+ */
+ @Override
+ public int getDbm() {
+ int dBm;
+
+ int level = mSignalStrength;
+ int asu = (level == 99 ? Integer.MAX_VALUE : level);
+ if (asu != Integer.MAX_VALUE) {
+ dBm = -113 + (2 * asu);
+ } else {
+ dBm = Integer.MAX_VALUE;
+ }
+ if (DBG) log("getDbm=" + dBm);
+ return dBm;
+ }
+
+ /**
+ * Get the signal level as an asu value between 0..31, 99 is unknown
+ * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+ */
+ @Override
+ public int getAsuLevel() {
+ // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+ // asu = 0 (-113dB or less) is very weak
+ // signal, its better to show 0 bars to the user in such cases.
+ // asu = 99 is a special case, where the signal strength is unknown.
+ int level = mSignalStrength;
+ if (DBG) log("getAsuLevel=" + level);
+ return level;
+ }
+
+ @Override
+ public int hashCode() {
+ int primeNum = 31;
+ return (mSignalStrength * primeNum) + (mBitErrorRate * primeNum);
+ }
+
+ @Override
+ public boolean equals (Object o) {
+ CellSignalStrengthWcdma s;
+
+ try {
+ s = (CellSignalStrengthWcdma) o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return mSignalStrength == s.mSignalStrength && mBitErrorRate == s.mBitErrorRate;
+ }
+
+ /**
+ * @return string representation.
+ */
+ @Override
+ public String toString() {
+ return "CellSignalStrengthWcdma:"
+ + " ss=" + mSignalStrength
+ + " ber=" + mBitErrorRate;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ dest.writeInt(mSignalStrength);
+ dest.writeInt(mBitErrorRate);
+ }
+
+ /**
+ * Construct a SignalStrength object from the given parcel
+ * where the token is already been processed.
+ */
+ private CellSignalStrengthWcdma(Parcel in) {
+ mSignalStrength = in.readInt();
+ mBitErrorRate = in.readInt();
+ if (DBG) log("CellSignalStrengthWcdma(Parcel): " + toString());
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @SuppressWarnings("hiding")
+ public static final Parcelable.Creator<CellSignalStrengthWcdma> CREATOR =
+ new Parcelable.Creator<CellSignalStrengthWcdma>() {
+ @Override
+ public CellSignalStrengthWcdma createFromParcel(Parcel in) {
+ return new CellSignalStrengthWcdma(in);
+ }
+
+ @Override
+ public CellSignalStrengthWcdma[] newArray(int size) {
+ return new CellSignalStrengthWcdma[size];
+ }
+ };
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/tests/CanvasCompare/AndroidManifest.xml b/tests/CanvasCompare/AndroidManifest.xml
index 1cb8c37..b55e290 100644
--- a/tests/CanvasCompare/AndroidManifest.xml
+++ b/tests/CanvasCompare/AndroidManifest.xml
@@ -15,8 +15,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test.hwuicompare" >
- <!-- for perfhud -->
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:label="@string/app_name"
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java
index e0ff1dc..1ed4723 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java
@@ -16,11 +16,20 @@
package com.android.test.hwuicompare;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.TreeSet;
-import com.android.test.hwuicompare.R;
+import org.json.JSONException;
+import org.json.JSONObject;
import android.os.Bundle;
+import android.os.Environment;
import android.os.Trace;
import android.util.Log;
import android.widget.ImageView;
@@ -31,20 +40,31 @@
private static final float ERROR_DISPLAY_THRESHOLD = 0.01f;
protected static final boolean DRAW_BITMAPS = false;
+ /**
+ * Threshold of error change required to consider a test regressed/improved
+ */
+ private static final float ERROR_CHANGE_THRESHOLD = 0.001f;
+
+ private static final float[] ERROR_CUTOFFS = {
+ 0, 0.005f, 0.01f, 0.02f, 0.05f, 0.1f, 0.25f, 0.5f, 1f, 2f
+ };
+
+ private final float[] mErrorRates = new float[ERROR_CUTOFFS.length];
+ private float mTotalTests = 0;
+ private float mTotalError = 0;
+ private int mTestsRegressed = 0;
+ private int mTestsImproved = 0;
+
private ImageView mSoftwareImageView = null;
private ImageView mHardwareImageView = null;
- private static final float[] ERROR_CUTOFFS = {0, 0.005f, 0.01f, 0.02f, 0.05f, 0.1f, 0.25f, 0.5f, 1f, 2f};
- private float[] mErrorRates = new float[ERROR_CUTOFFS.length];
- private float mTotalTests = 0;
- private float mTotalError = 0;
- public abstract static class TestCallback {
+ public abstract static class FinalCallback {
abstract void report(String name, float value);
- void complete() {}
+ void complete() {};
}
- private ArrayList<TestCallback> mTestCallbacks = new ArrayList<TestCallback>();
+ private final ArrayList<FinalCallback> mFinalCallbacks = new ArrayList<FinalCallback>();
Runnable mRunnable = new Runnable() {
@Override
@@ -64,32 +84,11 @@
float error = mErrorCalculator.calcErrorRS(mSoftwareBitmap, mHardwareBitmap);
Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
- if (error > ERROR_DISPLAY_THRESHOLD) {
- String modname = "";
- for (String s : DisplayModifier.getLastAppliedModifications()) {
- modname = modname.concat(s + ".");
- }
- Log.d(LOG_TAG, String.format("error for %s was %2.9f", modname, error));
- }
- for (int i = 0; i < ERROR_CUTOFFS.length; i++) {
- if (error <= ERROR_CUTOFFS[i]) break;
- mErrorRates[i]++;
- }
- mTotalError += error;
- mTotalTests++;
+ final String[] modifierNames = DisplayModifier.getLastAppliedModifications();
+ handleError(modifierNames, error);
if (DisplayModifier.step()) {
- for (TestCallback c : mTestCallbacks) {
- c.report("averageError", (mTotalError / mTotalTests));
- for (int i = 1; i < ERROR_CUTOFFS.length; i++) {
- c.report(String.format("error over %1.3f", ERROR_CUTOFFS[i]),
- mErrorRates[i]/mTotalTests);
- }
- c.complete();
- }
-
- Toast.makeText(getApplicationContext(), "done!", Toast.LENGTH_SHORT).show();
- finish();
+ finishTest();
} else {
mHardwareView.invalidate();
if (DRAW_BITMAPS) {
@@ -116,11 +115,186 @@
mHardwareImageView = (ImageView) findViewById(R.id.hardware_image_view);
onCreateCommon(mRunnable);
- mTestCallbacks.add(new TestCallback() {
+ beginTest();
+ }
+
+ private static class TestResult {
+ TestResult(String label, float error) {
+ mLabel = label;
+ mTotalError = error;
+ mCount = 1;
+ }
+ public void addInto(float error) {
+ mTotalError += error;
+ mCount++;
+ }
+ public float getAverage() {
+ return mTotalError / mCount;
+ }
+ final String mLabel;
+ float mTotalError;
+ int mCount;
+ }
+
+ JSONObject mOutputJson = null;
+ JSONObject mInputJson = null;
+ final HashMap<String, TestResult> mModifierResults = new HashMap<String, TestResult>();
+ final HashMap<String, TestResult> mIndividualResults = new HashMap<String, TestResult>();
+ final HashMap<String, TestResult> mModifierDiffResults = new HashMap<String, TestResult>();
+ final HashMap<String, TestResult> mIndividualDiffResults = new HashMap<String, TestResult>();
+ private void beginTest() {
+ mFinalCallbacks.add(new FinalCallback() {
+ @Override
void report(String name, float value) {
Log.d(LOG_TAG, name + " " + value);
};
});
+
+ File inputFile = new File(Environment.getExternalStorageDirectory(),
+ "CanvasCompareInput.json");
+ if (inputFile.exists() && inputFile.canRead() && inputFile.length() > 0) {
+ try {
+ FileInputStream inputStream = new FileInputStream(inputFile);
+ Log.d(LOG_TAG, "Parsing input file...");
+ StringBuffer content = new StringBuffer((int)inputFile.length());
+ byte[] buffer = new byte[1024];
+ while (inputStream.read(buffer) != -1) {
+ content.append(new String(buffer));
+ }
+ mInputJson = new JSONObject(content.toString());
+ inputStream.close();
+ Log.d(LOG_TAG, "Parsed input file with " + mInputJson.length() + " entries");
+ } catch (JSONException e) {
+ Log.e(LOG_TAG, "error parsing input json", e);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "error reading input json from sd", e);
+ }
+ }
+
+ mOutputJson = new JSONObject();
+ }
+
+ private static void logTestResultHash(String label, HashMap<String, TestResult> map) {
+ Log.d(LOG_TAG, "---------------");
+ Log.d(LOG_TAG, label + ":");
+ Log.d(LOG_TAG, "---------------");
+ TreeSet<TestResult> set = new TreeSet<TestResult>(new Comparator<TestResult>() {
+ @Override
+ public int compare(TestResult lhs, TestResult rhs) {
+ if (lhs == rhs) return 0; // don't need to worry about complex equality
+
+ int cmp = Float.compare(lhs.getAverage(), rhs.getAverage());
+ if (cmp != 0) {
+ return cmp;
+ }
+ return lhs.mLabel.compareTo(rhs.mLabel);
+ }
+ });
+
+ for (TestResult t : map.values()) {
+ set.add(t);
+ }
+
+ for (TestResult t : set.descendingSet()) {
+ if (Math.abs(t.getAverage()) > ERROR_DISPLAY_THRESHOLD) {
+ Log.d(LOG_TAG, String.format("%2.4f : %s", t.getAverage(), t.mLabel));
+ }
+ }
+ Log.d(LOG_TAG, "");
+ }
+
+ private void finishTest() {
+ for (FinalCallback c : mFinalCallbacks) {
+ c.report("averageError", (mTotalError / mTotalTests));
+ for (int i = 1; i < ERROR_CUTOFFS.length; i++) {
+ c.report(String.format("tests with error over %1.3f", ERROR_CUTOFFS[i]),
+ mErrorRates[i]);
+ }
+ if (mInputJson != null) {
+ c.report("tests regressed", mTestsRegressed);
+ c.report("tests improved", mTestsImproved);
+ }
+ c.complete();
+ }
+
+ try {
+ if (mOutputJson != null) {
+ String outputString = mOutputJson.toString(4);
+ File outputFile = new File(Environment.getExternalStorageDirectory(),
+ "CanvasCompareOutput.json");
+ FileOutputStream outputStream = new FileOutputStream(outputFile);
+ outputStream.write(outputString.getBytes());
+ outputStream.close();
+ Log.d(LOG_TAG, "Saved output file with " + mOutputJson.length() + " entries");
+ }
+ } catch (JSONException e) {
+ Log.e(LOG_TAG, "error during JSON stringify", e);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "error storing JSON output on sd", e);
+ }
+
+ logTestResultHash("Modifier change vs previous", mModifierDiffResults);
+ logTestResultHash("Invidual test change vs previous", mIndividualDiffResults);
+ logTestResultHash("Modifier average test results", mModifierResults);
+ logTestResultHash("Individual test results", mIndividualResults);
+
+ Toast.makeText(getApplicationContext(), "done!", Toast.LENGTH_SHORT).show();
+ finish();
+ }
+
+ /**
+ * Inserts the error value into all TestResult objects, associated with each of its modifiers
+ */
+ private static void addForAllModifiers(String fullName, float error, String[] modifierNames,
+ HashMap<String, TestResult> modifierResults) {
+ for (String modifierName : modifierNames) {
+ TestResult r = modifierResults.get(fullName);
+ if (r == null) {
+ modifierResults.put(modifierName, new TestResult(modifierName, error));
+ } else {
+ r.addInto(error);
+ }
+ }
+ }
+
+ private void handleError(final String[] modifierNames, final float error) {
+ String fullName = "";
+ for (String s : modifierNames) {
+ fullName = fullName.concat("." + s);
+ }
+ fullName = fullName.substring(1);
+
+ float deltaError = 0;
+ if (mInputJson != null) {
+ try {
+ deltaError = error - (float)mInputJson.getDouble(fullName);
+ } catch (JSONException e) {
+ Log.w(LOG_TAG, "Warning: unable to read from input json", e);
+ }
+ if (deltaError > ERROR_CHANGE_THRESHOLD) mTestsRegressed++;
+ if (deltaError < -ERROR_CHANGE_THRESHOLD) mTestsImproved++;
+ mIndividualDiffResults.put(fullName, new TestResult(fullName, deltaError));
+ addForAllModifiers(fullName, deltaError, modifierNames, mModifierDiffResults);
+ }
+
+ mIndividualResults.put(fullName, new TestResult(fullName, error));
+ addForAllModifiers(fullName, error, modifierNames, mModifierResults);
+
+ try {
+ if (mOutputJson != null) {
+ mOutputJson.put(fullName, error);
+ }
+ } catch (JSONException e) {
+ Log.e(LOG_TAG, "exception during JSON recording", e);
+ mOutputJson = null;
+ }
+
+ for (int i = 0; i < ERROR_CUTOFFS.length; i++) {
+ if (error <= ERROR_CUTOFFS[i]) break;
+ mErrorRates[i]++;
+ }
+ mTotalError += error;
+ mTotalTests++;
}
@Override
@@ -130,7 +304,7 @@
}
// FOR TESTING
- public void setCallback(TestCallback c) {
- mTestCallbacks.add(c);
+ public void setFinalCallback(FinalCallback c) {
+ mFinalCallbacks.add(c);
}
}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java
index 6ea8237..1ff153c 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java
@@ -1,6 +1,6 @@
package com.android.test.hwuicompare;
-import com.android.test.hwuicompare.AutomaticActivity.TestCallback;
+import com.android.test.hwuicompare.AutomaticActivity.FinalCallback;
import android.os.Bundle;
import android.test.ActivityInstrumentationTestCase2;
@@ -18,7 +18,7 @@
super.setUp();
mBundle = new Bundle();
mActivity = getActivity();
- mActivity.setCallback(new TestCallback() {
+ mActivity.setFinalCallback(new FinalCallback() {
@Override
void report(String key, float value) {
diff --git a/tests/TransitionTests/Android.mk b/tests/TransitionTests/Android.mk
new file mode 100644
index 0000000..22fa638
--- /dev/null
+++ b/tests/TransitionTests/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := TransitionTests
+
+LOCAL_STATIC_JAVA_LIBRARIES += android-common
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/TransitionTests/AndroidManifest.xml b/tests/TransitionTests/AndroidManifest.xml
new file mode 100644
index 0000000..8cd36bf
--- /dev/null
+++ b/tests/TransitionTests/AndroidManifest.xml
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.transitiontests"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <application
+ android:icon="@drawable/icon"
+ android:label="@string/app_name"
+ android:hardwareAccelerated="true"
+ android:theme="@style/AppTheme">
+ <activity android:label="@string/states_test1"
+ android:name="ScenesTestv21">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="@string/states_test_auto_targets"
+ android:name="ScenesTestAutoTargets">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="@string/states_test_auto_transition"
+ android:name="ScenesTestAutoTransition">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="@string/states_test_auto_transition2"
+ android:name="ScenesTestAutoTransition2">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="@string/contacts_expansion"
+ android:name="ContactsExpansion">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo0"
+ android:name="Demo0">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo1"
+ android:name="Demo1">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo2"
+ android:name="Demo2">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo3"
+ android:name="Demo3">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo4"
+ android:name="Demo4">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo5"
+ android:name="Demo5">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="LoginActivity"
+ android:name="LoginActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="LoginActivityFromResources"
+ android:name="LoginActivityFromResources">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="OverlayTest"
+ android:name="OverlayTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ResourceLoadingTest"
+ android:name="ResourceLoadingTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="FadingTest"
+ android:name="FadingTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="UniqueIds"
+ android:name="UniqueIds">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="HitRectBug"
+ android:name="HitRectBug">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="SequenceTest"
+ android:name="SequenceTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="SequenceTestSimple"
+ android:name="SequenceTestSimple">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ChangingText"
+ android:name="ChangingText">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ClippingText"
+ android:name="ClippingText">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ListViewAddRemove"
+ android:name="ListViewAddRemove">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ListViewAddRemoveNoTransition"
+ android:name="ListViewAddRemoveNoTransition">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="CrossFadeDemo"
+ android:name="CrossFadeDemo">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Reparenting"
+ android:name="Reparenting">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="SurfaceAndTextureViews"
+ android:name="SurfaceAndTextureViews">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="InstanceTargets"
+ android:name="InstanceTargets">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ </application>
+
+</manifest>
diff --git a/tests/TransitionTests/res/drawable-hdpi/icon.png b/tests/TransitionTests/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..8074c4c
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-ldpi/icon.png b/tests/TransitionTests/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..1095584
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-mdpi/icon.png b/tests/TransitionTests/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-nodpi/arrow_thumbnail.png b/tests/TransitionTests/res/drawable-nodpi/arrow_thumbnail.png
new file mode 100644
index 0000000..5cae8f2
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-nodpi/arrow_thumbnail.png
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-nodpi/self_portrait_square.jpg b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square.jpg
new file mode 100644
index 0000000..8cce1c1
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square.jpg
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_100.jpg b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_100.jpg
new file mode 100644
index 0000000..26c0a85
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_100.jpg
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_200.jpg b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_200.jpg
new file mode 100644
index 0000000..f18ae5b
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_200.jpg
Binary files differ
diff --git a/tests/TransitionTests/res/layout/activity_login.xml b/tests/TransitionTests/res/layout/activity_login.xml
new file mode 100644
index 0000000..2aaafc0
--- /dev/null
+++ b/tests/TransitionTests/res/layout/activity_login.xml
@@ -0,0 +1,86 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/password"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignRight="@+id/username"
+ android:layout_below="@+id/username"
+ android:layout_marginTop="30dp"
+ android:textColor="#7f7f7f"
+ android:text="@string/password"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/usernameEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/username"
+ android:layout_marginLeft="14dp"
+ android:layout_toRightOf="@+id/username"
+ android:ems="10" />
+
+ <TextView
+ android:id="@+id/username"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="20dp"
+ android:layout_marginTop="51dp"
+ android:text="@string/username"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/passwordEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/password"
+ android:layout_alignBottom="@+id/password"
+ android:layout_alignLeft="@+id/usernameEdit"
+ android:layout_alignParentRight="true"
+ android:editable="false"
+ android:ems="10" >
+
+ <requestFocus />
+ </EditText>
+
+ <Button
+ android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="14dp"
+ android:layout_toLeftOf="@+id/passwordEdit"
+ android:onClick="sendMessage"
+ android:text="@string/cancel" />
+
+ <Button
+ android:id="@+id/submit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/cancel"
+ android:layout_alignBottom="@+id/cancel"
+ android:layout_alignLeft="@+id/passwordEdit"
+ android:layout_marginLeft="41dp"
+ android:onClick="sendMessage"
+ android:text="@string/submit" />
+
+ <TextView
+ android:id="@+id/newuser"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/passwordEdit"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="16dp"
+ android:textColor="#0000ff"
+ android:text="@string/new_user"
+ android:clickable="true"
+ android:onClick="sendMessage"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="italic" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/button_template.xml b/tests/TransitionTests/res/layout/button_template.xml
new file mode 100644
index 0000000..9b867f2
--- /dev/null
+++ b/tests/TransitionTests/res/layout/button_template.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ android:onClick="sendMessage"
+ android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+</Button>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/changing_text_1.xml b/tests/TransitionTests/res/layout/changing_text_1.xml
new file mode 100644
index 0000000..88086a3
--- /dev/null
+++ b/tests/TransitionTests/res/layout/changing_text_1.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText1"
+ android:background="#8f8f8f"
+ android:id="@+id/textview1"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/buttona"
+ android:text="@string/button"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText1"
+ android:background="#8f8f8f"
+ android:id="@+id/textview2"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/buttonb"
+ android:text="@string/button"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/changing_text_2.xml b/tests/TransitionTests/res/layout/changing_text_2.xml
new file mode 100644
index 0000000..91fdfef
--- /dev/null
+++ b/tests/TransitionTests/res/layout/changing_text_2.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText2"
+ android:background="#8f8f8f"
+ android:id="@+id/textview1"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/buttona"
+ android:text="@string/button"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText2"
+ android:background="#8f8f8f"
+ android:id="@+id/textview2"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/buttonb"
+ android:text="@string/button"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/clipping_text_1.xml b/tests/TransitionTests/res/layout/clipping_text_1.xml
new file mode 100644
index 0000000..5fe45ab
--- /dev/null
+++ b/tests/TransitionTests/res/layout/clipping_text_1.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText1"
+ android:background="#8f8f8f"
+ android:id="@+id/textview1"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/clipping_text_2.xml b/tests/TransitionTests/res/layout/clipping_text_2.xml
new file mode 100644
index 0000000..3a0e3f4
--- /dev/null
+++ b/tests/TransitionTests/res/layout/clipping_text_2.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="500dip"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText2"
+ android:background="#8f8f8f"
+ android:id="@+id/textview1"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/contact_collapsed.xml b/tests/TransitionTests/res/layout/contact_collapsed.xml
new file mode 100644
index 0000000..bfa97df
--- /dev/null
+++ b/tests/TransitionTests/res/layout/contact_collapsed.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:background="#fff"
+ android:layout_width="fill_parent"
+ android:id="@+id/topContainer"
+ android:layout_height="wrap_content">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_arrow"
+ android:src="@drawable/arrow_thumbnail"/>
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_picture"/>
+ <LinearLayout android:orientation="vertical"
+ android:layout_width="0dip"
+ android:id="@+id/itemContainer"
+ android:background="#ccc"
+ android:layout_weight="1"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_name"/>
+ <LinearLayout android:orientation="vertical"
+ android:visibility="gone"
+ android:id="@+id/expanded_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_street"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_city"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_phone"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/contact_email"/>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/contact_expanded.xml b/tests/TransitionTests/res/layout/contact_expanded.xml
new file mode 100644
index 0000000..4007d4a
--- /dev/null
+++ b/tests/TransitionTests/res/layout/contact_expanded.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:background="#fff"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_arrow"
+ android:src="@drawable/arrow_thumbnail"/>
+ <LinearLayout android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_name"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_street"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_city"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_phone"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/contact_email"/>
+ </LinearLayout>
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_picture"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/contacts_list.xml b/tests/TransitionTests/res/layout/contacts_list.xml
new file mode 100644
index 0000000..d38704c
--- /dev/null
+++ b/tests/TransitionTests/res/layout/contacts_list.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/contactsContainer"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/crossfade.xml b/tests/TransitionTests/res/layout/crossfade.xml
new file mode 100644
index 0000000..d766ce1
--- /dev/null
+++ b/tests/TransitionTests/res/layout/crossfade.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/container">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:onClick="sendMessage"
+ android:id="@+id/button"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText1"
+ android:id="@+id/textview"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText1"
+ android:id="@+id/textview1"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/someText"
+ android:id="@+id/textview2"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/crossfade_1.xml b/tests/TransitionTests/res/layout/crossfade_1.xml
new file mode 100644
index 0000000..4cc5bc9
--- /dev/null
+++ b/tests/TransitionTests/res/layout/crossfade_1.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/container">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:onClick="sendMessage"
+ android:id="@+id/button"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText2"
+ android:id="@+id/textview"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText2"
+ android:id="@+id/textview1"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/someText"
+ android:id="@+id/textview2"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/fading_test.xml b/tests/TransitionTests/res/layout/fading_test.xml
new file mode 100644
index 0000000..3728b5e
--- /dev/null
+++ b/tests/TransitionTests/res/layout/fading_test.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/removingButton"
+ android:id="@+id/removingButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/invisibleButton"
+ android:id="@+id/invisibleButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/goneButton"
+ android:id="@+id/goneButton"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"
+ android:text="@string/button"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/fading_test_scene_2.xml b/tests/TransitionTests/res/layout/fading_test_scene_2.xml
new file mode 100644
index 0000000..baf5b4d
--- /dev/null
+++ b/tests/TransitionTests/res/layout/fading_test_scene_2.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/invisibleButton"
+ android:visibility="invisible"
+ android:id="@+id/invisibleButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/goneButton"
+ android:visibility="gone"
+ android:id="@+id/goneButton"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"
+ android:text="@string/button"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/fading_test_simple.xml b/tests/TransitionTests/res/layout/fading_test_simple.xml
new file mode 100644
index 0000000..d201eca
--- /dev/null
+++ b/tests/TransitionTests/res/layout/fading_test_simple.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/removingButton"
+ android:id="@+id/removingButton"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/fading_test_simple2.xml b/tests/TransitionTests/res/layout/fading_test_simple2.xml
new file mode 100644
index 0000000..80f3f94
--- /dev/null
+++ b/tests/TransitionTests/res/layout/fading_test_simple2.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="50dip"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/removingButton"
+ android:visibility="invisible"
+ android:id="@+id/removingButton"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/incorrect_password.xml b/tests/TransitionTests/res/layout/incorrect_password.xml
new file mode 100644
index 0000000..af59618
--- /dev/null
+++ b/tests/TransitionTests/res/layout/incorrect_password.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <Button
+ android:id="@+id/okay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="164dp"
+ android:onClick="sendMessage"
+ android:text="@string/okay" />
+
+ <TextView
+ android:id="@+id/incorrectpassword"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/okay"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="93dp"
+ android:text="@string/incorrect_password"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@+id/tryagain"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/incorrectpassword"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="35dp"
+ android:text="@string/try_again"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/instance_targets.xml b/tests/TransitionTests/res/layout/instance_targets.xml
new file mode 100644
index 0000000..5677d52
--- /dev/null
+++ b/tests/TransitionTests/res/layout/instance_targets.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/container">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:onClick="sendMessage"
+ android:id="@+id/button0"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/button0"
+ android:onClick="sendMessage"
+ android:id="@+id/button1"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/button1"
+ android:onClick="sendMessage"
+ android:id="@+id/button2"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/button2"
+ android:onClick="sendMessage"
+ android:id="@+id/button3"/>
+
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/list_view_add_remove.xml b/tests/TransitionTests/res/layout/list_view_add_remove.xml
new file mode 100644
index 0000000..230511f
--- /dev/null
+++ b/tests/TransitionTests/res/layout/list_view_add_remove.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ListView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/listview"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/login_password.xml b/tests/TransitionTests/res/layout/login_password.xml
new file mode 100644
index 0000000..1e75694
--- /dev/null
+++ b/tests/TransitionTests/res/layout/login_password.xml
@@ -0,0 +1,86 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".LoginActivity" >
+
+ <TextView
+ android:id="@+id/password"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignRight="@+id/username"
+ android:layout_below="@+id/username"
+ android:layout_marginTop="30dp"
+ android:text="@string/password"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/usernameEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/username"
+ android:layout_marginLeft="14dp"
+ android:layout_toRightOf="@+id/username"
+ android:ems="10" />
+
+ <TextView
+ android:id="@+id/username"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="20dp"
+ android:layout_marginTop="51dp"
+ android:text="@string/username"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/passwordEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/password"
+ android:layout_alignBottom="@+id/password"
+ android:layout_alignLeft="@+id/usernameEdit"
+ android:layout_alignParentRight="true"
+ android:ems="10" >
+
+ <requestFocus />
+ </EditText>
+
+ <Button
+ android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="14dp"
+ android:layout_toLeftOf="@+id/passwordEdit"
+ android:onClick="sendMessage"
+ android:text="@string/cancel" />
+
+ <Button
+ android:id="@+id/submit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/cancel"
+ android:layout_alignBottom="@+id/cancel"
+ android:layout_alignLeft="@+id/passwordEdit"
+ android:layout_marginLeft="41dp"
+ android:onClick="sendMessage"
+ android:text="@string/submit" />
+
+ <TextView
+ android:id="@+id/newuser"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/passwordEdit"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="16dp"
+ android:textColor="#0000ff"
+ android:text="@string/new_user"
+ android:clickable="true"
+ android:onClick="sendMessage"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="italic" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/main.xml b/tests/TransitionTests/res/layout/main.xml
new file mode 100644
index 0000000..b42318e
--- /dev/null
+++ b/tests/TransitionTests/res/layout/main.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/container">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/new_user.xml b/tests/TransitionTests/res/layout/new_user.xml
new file mode 100644
index 0000000..f8dfab06
--- /dev/null
+++ b/tests/TransitionTests/res/layout/new_user.xml
@@ -0,0 +1,92 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/container"
+ tools:context=".LoginActivity" >
+
+ <TextView
+ android:id="@+id/password"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignRight="@+id/username"
+ android:layout_below="@+id/username"
+ android:layout_marginTop="30dp"
+ android:text="@string/password"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/usernameEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/username"
+ android:layout_marginLeft="14dp"
+ android:layout_toRightOf="@+id/username"
+ android:ems="10" />
+
+ <TextView
+ android:id="@+id/username"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="20dp"
+ android:layout_marginTop="51dp"
+ android:text="@string/username"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/passwordEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/password"
+ android:layout_alignBottom="@+id/password"
+ android:layout_alignLeft="@+id/usernameEdit"
+ android:layout_alignParentRight="true"
+ android:ems="10" >
+
+ <requestFocus />
+ </EditText>
+
+ <Button
+ android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="14dp"
+ android:layout_toLeftOf="@+id/passwordEdit"
+ android:onClick="sendMessage"
+ android:text="@string/cancel" />
+
+ <Button
+ android:id="@+id/submit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/cancel"
+ android:layout_alignBottom="@+id/cancel"
+ android:layout_alignLeft="@+id/passwordEdit"
+ android:layout_marginLeft="41dp"
+ android:onClick="sendMessage"
+ android:text="@string/submit" />
+
+ <EditText
+ android:id="@+id/retypeEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/retype"
+ android:layout_alignBottom="@+id/retype"
+ android:layout_alignLeft="@+id/passwordEdit"
+ android:layout_alignParentRight="true"
+ android:ems="10" />
+
+ <TextView
+ android:id="@+id/retype"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/passwordEdit"
+ android:layout_marginTop="26dp"
+ android:layout_toLeftOf="@+id/usernameEdit"
+ android:text="@string/retype"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/overlay_test.xml b/tests/TransitionTests/res/layout/overlay_test.xml
new file mode 100644
index 0000000..edd0393
--- /dev/null
+++ b/tests/TransitionTests/res/layout/overlay_test.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"
+ android:text="@string/start"
+ android:onClick="onClick"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/fadingButton"
+ android:id="@+id/fadingButton"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/reparenting.xml b/tests/TransitionTests/res/layout/reparenting.xml
new file mode 100644
index 0000000..b3bfbb9
--- /dev/null
+++ b/tests/TransitionTests/res/layout/reparenting.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/container">
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/container1"/>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/container2"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/resources_test_layout.xml b/tests/TransitionTests/res/layout/resources_test_layout.xml
new file mode 100644
index 0000000..48affa0
--- /dev/null
+++ b/tests/TransitionTests/res/layout/resources_test_layout.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView
+ src="@drawable/icon"/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/results_screen.xml b/tests/TransitionTests/res/layout/results_screen.xml
new file mode 100644
index 0000000..8550a5e
--- /dev/null
+++ b/tests/TransitionTests/res/layout/results_screen.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:background="#7f7f7f"
+ android:id="@+id/container">
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|right"
+ android:id="@+id/searchContainer">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/searchText"
+ android:id="@+id/searchText"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/searchButton"
+ android:onClick="sendMessage"
+ android:id="@+id/searchButton"/>
+ </LinearLayout>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/resultsTitle"
+ android:id="@+id/resultsText"/>
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:id="@+id/resultsList">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/search_screen.xml b/tests/TransitionTests/res/layout/search_screen.xml
new file mode 100644
index 0000000..947702b
--- /dev/null
+++ b/tests/TransitionTests/res/layout/search_screen.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:background="#000000"
+ android:id="@+id/container">
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:id="@+id/searchContainer">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/searchText"
+ android:id="@+id/searchText"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/searchButton"
+ android:onClick="sendMessage"
+ android:id="@+id/searchButton"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/success.xml b/tests/TransitionTests/res/layout/success.xml
new file mode 100644
index 0000000..9c265ef
--- /dev/null
+++ b/tests/TransitionTests/res/layout/success.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <TextView
+ android:id="@+id/loginsuccessful"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="93dp"
+ android:text="@string/login_successful"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@+id/firstactivityscreen"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/loginsuccessful"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="35dp"
+ android:text="@string/first_activity_screen"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <Button
+ android:id="@+id/reset"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="164dp"
+ android:onClick="sendMessage"
+ android:text="@string/reset" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/surface_texture_views.xml b/tests/TransitionTests/res/layout/surface_texture_views.xml
new file mode 100644
index 0000000..9260bc0
--- /dev/null
+++ b/tests/TransitionTests/res/layout/surface_texture_views.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:text="@string/toggle"
+ android:id="@+id/toggleButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/unique_id_test.xml b/tests/TransitionTests/res/layout/unique_id_test.xml
new file mode 100644
index 0000000..9b7eb10
--- /dev/null
+++ b/tests/TransitionTests/res/layout/unique_id_test.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/username_taken.xml b/tests/TransitionTests/res/layout/username_taken.xml
new file mode 100644
index 0000000..9484e69
--- /dev/null
+++ b/tests/TransitionTests/res/layout/username_taken.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <Button
+ android:id="@+id/okay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="164dp"
+ android:onClick="sendMessage"
+ android:text="@string/okay" />
+
+ <TextView
+ android:id="@+id/usernametaken"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/okay"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="93dp"
+ android:text="@string/username_taken"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@+id/textView2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/usernametaken"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="35dp"
+ android:text="@string/try_again"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/incorrect_password_scene.xml b/tests/TransitionTests/res/scene/incorrect_password_scene.xml
new file mode 100644
index 0000000..a31ad22
--- /dev/null
+++ b/tests/TransitionTests/res/scene/incorrect_password_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/incorrect_password"/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/login_scene.xml b/tests/TransitionTests/res/scene/login_scene.xml
new file mode 100644
index 0000000..b258303
--- /dev/null
+++ b/tests/TransitionTests/res/scene/login_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/activity_login"/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/my_scene.xml b/tests/TransitionTests/res/scene/my_scene.xml
new file mode 100644
index 0000000..b8278c9
--- /dev/null
+++ b/tests/TransitionTests/res/scene/my_scene.xml
@@ -0,0 +1,3 @@
+<scene >
+
+</scene>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/new_user_scene.xml b/tests/TransitionTests/res/scene/new_user_scene.xml
new file mode 100644
index 0000000..d6e5f0f
--- /dev/null
+++ b/tests/TransitionTests/res/scene/new_user_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/new_user"/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/password_scene.xml b/tests/TransitionTests/res/scene/password_scene.xml
new file mode 100644
index 0000000..99a0038
--- /dev/null
+++ b/tests/TransitionTests/res/scene/password_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/login_password"/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/results_scene.xml b/tests/TransitionTests/res/scene/results_scene.xml
new file mode 100644
index 0000000..9c9fc69
--- /dev/null
+++ b/tests/TransitionTests/res/scene/results_scene.xml
@@ -0,0 +1,3 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/results_screen">
+</scene>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/search_scene.xml b/tests/TransitionTests/res/scene/search_scene.xml
new file mode 100644
index 0000000..742cd57
--- /dev/null
+++ b/tests/TransitionTests/res/scene/search_scene.xml
@@ -0,0 +1,3 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/search_screen">
+</scene>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/success_scene.xml b/tests/TransitionTests/res/scene/success_scene.xml
new file mode 100644
index 0000000..3d76d89
--- /dev/null
+++ b/tests/TransitionTests/res/scene/success_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/success"/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/username_taken_scene.xml b/tests/TransitionTests/res/scene/username_taken_scene.xml
new file mode 100644
index 0000000..b2f050e
--- /dev/null
+++ b/tests/TransitionTests/res/scene/username_taken_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/username_taken"/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/colorizer_transition.xml b/tests/TransitionTests/res/transition/colorizer_transition.xml
new file mode 100644
index 0000000..731d7ee
--- /dev/null
+++ b/tests/TransitionTests/res/transition/colorizer_transition.xml
@@ -0,0 +1,6 @@
+<recolor xmlns:android="http://schemas.android.com/apk/res/android">>
+ <targets>
+ <target android:targetID="@id/password"/>
+ <target android:targetID="@id/passwordEdit"/>
+ </targets>
+</recolor>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/fader.xml b/tests/TransitionTests/res/transition/fader.xml
new file mode 100644
index 0000000..c71fd94
--- /dev/null
+++ b/tests/TransitionTests/res/transition/fader.xml
@@ -0,0 +1 @@
+<fade/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/login_slider_transition.xml b/tests/TransitionTests/res/transition/login_slider_transition.xml
new file mode 100644
index 0000000..dbdd6e9
--- /dev/null
+++ b/tests/TransitionTests/res/transition/login_slider_transition.xml
@@ -0,0 +1,22 @@
+<transitionGroup xmlns:android="http://schemas.android.com/apk/res/android">
+ <slide>
+ <targets>
+ <target android:targetID="@id/retype"/>
+ <target android:targetID="@id/retypeEdit"/>
+ </targets>
+ </slide>
+ <recolor>
+ <targets>
+ <target android:targetID="@id/password"/>
+ <target android:targetID="@id/passwordEdit"/>
+ </targets>
+ </recolor>
+ <fade/>
+</transitionGroup>
+
+<!--
+ TransitionGroup slider = new TransitionGroup();
+ slider.addTransition(new Slide(R.id.retype, R.id.retypeEdit));
+ slider.addTransition(new Recolor(R.id.password, R.id.passwordEdit));
+ slider.addTransition(new Fade());
+-->
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/login_transition_mgr.xml b/tests/TransitionTests/res/transition/login_transition_mgr.xml
new file mode 100644
index 0000000..8a8b9e9
--- /dev/null
+++ b/tests/TransitionTests/res/transition/login_transition_mgr.xml
@@ -0,0 +1,39 @@
+<transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
+ <transition fromScene="@scene/login_scene" toScene="@scene/new_user_scene"
+ transition="@transition/login_slider_transition"/>
+ <transition fromScene="@scene/password_scene" toScene="@scene/new_user_scene"
+ transition="@transition/login_slider_transition"/>
+ <transition fromScene="@scene/new_user_scene" toScene="@scene/login_scene"
+ transition="@transition/login_slider_transition"/>
+ <transition fromScene="@scene/new_user_scene" toScene="@scene/password_scene"
+ transition="@transition/login_slider_transition"/>
+ <transition fromScene="@scene/login_scene" toScene="@scene/password_scene"
+ transition="@transition/colorizer_transition"/>
+ <transition fromScene="@scene/password_scene" toScene="@scene/login_scene"
+ transition="@transition/colorizer_transition"/>
+</transitionManager>
+
+ <!--
+ mLoginScene = new Scene(this, mSceneRoot, R.layout.activity_login);
+ mPasswordScene = new Scene(this, mSceneRoot, R.layout.login_password);
+ mIncorrectPasswordScene = new Scene(this, mSceneRoot, R.layout.incorrect_password);
+ mUsernameTakenScene = new Scene(this, mSceneRoot, R.layout.username_taken);
+ mSuccessScene = new Scene(this, mSceneRoot, R.layout.success);
+ mNewUserScene = new Scene(this, mSceneRoot, R.layout.new_user);
+
+ mTransitionManager = new TransitionManager();
+ // Custom transitions in/out of NewUser screen - slide in the 2nd password UI
+ TransitionGroup slider = new TransitionGroup();
+ slider.addTransition(new Slide(R.id.retype, R.id.retypeEdit));
+ slider.addTransition(new Recolor(R.id.password, R.id.passwordEdit));
+ slider.addTransition(new Fade());
+ mTransitionManager.setTransition(mLoginScene, mNewUserScene, slider);
+ mTransitionManager.setTransition(mPasswordScene, mNewUserScene, slider);
+ mTransitionManager.setTransition(mNewUserScene, mLoginScene, slider);
+ mTransitionManager.setTransition(mNewUserScene, mPasswordScene, slider);
+
+ // Custom transitions with recoloring password field
+ Transition colorizer = new Recolor(R.id.password, R.id.passwordEdit);
+ mTransitionManager.setTransition(mLoginScene, mPasswordScene, colorizer);
+ mTransitionManager.setTransition(mPasswordScene, mLoginScene, colorizer);
+-->
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/mover.xml b/tests/TransitionTests/res/transition/mover.xml
new file mode 100644
index 0000000..3c47606
--- /dev/null
+++ b/tests/TransitionTests/res/transition/mover.xml
@@ -0,0 +1 @@
+<move/>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/mover_fader.xml b/tests/TransitionTests/res/transition/mover_fader.xml
new file mode 100644
index 0000000..8b805e9
--- /dev/null
+++ b/tests/TransitionTests/res/transition/mover_fader.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<transitionGroup xmlns:android="http://schemas.android.com/apk/res/android">
+ <fade/>
+ <move/>
+</transitionGroup>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/my_transition.xml b/tests/TransitionTests/res/transition/my_transition.xml
new file mode 100644
index 0000000..14c91b9
--- /dev/null
+++ b/tests/TransitionTests/res/transition/my_transition.xml
@@ -0,0 +1,20 @@
+<transitionGroup xmlns:android="http://schemas.android.com/apk/res/android">
+ <fade></fade>
+ <transitionGroup>
+ <move android:duration="500">
+ <targets>
+ <target android:targetID="@id/container"/>
+ <target android:targetID="@id/resultsList"/>
+ </targets>
+ </move>
+ <transitionGroup>
+ <targets>
+ <target android:targetID="@id/container"/>
+ <target android:targetID="@id/resultsList"/>
+ </targets>
+ <fade android:startOffset="25"/>
+ </transitionGroup>
+ <recolor/>
+ </transitionGroup>
+ <move/>
+</transitionGroup>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/my_transition_mgr.xml b/tests/TransitionTests/res/transition/my_transition_mgr.xml
new file mode 100644
index 0000000..ca9705a
--- /dev/null
+++ b/tests/TransitionTests/res/transition/my_transition_mgr.xml
@@ -0,0 +1,6 @@
+<transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
+ <transition fromScene="@scene/search_scene" toScene="@scene/results_scene"
+ transition="@transition/mover_fader"/>
+ <transition fromScene="@scene/results_scene" toScene="@scene/search_scene"
+ transition="@transition/mover_fader"/>
+</transitionManager>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/values-v11/styles.xml b/tests/TransitionTests/res/values-v11/styles.xml
new file mode 100644
index 0000000..541752f
--- /dev/null
+++ b/tests/TransitionTests/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+ <!--
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!-- API 11 theme customizations can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/values-v14/styles.xml b/tests/TransitionTests/res/values-v14/styles.xml
new file mode 100644
index 0000000..f20e015
--- /dev/null
+++ b/tests/TransitionTests/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/values/strings.xml b/tests/TransitionTests/res/values/strings.xml
new file mode 100644
index 0000000..e251d5c
--- /dev/null
+++ b/tests/TransitionTests/res/values/strings.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">StatesTest</string>
+ <string name="states_test1">StatesTestv21</string>
+ <string name="states_test_auto_targets">StatesTestAutoTargets</string>
+ <string name="states_test_auto_transition">StatesTestAutoTransition</string>
+ <string name="states_test_auto_transition2">StatesTestAutoTransition2</string>
+ <string name="contacts_expansion">ContactsExpansion</string>
+ <string name="states_test3">StatesTest3</string>
+ <string name="states_test4">StatesTest4</string>
+ <string name="states_test5">StatesTest5</string>
+ <string name="states_test6">StatesTest6</string>
+ <string name="button">Button</string>
+ <string name="searchButton">Search</string>
+ <string name="searchText">This is some text</string>
+ <string name="resultsTitle">Search Results</string>
+ <string name="placeholder">Blah Blah Blah</string>
+ <string name="username">Username:</string>
+ <string name="password">Password:</string>
+ <string name="retype">Retype:</string>
+ <string name="new_user">New User?</string>
+ <string name="incorrect_password">Incorrect password:</string>
+ <string name="try_again">Please try again.</string>
+ <string name="login_successful">Success!</string>
+ <string name="first_activity_screen">First activity screen</string>
+ <string name="username_taken">Username taken:</string>
+ <string name="cancel">Cancel</string>
+ <string name="submit">Submit</string>
+ <string name="okay">Okay</string>
+ <string name="reset">Reset</string>
+ <string name="fadingButton">Fading Button</string>
+ <string name="removingButton">Removing Button</string>
+ <string name="invisibleButton">invisible Button</string>
+ <string name="goneButton">Gone Button</string>
+ <string name="start">Start</string>
+ <string name="toggle">Toggle State</string>
+ <string name="someText">This is some text</string>
+ <string name="shortText1">This is some short text</string>
+ <string name="shortText2">Not much text here</string>
+ <string name="longText1">This is the beginning of the Spring of my discontent. In the event of a real emergency, you would be notified by email. Fear not, for death comes swiftly.</string>
+ <string name="longText2">When do we get to eat? I like all things, especially following strong leaders, and mangy cats. Break glass in emergency. The purpose of a framework is to provide the facilities and functionality of a powerful toolkit with the simplicity of a refrigerator.</string>
+</resources>
diff --git a/tests/TransitionTests/res/values/styles.xml b/tests/TransitionTests/res/values/styles.xml
new file mode 100644
index 0000000..4a10ca4
--- /dev/null
+++ b/tests/TransitionTests/res/values/styles.xml
@@ -0,0 +1,20 @@
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ChangingText.java b/tests/TransitionTests/src/com/android/transitiontests/ChangingText.java
new file mode 100644
index 0000000..ed9b46a
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ChangingText.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.TextChange;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+public class ChangingText extends Activity {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ Fade fader;
+ TransitionGroup mChanger;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.changing_text_1);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mScene1 = new Scene(mSceneRoot, R.layout.changing_text_1, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.changing_text_2, this);
+
+ mChanger = new TransitionGroup(TransitionGroup.TOGETHER);
+ mChanger.addTransitions(new Move(), new TextChange());
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2, mChanger);
+ } else {
+ TransitionManager.go(mScene1, mChanger);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ClippingText.java b/tests/TransitionTests/src/com/android/transitiontests/ClippingText.java
new file mode 100644
index 0000000..0ecf1f4
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ClippingText.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.TextChange;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+public class ClippingText extends Activity {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ // static Fade sFade = new Fade(R.id.removingButton, R.id.invisibleButton, R.id.goneButton);
+ Fade fader;
+ TransitionGroup mChanger;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.clipping_text_1);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mScene1 = new Scene(mSceneRoot, R.layout.clipping_text_1, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.clipping_text_2, this);
+
+ mChanger = new TransitionGroup(TransitionGroup.TOGETHER);
+ Move move = new Move();
+ move.setResizeClip(true);
+ mChanger.addTransitions(move, new TextChange());
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2, mChanger);
+ } else {
+ TransitionManager.go(mScene1, mChanger);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ContactsExpansion.java b/tests/TransitionTests/src/com/android/transitiontests/ContactsExpansion.java
new file mode 100644
index 0000000..55a96a5
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ContactsExpansion.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Scene;
+import android.view.transition.Transition;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.view.transition.Crossfade;
+import android.view.transition.Move;
+import android.view.transition.Rotate;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+public class ContactsExpansion extends Activity {
+
+ String contactsData[] = {
+ "Alan Green", "56 Bob Street", "Boston, MA 02134", "617-555-5555", "blatt@blatt.com",
+ "Bob Foonman", "92 The Avenue", "Chico, CA 78456", "510-555-5556", "bob@jerk.com",
+ "Tracey Sue", "95 Houses Street", "San Jose, CA 96504", "415-555-5557", "ts@thing.com",
+ };
+
+ View currentItem = null;
+
+ TransitionGroup mMyAutoTransition = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.contacts_list);
+ ViewGroup contactsContainer = (ViewGroup) findViewById(R.id.contactsContainer);
+
+ int contactsIndex = 0;
+ addContact(contactsContainer, contactsIndex, R.drawable.self_portrait_square_100);
+ contactsIndex += 5;
+ addContact(contactsContainer, contactsIndex, R.drawable.self_portrait_square_100);
+ contactsIndex += 5;
+ addContact(contactsContainer, contactsIndex, R.drawable.self_portrait_square_100);
+
+ }
+
+ private void addContact(ViewGroup container, int dataIndex, int thumbnailID) {
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View contactItem = inflater.inflate(R.layout.contact_collapsed, container, false);
+ ImageView thumbnailView = (ImageView) contactItem.findViewById(R.id.contact_picture);
+ thumbnailView.setImageResource(thumbnailID);
+ ((TextView)contactItem.findViewById(R.id.contact_name)).setText(contactsData[dataIndex++]);
+ ((TextView)contactItem.findViewById(R.id.contact_street)).
+ setText(contactsData[dataIndex++]);
+ ((TextView)contactItem.findViewById(R.id.contact_city)).setText(contactsData[dataIndex++]);
+ ((TextView)contactItem.findViewById(R.id.contact_phone)).setText(contactsData[dataIndex++]);
+ ((TextView)contactItem.findViewById(R.id.contact_email)).setText(contactsData[dataIndex++]);
+ container.addView(contactItem);
+
+ final TransitionGroup myTransition = new TransitionGroup();
+ myTransition.addTransitions(new Fade(Fade.IN),
+ new Rotate().setTargetIds(R.id.contact_arrow),
+ new Move(), new Fade(Fade.OUT),
+ new Crossfade().setTargetIds(R.id.contact_picture));
+ final ToggleScene toggleScene = new ToggleScene(container, myTransition);
+ contactItem.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ currentItem = v;
+ toggleScene.changeToScene();
+ }
+ });
+ }
+
+ class ToggleScene {
+ boolean expanded = false;
+ Scene mScene;
+ Transition mTransition;
+
+ ToggleScene(ViewGroup rootView, Transition transition) {
+ mScene = new Scene(rootView);
+ mTransition = transition;
+ mScene.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ if (currentItem != null) {
+ System.out.println("onsceneChanged: currentItem = " + currentItem);
+ View expandedContainer = currentItem.findViewById(R.id.expanded_info);
+ expandedContainer.setVisibility(expanded ? View.GONE : View.VISIBLE);
+ ImageView thumbnailView =
+ (ImageView) currentItem.findViewById(R.id.contact_picture);
+ thumbnailView.setImageResource(expanded ? R.drawable.self_portrait_square_100 :
+ R.drawable.self_portrait_square_200);
+ ImageView arrow = (ImageView) currentItem.findViewById(R.id.contact_arrow);
+ arrow.setRotation(expanded ? 0 : 90);
+ expanded = !expanded;
+ }
+ }
+ });
+ }
+
+ void changeToScene() {
+ TransitionManager.go(mScene, mTransition);
+ }
+ };
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.java b/tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.java
new file mode 100644
index 0000000..c428a73
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Crossfade;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class CrossFadeDemo extends Activity {
+
+ ViewGroup mSceneRoot;
+ static int mCurrentScene;
+ Scene mScene1, mScene2;
+ TransitionManager mTransitionManager;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.crossfade);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mScene1 = new Scene(mSceneRoot, R.layout.crossfade, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.crossfade_1, this);
+
+ Crossfade crossfade = new Crossfade();
+ crossfade.setTargetIds(R.id.textview, R.id.textview1, R.id.textview2);
+ mTransitionManager = new TransitionManager();
+ TransitionGroup moveCrossFade = new TransitionGroup();
+ moveCrossFade.addTransitions(crossfade, new Move());
+ mTransitionManager.setTransition(mScene1, moveCrossFade);
+ mTransitionManager.setTransition(mScene2, moveCrossFade);
+ mCurrentScene = 1;
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == 1) {
+ mTransitionManager.transitionTo(mScene2);
+ mCurrentScene = 2;
+ } else {
+ mTransitionManager.transitionTo(mScene1);
+ mCurrentScene = 1;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo0.java b/tests/TransitionTests/src/com/android/transitiontests/Demo0.java
new file mode 100644
index 0000000..55bf956
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo0.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.transitiontest.R;
+
+
+public class Demo0 extends Activity {
+
+ private static final int SEARCH_SCREEN = 0;
+ private static final int RESULTS_SCREEN = 1;
+ ViewGroup mSceneRoot;
+ static int mCurrentScene;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mCurrentScene = SEARCH_SCREEN;
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == RESULTS_SCREEN) {
+ mSceneRoot.removeAllViews();
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.search_screen, mSceneRoot);
+ mCurrentScene = SEARCH_SCREEN;
+ } else {
+ mSceneRoot.removeAllViews();
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.results_screen, mSceneRoot);
+ mCurrentScene = RESULTS_SCREEN;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo1.java b/tests/TransitionTests/src/com/android/transitiontests/Demo1.java
new file mode 100644
index 0000000..360a764
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo1.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class Demo1 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ boolean mFirstTime = true;
+ Scene mSearchScreen, mResultsScreen;
+ TransitionManager mTransitionManager = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+// mResultsScreen = new MyScene(mSceneRoot, R.layout.results_screen);
+// mSearchScreen = new MyScene(mSceneRoot, R.layout.search_screen);
+ mResultsScreen = new Scene(mSceneRoot);
+ mResultsScreen.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.results_screen, mSceneRoot);
+ }
+ });
+ mSearchScreen = new Scene(mSceneRoot);
+ mSearchScreen.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.search_screen, mSceneRoot);
+ }
+ });
+
+ }
+
+ public void sendMessage(View view) {
+ if (mFirstTime) {
+ mFirstTime = false;
+ TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Fade().setTargetIds(R.id.resultsText, R.id.resultsList),
+ new Move().setTargetIds(R.id.searchContainer));
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transition);
+ mTransitionManager.setTransition(mResultsScreen, transition);
+ }
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo2.java b/tests/TransitionTests/src/com/android/transitiontests/Demo2.java
new file mode 100644
index 0000000..40f8a084
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo2.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionInflater;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+public class Demo2 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ boolean mFirstTime = true;
+ Scene mSearchScreen, mResultsScreen;
+ TransitionManager mTransitionManager = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ }
+
+ public void sendMessage(View view) {
+ if (mFirstTime) {
+ mFirstTime = false;
+ // Non-resource approach of creating scenes
+// mSearchScreen = new Scene(this, mSceneRoot, R.layout.search_screen);
+// mResultsScreen = new Scene(this, mSceneRoot, R.layout.results_screen);
+ try {
+ mSearchScreen = TransitionInflater.from(this).
+ inflateScene(R.scene.search_scene, mSceneRoot);
+ mResultsScreen = TransitionInflater.from(this).
+ inflateScene(R.scene.results_scene, mSceneRoot);
+ } catch (Exception e) {
+ System.out.println("Problem loading scene resource: " + e);
+ }
+
+ TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Fade().setTargetIds(R.id.resultsText, R.id.resultsList),
+ new Move().setTargetIds(R.id.searchContainer),
+ new Recolor().setTargetIds(R.id.container));
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transition);
+ mTransitionManager.setTransition(mResultsScreen, transition);
+ }
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo3.java b/tests/TransitionTests/src/com/android/transitiontests/Demo3.java
new file mode 100644
index 0000000..f30f502
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo3.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class Demo3 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ Scene mSearchScreen, mResultsScreen;
+ TransitionManager mTransitionManager = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Fade(), new Move(), new Recolor());
+
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transition);
+ mTransitionManager.setTransition(mResultsScreen, transition);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo4.java b/tests/TransitionTests/src/com/android/transitiontests/Demo4.java
new file mode 100644
index 0000000..41d6d08
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo4.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class Demo4 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ Scene mSearchScreen, mResultsScreen;
+ TransitionManager mTransitionManager = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ TransitionGroup transitionToResults = new TransitionGroup();
+ Fade fade = new Fade();
+ fade.setTargetIds(R.id.resultsText, R.id.resultsList);
+ fade.setStartDelay(300);
+ fade.setDuration(1000);
+ transitionToResults.addTransitions(fade, new Move().setTargetIds(R.id.searchContainer),
+ new Recolor().setTargetIds(R.id.container));
+
+ TransitionGroup transitionToSearch = new TransitionGroup();
+ transitionToSearch.addTransitions(fade, new Move().setTargetIds(R.id.searchContainer),
+ new Recolor().setTargetIds(R.id.container));
+
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transitionToSearch);
+ mTransitionManager.setTransition(mResultsScreen, transitionToResults);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo5.java b/tests/TransitionTests/src/com/android/transitiontests/Demo5.java
new file mode 100644
index 0000000..9d64f07
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo5.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class Demo5 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ Scene mSearchScreen, mResultsScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ TransitionManager.go(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ TransitionManager.go(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/FadingTest.java b/tests/TransitionTests/src/com/android/transitiontests/FadingTest.java
new file mode 100644
index 0000000..7d30dfd
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/FadingTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class FadingTest extends Activity {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ static Fade sFade = new Fade();
+
+ static {
+ sFade.setTargetIds(R.id.removingButton, R.id.invisibleButton, R.id.goneButton);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.fading_test);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+
+ mRemovingButton = (Button) findViewById(R.id.removingButton);
+ mInvisibleButton = (Button) findViewById(R.id.invisibleButton);
+ mGoneButton = (Button) findViewById(R.id.goneButton);
+
+ mGoneButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mGoneButton.setAlpha(mGoneButton.getAlpha() < 1 ? 1 : .6f);
+ }
+ });
+
+ mScene1 = new Scene(mSceneRoot, R.layout.fading_test, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.fading_test_scene_2, this);
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2);
+ } else {
+ TransitionManager.go(mScene1);
+ }
+ }
+
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/HitRectBug.java b/tests/TransitionTests/src/com/android/transitiontests/HitRectBug.java
new file mode 100644
index 0000000..33e00a8
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/HitRectBug.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import com.android.transitiontest.R;
+
+
+public class HitRectBug extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(new TestDrawingView(this));
+ }
+
+ public static class TestDrawingView extends RelativeLayout
+ {
+ private Rect mRect = new Rect();
+ private Paint mPaint;
+ private ImageView mImageView;
+
+ public TestDrawingView(Context context)
+ {
+ super(context);
+ setWillNotDraw(false);
+
+ mPaint = new Paint();
+ mPaint.setColor(Color.RED);
+ mPaint.setStyle(Paint.Style.STROKE);
+
+ mImageView = new ImageView(context);
+ mImageView.setLeft(100);
+ mImageView.setRight(200);
+ mImageView.setImageResource(R.drawable.self_portrait_square);
+ mImageView.setScaleX(3);
+ mImageView.setScaleY(3);
+// mImageView.setRotation(145);
+
+ ObjectAnimator anim = ObjectAnimator.ofFloat(mImageView, View.ROTATION, 0, 360);
+ anim.setRepeatCount(ValueAnimator.INFINITE);
+ anim.setDuration(5000);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ invalidate();
+ }
+ });
+ anim.start();
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(128, 128);
+ params.addRule(RelativeLayout.CENTER_IN_PARENT);
+ addView(mImageView, params);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas)
+ {
+ super.onDraw(canvas);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ mImageView.getHitRect(mRect);
+ canvas.drawRect(mRect, mPaint);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/InstanceTargets.java b/tests/TransitionTests/src/com/android/transitiontests/InstanceTargets.java
new file mode 100644
index 0000000..efccfa9
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/InstanceTargets.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.TransitionManager;
+import android.widget.Button;
+import android.widget.RelativeLayout;
+import com.android.transitiontest.R;
+
+import static android.widget.RelativeLayout.ALIGN_PARENT_LEFT;
+import static android.widget.RelativeLayout.ALIGN_PARENT_RIGHT;
+import static android.widget.RelativeLayout.LayoutParams;
+
+public class InstanceTargets extends Activity {
+
+ ViewGroup mSceneRoot;
+ static int mCurrentScene;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.instance_targets);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container;
+ }
+
+ public void sendMessage(final View view) {
+ TransitionManager.go(mSceneRoot, new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < mSceneRoot.getChildCount(); ++i) {
+ Button button = (Button) mSceneRoot.getChildAt(i);
+ LayoutParams params = (LayoutParams) button.getLayoutParams();
+ int rules[] = params.getRules();
+ if (rules[ALIGN_PARENT_RIGHT] != 0) {
+ params.removeRule(ALIGN_PARENT_RIGHT);
+ params.addRule(ALIGN_PARENT_LEFT);
+ } else {
+ params.removeRule(ALIGN_PARENT_LEFT);
+ params.addRule(ALIGN_PARENT_RIGHT);
+ }
+ button.setLayoutParams(params);
+ }
+ }
+ }, new Move().setTargets(view));
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemove.java b/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemove.java
new file mode 100644
index 0000000..f0864dc
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemove.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.transition.Fade;
+import android.view.transition.Scene;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.view.transition.AutoTransition;
+import android.view.transition.Move;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class ListViewAddRemove extends Activity {
+
+ final ArrayList<String> numList = new ArrayList<String>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.list_view_add_remove);
+
+ final LinearLayout container = (LinearLayout) findViewById(R.id.container);
+
+ final ListView listview = (ListView) findViewById(R.id.listview);
+ for (int i = 0; i < 200; ++i) {
+ numList.add(Integer.toString(i));
+ }
+ final StableArrayAdapter adapter = new StableArrayAdapter(this,
+ android.R.layout.simple_list_item_1, numList);
+ listview.setAdapter(adapter);
+
+ final ViewTreeObserver observer = container.getViewTreeObserver();
+ observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ public void onGlobalLayout() {
+ System.out.println("-------------------------------------");
+ System.out.println("onLayoutListener: listview view tops: ");
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView view = (TextView) listview.getChildAt(i);
+ System.out.println(" " + view.getText() + ": " + view.getTop());
+ }
+ }
+ });
+
+ final Scene mySceneChanger = new Scene(listview);
+
+ mySceneChanger.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ numList.remove(mItemToDelete);
+ adapter.notifyDataSetChanged();
+ }
+ });
+ final Transition myTransition = new AutoTransition();
+ final TransitionGroup noFadeIn = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ Fade fadeIn = new Fade(Fade.IN);
+ fadeIn.setDuration(50);
+ noFadeIn.addTransitions(new Fade(Fade.OUT), new Move(), fadeIn);
+
+ myTransition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ System.out.println("---------ListView Tops: Before--------");
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView view = (TextView) listview.getChildAt(i);
+ int position = listview.getPositionForView(view);
+ }
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ System.out.println("---------ListView Tops: After--------");
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView view = (TextView) listview.getChildAt(i);
+ int position = listview.getPositionForView(view);
+ if (view.hasTransientState()) {
+// view.setHasTransientState(false);
+ }
+ }
+ myTransition.removeListener(this);
+ }
+ });
+
+ listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
+ System.out.println("---------ListView Tops: OnClick--------");
+ String item = (String) parent.getItemAtPosition(position);
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView v = (TextView) listview.getChildAt(i);
+ if (!item.equals(v.getText())) {
+// v.setHasTransientState(true);
+ }
+ }
+// listview.setHasTransientState(true);
+ mItemToDelete = item;
+// numList.remove(item);
+ TransitionManager.go(mySceneChanger, noFadeIn);
+// view.postDelayed(new Runnable() {
+// @Override
+// public void run() {
+// for (int i = 0; i < listview.getChildCount(); ++i) {
+// TextView v = (TextView) listview.getChildAt(i);
+// v.setHasTransientState(false);
+// }
+// }
+// }, 200);
+ }
+
+ });
+ }
+
+ String mItemToDelete = null;
+
+ private class StableArrayAdapter extends ArrayAdapter<String> {
+
+ HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();
+
+ public StableArrayAdapter(Context context, int textViewResourceId,
+ List<String> objects) {
+ super(context, textViewResourceId, objects);
+ for (int i = 0; i < objects.size(); ++i) {
+ mIdMap.put(objects.get(i), i);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ String item = getItem(position);
+ return mIdMap.get(item);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemoveNoTransition.java b/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemoveNoTransition.java
new file mode 100644
index 0000000..93d3d97
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemoveNoTransition.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+import com.android.transitiontest.R;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class ListViewAddRemoveNoTransition extends Activity {
+
+ final ArrayList<String> numList = new ArrayList<String>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.list_view_add_remove);
+
+ final LinearLayout container = (LinearLayout) findViewById(R.id.container);
+
+ final ListView listview = (ListView) findViewById(R.id.listview);
+ for (int i = 0; i < 200; ++i) {
+ numList.add(Integer.toString(i));
+ }
+ final StableArrayAdapter adapter = new StableArrayAdapter(this,
+ android.R.layout.simple_list_item_1, numList);
+ listview.setAdapter(adapter);
+
+ listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
+ String item = (String) parent.getItemAtPosition(position);
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView v = (TextView) listview.getChildAt(i);
+ if (!item.equals(v.getText())) {
+ v.setHasTransientState(true);
+ }
+ }
+ numList.remove(item);
+ adapter.notifyDataSetChanged();
+ view.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView v = (TextView) listview.getChildAt(i);
+ v.setHasTransientState(false);
+ }
+ }
+ }, 200);
+ }
+
+ });
+ }
+
+ private class StableArrayAdapter extends ArrayAdapter<String> {
+
+ HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();
+
+ public StableArrayAdapter(Context context, int textViewResourceId,
+ List<String> objects) {
+ super(context, textViewResourceId, objects);
+ for (int i = 0; i < objects.size(); ++i) {
+ mIdMap.put(objects.get(i), i);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ String item = getItem(position);
+ return mIdMap.get(item);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/LoginActivity.java b/tests/TransitionTests/src/com/android/transitiontests/LoginActivity.java
new file mode 100644
index 0000000..d3c5174
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/LoginActivity.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.widget.TextView;
+import android.view.transition.Fade;
+import android.view.transition.Recolor;
+import android.view.transition.Slide;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class LoginActivity extends Activity {
+ ViewGroup mSceneRoot;
+ Scene mCurrentScene;
+ TransitionManager mTransitionManager;
+ Scene mLoginScene, mPasswordScene, mIncorrectPasswordScene, mSuccessScene, mUsernameTakenScene,
+ mNewUserScene;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_login);
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mLoginScene = new Scene(mSceneRoot, R.layout.activity_login, this);
+ mPasswordScene = new Scene(mSceneRoot, R.layout.login_password, this);
+ mIncorrectPasswordScene = new Scene(mSceneRoot, R.layout.incorrect_password, this);
+ mUsernameTakenScene = new Scene(mSceneRoot, R.layout.username_taken, this);
+ mSuccessScene = new Scene(mSceneRoot, R.layout.success, this);
+ mNewUserScene = new Scene(mSceneRoot, R.layout.new_user, this);
+
+ mTransitionManager = new TransitionManager();
+
+ // Custom transitions in/out of NewUser screen - slide in the 2nd password UI
+ TransitionGroup slider = new TransitionGroup();
+ slider.addTransitions(new Slide().setTargetIds(R.id.retype, R.id.retypeEdit));
+ slider.addTransitions(new Recolor().setTargetIds(R.id.password, R.id.passwordEdit));
+ slider.addTransitions(new Fade());
+ mTransitionManager.setTransition(mLoginScene, mNewUserScene, slider);
+ mTransitionManager.setTransition(mPasswordScene, mNewUserScene, slider);
+ mTransitionManager.setTransition(mNewUserScene, mLoginScene, slider);
+ mTransitionManager.setTransition(mNewUserScene, mPasswordScene, slider);
+
+ // Custom transitions with recoloring password field
+ Transition colorizer = new Recolor().setTargetIds(R.id.password, R.id.passwordEdit);
+ mTransitionManager.setTransition(mLoginScene, mPasswordScene, colorizer);
+ mTransitionManager.setTransition(mPasswordScene, mLoginScene, colorizer);
+
+ mCurrentScene = mLoginScene;
+ mSceneRoot.setCurrentScene(mLoginScene);
+ }
+
+ public void applyScene(Scene scene) {
+ mTransitionManager.transitionTo(scene);
+ mCurrentScene = scene;
+ }
+
+ public void sendMessage(View view) {
+ TextView textView = (TextView) view;
+ CharSequence text = textView.getText();
+ if (text.equals("Cancel")) {
+ applyScene(mLoginScene);
+ } else if (text.equals("Submit")) {
+ if (mCurrentScene == mLoginScene) {
+ applyScene(mPasswordScene);
+ } else if (mCurrentScene == mPasswordScene) {
+ applyScene(Math.random() < .5 ? mSuccessScene : mIncorrectPasswordScene);
+ } else if (mCurrentScene == mNewUserScene) {
+ applyScene(Math.random() < .5 ? mSuccessScene : mUsernameTakenScene);
+ }
+ } else if (text.equals("New User?")) {
+ applyScene(mNewUserScene);
+ } else if (text.equals("Okay")) {
+ if (mCurrentScene == mIncorrectPasswordScene) {
+ applyScene(mPasswordScene);
+ } else { // username taken scene
+ applyScene(mNewUserScene);
+ }
+ } else if (text.equals("Reset")) {
+ applyScene(mLoginScene);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/LoginActivityFromResources.java b/tests/TransitionTests/src/com/android/transitiontests/LoginActivityFromResources.java
new file mode 100644
index 0000000..fc8aa9b
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/LoginActivityFromResources.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.view.transition.TransitionInflater;
+import android.widget.TextView;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class LoginActivityFromResources extends Activity {
+ ViewGroup mSceneRoot;
+ Scene mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ Scene mLoginScene, mPasswordScene, mIncorrectPasswordScene, mSuccessScene, mUsernameTakenScene,
+ mNewUserScene;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_login);
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ }
+
+ public void applyScene(Scene scene) {
+ mTransitionManager.transitionTo(scene);
+ mCurrentScene = scene;
+ }
+
+ public void sendMessage(View view) {
+ if (mTransitionManager == null) {
+ TransitionInflater inflater = TransitionInflater.from(this);
+
+ mLoginScene = inflater.inflateScene(R.scene.login_scene, mSceneRoot);
+ mPasswordScene = inflater.inflateScene(R.scene.password_scene, mSceneRoot);
+ mIncorrectPasswordScene =
+ inflater.inflateScene(R.scene.incorrect_password_scene,mSceneRoot);
+ mUsernameTakenScene =
+ inflater.inflateScene(R.scene.username_taken_scene, mSceneRoot);
+ mSuccessScene = inflater.inflateScene(R.scene.success_scene, mSceneRoot);
+ mNewUserScene = inflater.inflateScene(R.scene.new_user_scene, mSceneRoot);
+
+ mTransitionManager =
+ inflater.inflateTransitionManager(R.transition.login_transition_mgr,
+ mSceneRoot);
+
+ mCurrentScene = mLoginScene;
+ mSceneRoot.setCurrentScene(mLoginScene);
+ }
+ TextView textView = (TextView) view;
+ CharSequence text = textView.getText();
+ if (text.equals("Cancel")) {
+ applyScene(mLoginScene);
+ } else if (text.equals("Submit")) {
+ if (mCurrentScene == mLoginScene) {
+ applyScene(mPasswordScene);
+ } else if (mCurrentScene == mPasswordScene) {
+ applyScene(Math.random() < .5 ? mSuccessScene : mIncorrectPasswordScene);
+ } else if (mCurrentScene == mNewUserScene) {
+ applyScene(Math.random() < .5 ? mSuccessScene : mUsernameTakenScene);
+ }
+ } else if (text.equals("New User?")) {
+ applyScene(mNewUserScene);
+ } else if (text.equals("Okay")) {
+ if (mCurrentScene == mIncorrectPasswordScene) {
+ applyScene(mPasswordScene);
+ } else { // username taken scene
+ applyScene(mNewUserScene);
+ }
+ } else if (text.equals("Reset")) {
+ applyScene(mLoginScene);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/OverlayTest.java b/tests/TransitionTests/src/com/android/transitiontests/OverlayTest.java
new file mode 100644
index 0000000..2f8224a
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/OverlayTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import com.android.transitiontest.R;
+
+
+public class OverlayTest extends Activity {
+
+ ViewGroup mContainer;
+ ViewGroup mRoot;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.overlay_test);
+
+ mContainer = (ViewGroup) findViewById(R.id.container);
+ mRoot = (ViewGroup) mContainer.getParent();
+ }
+
+ public void onClick(View view) {
+ final Button fadingButton = (Button) findViewById(R.id.fadingButton);
+ if (fadingButton != null) {
+ mContainer.removeView(fadingButton);
+ mRoot.getOverlay().add(fadingButton);
+ fadingButton.animate().alpha(0).setDuration(1000).withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ fadingButton.setAlpha(1);
+ mRoot.getOverlay().remove(fadingButton);
+ mContainer.addView(fadingButton);
+ }
+ });
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Reparenting.java b/tests/TransitionTests/src/com/android/transitiontests/Reparenting.java
new file mode 100644
index 0000000..8ee9d3f
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Reparenting.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.TransitionManager;
+import android.widget.Button;
+import com.android.transitiontest.R;
+
+public class Reparenting extends Activity {
+
+ ViewGroup mSceneRoot;
+ ViewGroup mContainer1, mContainer2;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.reparenting);
+
+ ViewGroup container = (ViewGroup) findViewById(R.id.container);
+ mContainer1 = (ViewGroup) findViewById(R.id.container1);
+ mContainer2 = (ViewGroup) findViewById(R.id.container2);
+ System.out.println("container 1 and 2 " + mContainer1 + ", " + mContainer2);
+
+ setupButtons(0, mContainer1);
+ setupButtons(3, mContainer2);
+
+ mSceneRoot = container;
+ }
+
+ private void setupButtons(int startIndex, ViewGroup parent) {
+ for (int i = startIndex; i < (startIndex + 3); ++i) {
+ Button button = new Button(this);
+ button.setText(Integer.toString(i));
+ button.setOnClickListener(mButtonListener);
+ parent.addView(button);
+ }
+ }
+
+ private View.OnClickListener mButtonListener = new View.OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ Scene newScene = new Scene(mSceneRoot);
+ newScene.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ ViewGroup oldParent = (ViewGroup) v.getParent();
+ ViewGroup newParent = oldParent == mContainer1 ? mContainer2 : mContainer1;
+ oldParent.removeView(v);
+ newParent.addView(v);
+ }
+ });
+ Move reparent = new Move();
+ reparent.setReparent(true);
+ TransitionManager.go(newScene, reparent);
+ }
+ };
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ResourceLoadingTest.java b/tests/TransitionTests/src/com/android/transitiontests/ResourceLoadingTest.java
new file mode 100644
index 0000000..c05f898
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ResourceLoadingTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.view.transition.TransitionInflater;
+import android.view.transition.Transition;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class ResourceLoadingTest extends Activity {
+
+ private static final int SEARCH_SCREEN = 0;
+ private static final int RESULTS_SCREEN = 1;
+ ViewGroup mSceneRoot;
+ static int mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ TransitionInflater mInflater;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mCurrentScene = SEARCH_SCREEN;
+
+ mInflater = TransitionInflater.from(this);
+ }
+
+ public void sendMessage(View view) {
+ if (mTransitionManager == null) {
+ try {
+ TransitionInflater inflater = TransitionInflater.from(this);
+ mTransitionManager =
+ inflater.inflateTransitionManager(R.transition.my_transition_mgr,
+ mSceneRoot);
+ Scene loadedScene = inflater.inflateScene(R.scene.my_scene, mSceneRoot);
+ System.out.println("loadedScene = " + loadedScene);
+ Transition loadedTransition = inflater.inflateTransition(R.transition.my_transition);
+ System.out.println("loadedTransition = " + loadedTransition);
+ } catch (Exception e) {
+ System.out.println("Problem loading scene resource: " + e);
+ }
+ }
+ if (mCurrentScene == RESULTS_SCREEN) {
+ Scene scene = mInflater.inflateScene(R.scene.search_scene, mSceneRoot);
+ mTransitionManager.transitionTo(scene);
+ mCurrentScene = SEARCH_SCREEN;
+ } else {
+ Scene scene = mInflater.inflateScene(R.scene.results_scene, mSceneRoot);
+ mTransitionManager.transitionTo(scene);
+ mCurrentScene = RESULTS_SCREEN;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTargets.java b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTargets.java
new file mode 100644
index 0000000..ae8ee5b
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTargets.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class ScenesTestAutoTargets extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ Scene mResultsScreen, mSearchScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Fade(), new Move(), new Recolor());
+
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transition);
+ mTransitionManager.setTransition(mResultsScreen, transition);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition.java b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition.java
new file mode 100644
index 0000000..9184e87
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.AutoTransition;
+import android.view.transition.Scene;
+import android.view.transition.Transition;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class ScenesTestAutoTransition extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ Transition mTransition = new AutoTransition();
+ Scene mResultsScreen, mSearchScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ TransitionManager.go(mSearchScreen, mTransition);
+ mCurrentScene = mSearchScreen;
+ } else {
+ TransitionManager.go(mResultsScreen, mTransition);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition2.java b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition2.java
new file mode 100644
index 0000000..fc8acc1
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition2.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class ScenesTestAutoTransition2 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+
+ Scene mResultsScreen, mSearchScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ TransitionManager.go(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ TransitionManager.go(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ScenesTestv21.java b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestv21.java
new file mode 100644
index 0000000..2dae463
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestv21.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class ScenesTestv21 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ Scene mResultsScreen, mSearchScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ TransitionGroup transitionToResults = new TransitionGroup();
+ Fade fade = new Fade();
+ fade.setTargetIds(R.id.resultsText, R.id.resultsList);
+ fade.setStartDelay(300);
+ transitionToResults.addTransitions(fade);
+ transitionToResults.addTransitions(new Move().setTargetIds(R.id.searchContainer));
+ transitionToResults.addTransitions(new Recolor().setTargetIds(R.id.container));
+
+ TransitionGroup transitionToSearch = new TransitionGroup();
+ transitionToSearch.addTransitions(new Fade().setTargetIds(R.id.resultsText, R.id.resultsList));
+ transitionToSearch.addTransitions(new Move().setTargetIds(R.id.searchContainer));
+ transitionToSearch.addTransitions(new Recolor().setTargetIds(R.id.container));
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transitionToSearch);
+ mTransitionManager.setTransition(mResultsScreen, transitionToResults);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/SequenceTest.java b/tests/TransitionTests/src/com/android/transitiontests/SequenceTest.java
new file mode 100644
index 0000000..8cb6a1a
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/SequenceTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.view.transition.Transition;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class SequenceTest extends Activity {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ TransitionGroup sequencedFade, reverseSequencedFade;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.fading_test);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mRemovingButton = (Button) findViewById(R.id.removingButton);
+ mInvisibleButton = (Button) findViewById(R.id.invisibleButton);
+ mGoneButton = (Button) findViewById(R.id.goneButton);
+
+ mScene1 = new Scene(mSceneRoot, R.layout.fading_test, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.fading_test_scene_2, this);
+
+ Transition fade1 = new Fade().setTargetIds(R.id.removingButton);
+ Transition fade2 = new Fade().setTargetIds(R.id.invisibleButton);
+ Transition fade3 = new Fade().setTargetIds(R.id.goneButton);
+ TransitionGroup fader = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ fader.addTransitions(fade1, fade2, fade3, new Move());
+ sequencedFade = fader;
+
+ reverseSequencedFade = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ reverseSequencedFade.addTransitions(new Move(), fade3, fade2, fade1);
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2, sequencedFade);
+ } else {
+ TransitionManager.go(mScene1, reverseSequencedFade);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/SequenceTestSimple.java b/tests/TransitionTests/src/com/android/transitiontests/SequenceTestSimple.java
new file mode 100644
index 0000000..93bfdb4
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/SequenceTestSimple.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class SequenceTestSimple extends Activity {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ Transition sequencedFade;
+ TransitionGroup sequencedFadeReverse;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.fading_test_simple);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mRemovingButton = (Button) findViewById(R.id.removingButton);
+
+ mScene1 = new Scene(mSceneRoot, R.layout.fading_test_simple, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.fading_test_simple2, this);
+
+ TransitionGroup fader = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ fader.addTransitions(new Fade().setTargetIds(R.id.removingButton));
+ fader.addTransitions(new Move().setTargetIds(R.id.sceneSwitchButton));
+ sequencedFade = fader;
+
+ sequencedFadeReverse = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ sequencedFadeReverse.addTransitions(new Move().setTargetIds(R.id.sceneSwitchButton));
+ sequencedFadeReverse.addTransitions(new Fade().setTargetIds(R.id.removingButton));
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2, sequencedFade);
+ } else {
+ TransitionManager.go(mScene1, sequencedFadeReverse);
+ }
+ }}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/SurfaceAndTextureViews.java b/tests/TransitionTests/src/com/android/transitiontests/SurfaceAndTextureViews.java
new file mode 100644
index 0000000..f8829c9
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/SurfaceAndTextureViews.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Crossfade;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import android.widget.Button;
+import com.android.transitiontest.R;
+
+import static android.widget.LinearLayout.LayoutParams;
+
+public class SurfaceAndTextureViews extends Activity {
+
+ SimpleView mView;
+ SimpleSurfaceView mSurfaceView;
+ SimpleTextureView mTextureView;
+ private static final int SMALL_SIZE = 200;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.surface_texture_views);
+
+ final ViewGroup container = (ViewGroup) findViewById(R.id.container);
+ Button toggleButton = (Button) findViewById(R.id.toggleButton);
+
+ mView = new SimpleView(this);
+ mView.setId(0);
+ mView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ container.addView(mView);
+
+ mSurfaceView = new SimpleSurfaceView(this);
+ mSurfaceView.setId(1);
+ mSurfaceView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ container.addView(mSurfaceView);
+
+ mTextureView = new SimpleTextureView(this);
+ mTextureView.setId(2);
+ mTextureView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ container.addView(mTextureView);
+
+ final TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Move(), new Crossfade().setTargetIds(0, 1, 2));
+
+ toggleButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Scene newScene = new Scene(container);
+ newScene.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ if (mView.getWidth() <= SMALL_SIZE) {
+ mView.setLayoutParams(new LayoutParams(SMALL_SIZE * 2, SMALL_SIZE));
+ mSurfaceView.setLayoutParams(new LayoutParams(SMALL_SIZE * 2, SMALL_SIZE));
+ mTextureView.setLayoutParams(new LayoutParams(SMALL_SIZE * 2, SMALL_SIZE));
+ mView.mColor = SimpleView.LARGE_COLOR;
+ mSurfaceView.mColor = SimpleSurfaceView.LARGE_COLOR;
+ mTextureView.mColor = SimpleTextureView.LARGE_COLOR;
+ } else {
+ mView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ mSurfaceView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ mTextureView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ mView.mColor = SimpleView.SMALL_COLOR;
+ mSurfaceView.mColor = SimpleSurfaceView.SMALL_COLOR;
+ mTextureView.mColor = SimpleTextureView.SMALL_COLOR;
+ }
+ }
+ });
+ TransitionManager.go(newScene, transition);
+ }
+ });
+
+ }
+
+ static private class SimpleView extends View {
+ static final int SMALL_COLOR = Color.BLUE;
+ static final int LARGE_COLOR = Color.YELLOW;
+ int mColor = SMALL_COLOR;
+
+ private SimpleView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.drawColor(mColor);
+ }
+ }
+
+ static private class SimpleSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+
+ static final int SMALL_COLOR = Color.GREEN;
+ static final int LARGE_COLOR = Color.GRAY;
+ int mColor = SMALL_COLOR;
+ SurfaceHolder mHolder = null;
+
+ private SimpleSurfaceView(Context context) {
+ super(context);
+ SurfaceHolder holder = getHolder();
+ holder.addCallback(this);
+ }
+
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ System.out.println("surfaceCreated");
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ System.out.println("surfaceChanged: w h = " + width + ", " + height);
+ Canvas canvas = holder.lockCanvas();
+ canvas.drawColor(mColor);
+ holder.unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ System.out.println("surfaceDestroyed");
+ }
+ }
+
+ static private class SimpleTextureView extends TextureView implements TextureView.SurfaceTextureListener {
+
+ static final int SMALL_COLOR = Color.RED;
+ static final int LARGE_COLOR = Color.CYAN;
+ int mColor = SMALL_COLOR;
+
+ private SimpleTextureView(Context context) {
+ super(context);
+ setSurfaceTextureListener(this);
+ }
+
+ private SimpleTextureView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setSurfaceTextureListener(this);
+ }
+
+ private SimpleTextureView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setSurfaceTextureListener(this);
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ System.out.println("SurfaceTexture available");
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ System.out.println("SurfaceTexture size changed to " + width + ", " + height);
+ Canvas canvas = lockCanvas();
+ canvas.drawColor(mColor);
+ unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ return false;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ System.out.println("SurfaceTexture updated");
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/UniqueIds.java b/tests/TransitionTests/src/com/android/transitiontests/UniqueIds.java
new file mode 100644
index 0000000..18537c7
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/UniqueIds.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.view.transition.Transition;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+import java.util.HashMap;
+
+public class UniqueIds extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ HashMap<Button, ToggleScene> mSceneMap = new HashMap<Button, ToggleScene>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.unique_id_test);
+
+ LinearLayout container = (LinearLayout) findViewById(R.id.container);
+ LayoutInflater inflater = getLayoutInflater();
+ Button button = (Button) inflater.inflate(R.layout.button_template, null);
+ container.addView(button);
+ ToggleScene scene = new ToggleScene(container, button);
+ mSceneMap.put(button, scene);
+ button = (Button) inflater.inflate(R.layout.button_template, null);
+ container.addView(button);
+ scene = new ToggleScene(container, button);
+ mSceneMap.put(button, scene);
+ }
+
+ public void sendMessage(View view) {
+ mSceneMap.get(view).changeToScene();
+ }
+
+ class ToggleScene {
+ Scene mScene;
+ Transition mTransition;
+ Button mButton;
+
+ ToggleScene(ViewGroup rootView, Button button) {
+ mScene = new Scene(rootView);
+ mButton = button;
+ mScene.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ if (mButton.getLeft() == 0) {
+ mButton.offsetLeftAndRight(500);
+ } else {
+ int width = mButton.getWidth();
+ mButton.setLeft(0);
+ mButton.setRight(width);
+ }
+ }
+ });
+ }
+
+ void changeToScene() {
+ TransitionManager.go(mScene);
+ }
+ }
+}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 77168f9..fe5c810 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -172,6 +172,7 @@
bool isValidResourceType(const String8& type)
{
return type == "anim" || type == "animator" || type == "interpolator"
+ || type == "transition" || type == "scene"
|| type == "drawable" || type == "layout"
|| type == "values" || type == "xml" || type == "raw"
|| type == "color" || type == "menu" || type == "mipmap";
@@ -932,6 +933,8 @@
sp<ResourceTypeSet> anims;
sp<ResourceTypeSet> animators;
sp<ResourceTypeSet> interpolators;
+ sp<ResourceTypeSet> transitions;
+ sp<ResourceTypeSet> scenes;
sp<ResourceTypeSet> xmls;
sp<ResourceTypeSet> raws;
sp<ResourceTypeSet> colors;
@@ -943,6 +946,8 @@
ASSIGN_IT(anim);
ASSIGN_IT(animator);
ASSIGN_IT(interpolator);
+ ASSIGN_IT(transition);
+ ASSIGN_IT(scene);
ASSIGN_IT(xml);
ASSIGN_IT(raw);
ASSIGN_IT(color);
@@ -965,6 +970,8 @@
!applyFileOverlay(bundle, assets, &anims, "anim") ||
!applyFileOverlay(bundle, assets, &animators, "animator") ||
!applyFileOverlay(bundle, assets, &interpolators, "interpolator") ||
+ !applyFileOverlay(bundle, assets, &transitions, "transition") ||
+ !applyFileOverlay(bundle, assets, &scenes, "scene") ||
!applyFileOverlay(bundle, assets, &xmls, "xml") ||
!applyFileOverlay(bundle, assets, &raws, "raw") ||
!applyFileOverlay(bundle, assets, &colors, "color") ||
@@ -1024,6 +1031,20 @@
}
}
+ if (transitions != NULL) {
+ err = makeFileResources(bundle, assets, &table, transitions, "transition");
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (scenes != NULL) {
+ err = makeFileResources(bundle, assets, &table, scenes, "scene");
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
if (interpolators != NULL) {
err = makeFileResources(bundle, assets, &table, interpolators, "interpolator");
if (err != NO_ERROR) {
@@ -1168,6 +1189,36 @@
err = NO_ERROR;
}
+ if (transitions != NULL) {
+ ResourceDirIterator it(transitions, String8("transition"));
+ while ((err=it.next()) == NO_ERROR) {
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (err < NO_ERROR) {
+ hasErrors = true;
+ }
+ err = NO_ERROR;
+ }
+
+ if (scenes != NULL) {
+ ResourceDirIterator it(scenes, String8("scene"));
+ while ((err=it.next()) == NO_ERROR) {
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (err < NO_ERROR) {
+ hasErrors = true;
+ }
+ err = NO_ERROR;
+ }
+
if (xmls != NULL) {
ResourceDirIterator it(xmls, String8("xml"));
while ((err=it.next()) == NO_ERROR) {