summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/14.txt2
-rw-r--r--api/current.txt5
-rw-r--r--core/java/android/hardware/Sensor.java7
-rw-r--r--core/java/android/hardware/SensorEvent.java58
-rw-r--r--core/java/android/hardware/usb/UsbDeviceConnection.java12
-rw-r--r--core/java/android/net/LinkProperties.java3
-rw-r--r--core/java/android/net/MobileDataStateTracker.java15
-rw-r--r--core/java/android/net/NetworkUtils.java110
-rw-r--r--core/java/android/net/RouteInfo.java59
-rw-r--r--core/java/android/os/INetworkManagementService.aidl17
-rw-r--r--core/java/android/view/View.java2
-rw-r--r--core/java/android/view/accessibility/AccessibilityRecord.java2
-rw-r--r--core/jni/android_hardware_UsbDeviceConnection.cpp22
-rw-r--r--core/jni/android_net_NetUtils.cpp60
-rw-r--r--core/res/res/values/dimens.xml4
-rw-r--r--data/fonts/MTLc3m.ttfbin1922744 -> 1924864 bytes
-rw-r--r--data/fonts/MTLmr3m.ttfbin2868496 -> 2871020 bytes
-rw-r--r--docs/html/guide/appendix/install-location.jd5
-rw-r--r--docs/html/guide/publishing/licensing.jd6
-rw-r--r--docs/html/guide/topics/manifest/manifest-element.jd9
-rw-r--r--media/java/android/media/MediaFile.java1
-rw-r--r--media/java/android/media/MediaRecorder.java9
-rw-r--r--media/libstagefright/AwesomePlayer.cpp148
-rw-r--r--media/libstagefright/WVMExtractor.cpp19
-rw-r--r--media/libstagefright/include/AwesomePlayer.h5
-rw-r--r--media/libstagefright/include/WVMExtractor.h20
-rw-r--r--packages/SystemUI/res/drawable-hdpi/recents_bg_protect_tile.pngbin0 -> 7927 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.pngbin0 -> 9784 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/recents_callout_line.pngbin0 -> 114 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.pngbin0 -> 3981 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/recents_bg_protect_tile.pngbin0 -> 7927 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.pngbin0 -> 9784 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/recents_callout_line.pngbin0 -> 114 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.pngbin0 -> 3981 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.pngbin0 -> 2056 bytes
-rw-r--r--packages/SystemUI/res/layout-land/status_bar_recent_item.xml79
-rw-r--r--packages/SystemUI/res/layout-land/status_bar_recent_panel.xml84
-rw-r--r--packages/SystemUI/res/layout-large/status_bar_recent_item.xml8
-rw-r--r--packages/SystemUI/res/layout-large/status_bar_recent_panel.xml45
-rw-r--r--packages/SystemUI/res/layout/status_bar_recent_item.xml90
-rw-r--r--packages/SystemUI/res/layout/status_bar_recent_panel.xml84
-rw-r--r--packages/SystemUI/res/values-land/dimens.xml16
-rw-r--r--packages/SystemUI/res/values-large/dimens.xml26
-rw-r--r--packages/SystemUI/res/values/dimens.xml14
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/Choreographer.java131
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java307
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsListView.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java)294
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java309
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/carousel/RecentApplicationsActivity.java (renamed from packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/carousel/RecentApplicationsCarouselView.java (renamed from packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsCarouselView.java)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java11
-rw-r--r--services/java/com/android/server/ConnectivityService.java87
-rw-r--r--services/java/com/android/server/NetworkManagementService.java166
-rw-r--r--services/java/com/android/server/connectivity/Tethering.java31
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java18
-rw-r--r--telephony/java/com/android/internal/telephony/ApnContext.java2
-rw-r--r--telephony/java/com/android/internal/telephony/DataConnection.java72
-rw-r--r--telephony/java/com/android/internal/telephony/DataConnectionAc.java32
-rw-r--r--telephony/java/com/android/internal/telephony/Phone.java1
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java131
-rwxr-xr-xtelephony/java/com/android/internal/telephony/gsm/SIMRecords.java5
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java12
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java8
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java34
66 files changed, 2249 insertions, 574 deletions
diff --git a/api/14.txt b/api/14.txt
index ebb4308fb3ab..b080d25e4b4a 100644
--- a/api/14.txt
+++ b/api/14.txt
@@ -22002,7 +22002,7 @@ package android.view.accessibility {
method public boolean isEnabled();
method public boolean isFullScreen();
method public boolean isPassword();
- method protected static android.view.accessibility.AccessibilityRecord obtain();
+ method public static android.view.accessibility.AccessibilityRecord obtain();
method public void recycle();
method public void setAddedCount(int);
method public void setBeforeText(java.lang.CharSequence);
diff --git a/api/current.txt b/api/current.txt
index b88ef3a4626f..cacd87b2ba2c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8839,6 +8839,7 @@ package android.hardware {
field public static final deprecated int TYPE_ORIENTATION = 3; // 0x3
field public static final int TYPE_PRESSURE = 6; // 0x6
field public static final int TYPE_PROXIMITY = 8; // 0x8
+ field public static final int TYPE_RELATIVE_HUMIDITY = 12; // 0xc
field public static final int TYPE_ROTATION_VECTOR = 11; // 0xb
field public static final deprecated int TYPE_TEMPERATURE = 7; // 0x7
}
@@ -9015,6 +9016,7 @@ package android.hardware.usb {
method public void close();
method public int controlTransfer(int, int, int, int, byte[], int, int);
method public int getFileDescriptor();
+ method public byte[] getRawDescriptors();
method public java.lang.String getSerial();
method public boolean releaseInterface(android.hardware.usb.UsbInterface);
method public android.hardware.usb.UsbRequest requestWait();
@@ -10113,6 +10115,7 @@ package android.media {
method public void setAuxiliaryOutputFile(java.lang.String);
method public void setCamera(android.hardware.Camera);
method public void setCaptureRate(double);
+ method public void setLocation(float, float);
method public void setMaxDuration(int) throws java.lang.IllegalArgumentException;
method public void setMaxFileSize(long) throws java.lang.IllegalArgumentException;
method public void setOnErrorListener(android.media.MediaRecorder.OnErrorListener);
@@ -22241,7 +22244,7 @@ package android.view.accessibility {
method public boolean isEnabled();
method public boolean isFullScreen();
method public boolean isPassword();
- method protected static android.view.accessibility.AccessibilityRecord obtain();
+ method public static android.view.accessibility.AccessibilityRecord obtain();
method public void recycle();
method public void setAddedCount(int);
method public void setBeforeText(java.lang.CharSequence);
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index a4ba3bde0184..68fc1015449f 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -104,6 +104,13 @@ public class Sensor {
*/
public static final int TYPE_ROTATION_VECTOR = 11;
+ /**
+ * A constant describing a relative humidity sensor type.
+ * See {@link android.hardware.SensorEvent SensorEvent}
+ * for more details.
+ */
+ public static final int TYPE_RELATIVE_HUMIDITY = 12;
+
/** A constant describing an ambient temperature sensor type */
public static final int TYPE_AMBIENT_TEMPERATURE = 13;
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index b111b84ca337..0411b5c02f1b 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -327,6 +327,64 @@ public class SensorEvent {
* in the clockwise direction (mathematically speaking, it should be
* positive in the counter-clockwise direction).
* </p>
+ *
+ * <h4>{@link android.hardware.Sensor#TYPE_RELATIVE_HUMIDITY
+ * Sensor.TYPE_RELATIVE_HUMIDITY}:</h4>
+ * <ul>
+ * <p>
+ * values[0]: Relative ambient air humidity in percent
+ * </p>
+ * </ul>
+ * <p>
+ * When relative ambient air humidity and ambient temperature are
+ * measured, the dew point and absolute humidity can be calculated.
+ * </p>
+ * <u>Dew Point</u>
+ * <p>
+ * The dew point is the temperature to which a given parcel of air must be
+ * cooled, at constant barometric pressure, for water vapor to condense
+ * into water.
+ * </p>
+ * <center><pre>
+ * ln(RH/100%) + m&#183;t/(T<sub>n</sub>+t)
+ * t<sub>d</sub>(t,RH) = T<sub>n</sub> &#183; ------------------------------
+ * m - [ln(RH/100%) + m&#183;t/(T<sub>n</sub>+t)]
+ * </pre></center>
+ * <dl>
+ * <dt>t<sub>d</sub></dt> <dd>dew point temperature in &deg;C</dd>
+ * <dt>t</dt> <dd>actual temperature in &deg;C</dd>
+ * <dt>RH</dt> <dd>actual relative humidity in %</dd>
+ * <dt>m</dt> <dd>17.62</dd>
+ * <dt>T<sub>n</sub></dt> <dd>243.12 &deg;C</dd>
+ * </dl>
+ * <p>for example:</p>
+ * <pre class="prettyprint">
+ * h = Math.log(rh / 100.0) + (17.62 * t) / (243.12 + t);
+ * td = 243.12 * h / (17.62 - h);
+ * </pre>
+ * <u>Absolute Humidity</u>
+ * <p>
+ * The absolute humidity is the mass of water vapor in a particular volume
+ * of dry air. The unit is g/m<sup>3</sup>.
+ * </p>
+ * <center><pre>
+ * RH/100%&#183;A&#183;exp(m&#183;t/(T<sub>n</sub>+t))
+ * d<sub>v</sub>(t,RH) = 216.7 &#183; -------------------------
+ * 273.15 + t
+ * </pre></center>
+ * <dl>
+ * <dt>d<sub>v</sub></dt> <dd>absolute humidity in g/m<sup>3</sup></dd>
+ * <dt>t</dt> <dd>actual temperature in &deg;C</dd>
+ * <dt>RH</dt> <dd>actual relative humidity in %</dd>
+ * <dt>m</dt> <dd>17.62</dd>
+ * <dt>T<sub>n</sub></dt> <dd>243.12 &deg;C</dd>
+ * <dt>A</dt> <dd>6.112 hPa</dd>
+ * </dl>
+ * <p>for example:</p>
+ * <pre class="prettyprint">
+ * dv = 216.7 *
+ * (rh / 100.0 * 6.112 * Math.exp(17.62 * t / (243.12 + t)) / (273.15 + t));
+ * </pre>
*
* <h4>{@link android.hardware.Sensor#TYPE_AMBIENT_TEMPERATURE Sensor.TYPE_AMBIENT_TEMPERATURE}:
* </h4>
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index a153c0b24fe1..b53649089d10 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -69,6 +69,17 @@ public class UsbDeviceConnection {
}
/**
+ * Returns the raw USB descriptors for the device.
+ * This can be used to access descriptors not supported directly
+ * via the higher level APIs.
+ *
+ * @return raw USB descriptors
+ */
+ public byte[] getRawDescriptors() {
+ return native_get_desc();
+ }
+
+ /**
* Claims exclusive access to a {@link android.hardware.usb.UsbInterface}.
* This must be done before sending or receiving data on any
* {@link android.hardware.usb.UsbEndpoint}s belonging to the interface.
@@ -160,6 +171,7 @@ public class UsbDeviceConnection {
private native boolean native_open(String deviceName, FileDescriptor pfd);
private native void native_close();
private native int native_get_fd();
+ private native byte[] native_get_desc();
private native boolean native_claim_interface(int interfaceID, boolean force);
private native boolean native_release_interface(int interfaceID);
private native int native_control_request(int requestType, int request, int value,
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 61acf2b841dd..19894a039a15 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -68,7 +68,8 @@ public class LinkProperties implements Parcelable {
mLinkAddresses = source.getLinkAddresses();
mDnses = source.getDnses();
mRoutes = source.getRoutes();
- mHttpProxy = new ProxyProperties(source.getHttpProxy());
+ mHttpProxy = (source.getHttpProxy() == null) ?
+ null : new ProxyProperties(source.getHttpProxy());
}
}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index bb6ee0f809cd..770f152f1b76 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -276,6 +276,21 @@ public class MobileDataStateTracker implements NetworkStateTracker {
setDetailedState(DetailedState.CONNECTED, reason, apnName);
break;
}
+ } else {
+ // There was no state change. Check if LinkProperties has been updated.
+ if (TextUtils.equals(reason, Phone.REASON_LINK_PROPERTIES_CHANGED)) {
+ mLinkProperties = intent.getParcelableExtra(Phone.DATA_LINK_PROPERTIES_KEY);
+ if (mLinkProperties == null) {
+ log("No link property in LINK_PROPERTIES change event.");
+ mLinkProperties = new LinkProperties();
+ }
+ // Just update reason field in this NetworkInfo
+ mNetworkInfo.setDetailedState(mNetworkInfo.getDetailedState(), reason,
+ mNetworkInfo.getExtraInfo());
+ Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED,
+ mNetworkInfo);
+ msg.sendToTarget();
+ }
}
} else if (intent.getAction().
equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index fbe537948d27..8a678d6c8b1c 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -38,32 +38,6 @@ public class NetworkUtils {
/** Bring the named network interface down. */
public native static int disableInterface(String interfaceName);
- /**
- * Add a route to the routing table.
- *
- * @param interfaceName the interface to route through.
- * @param dst the network or host to route to. May be IPv4 or IPv6, e.g.
- * "0.0.0.0" or "2001:4860::".
- * @param prefixLength the prefix length of the route.
- * @param gw the gateway to use, e.g., "192.168.251.1". If null,
- * indicates a directly-connected route.
- */
- public native static int addRoute(String interfaceName, String dst,
- int prefixLength, String gw);
-
- /** Return the gateway address for the default route for the named interface. */
- public static InetAddress getDefaultRoute(String interfaceName) {
- int addr = getDefaultRouteNative(interfaceName);
- return intToInetAddress(addr);
- }
- private native static int getDefaultRouteNative(String interfaceName);
-
- /** Remove host routes that uses the named interface. */
- public native static int removeHostRoutes(String interfaceName);
-
- /** Remove the default route for the named interface. */
- public native static int removeDefaultRoute(String interfaceName);
-
/** Reset any sockets that are connected via the named interface. */
public native static int resetConnections(String interfaceName);
@@ -160,6 +134,15 @@ public class NetworkUtils {
}
/**
+ * Convert a IPv4 netmask integer to a prefix length
+ * @param netmask as an integer in network byte order
+ * @return the network prefix length
+ */
+ public static int netmaskIntToPrefixLength(int netmask) {
+ return Integer.bitCount(netmask);
+ }
+
+ /**
* Create an InetAddress from a string where the string must be a standard
* representation of a V4 or V6 address. Avoids doing a DNS lookup on failure
* but it will throw an IllegalArgumentException in that case.
@@ -173,60 +156,6 @@ public class NetworkUtils {
}
/**
- * Add a default route through the specified gateway.
- * @param interfaceName interface on which the route should be added
- * @param gw the IP address of the gateway to which the route is desired,
- * @return {@code true} on success, {@code false} on failure
- */
- public static boolean addDefaultRoute(String interfaceName, InetAddress gw) {
- String dstStr;
- String gwStr = gw.getHostAddress();
-
- if (gw instanceof Inet4Address) {
- dstStr = "0.0.0.0";
- } else if (gw instanceof Inet6Address) {
- dstStr = "::";
- } else {
- Log.w(TAG, "addDefaultRoute failure: address is neither IPv4 nor IPv6" +
- "(" + gwStr + ")");
- return false;
- }
- return addRoute(interfaceName, dstStr, 0, gwStr) == 0;
- }
-
- /**
- * Add a host route.
- * @param interfaceName interface on which the route should be added
- * @param dst the IP address of the host to which the route is desired,
- * this should not be null.
- * @param gw the IP address of the gateway to which the route is desired,
- * if null, indicates a directly-connected route.
- * @return {@code true} on success, {@code false} on failure
- */
- public static boolean addHostRoute(String interfaceName, InetAddress dst,
- InetAddress gw) {
- if (dst == null) {
- Log.w(TAG, "addHostRoute: dst should not be null");
- return false;
- }
-
- int prefixLength;
- String dstStr = dst.getHostAddress();
- String gwStr = (gw != null) ? gw.getHostAddress() : null;
-
- if (dst instanceof Inet4Address) {
- prefixLength = 32;
- } else if (dst instanceof Inet6Address) {
- prefixLength = 128;
- } else {
- Log.w(TAG, "addHostRoute failure: address is neither IPv4 nor IPv6" +
- "(" + dst + ")");
- return false;
- }
- return addRoute(interfaceName, dstStr, prefixLength, gwStr) == 0;
- }
-
- /**
* Get InetAddress masked with prefixLength. Will never return null.
* @param IP address which will be masked with specified prefixLength
* @param prefixLength the prefixLength used to mask the IP
@@ -271,4 +200,25 @@ public class NetworkUtils {
return (((left instanceof Inet4Address) && (right instanceof Inet4Address)) ||
((left instanceof Inet6Address) && (right instanceof Inet6Address)));
}
+
+ /**
+ * Convert a 32 char hex string into a Inet6Address.
+ * throws a runtime exception if the string isn't 32 chars, isn't hex or can't be
+ * made into an Inet6Address
+ * @param addrHexString a 32 character hex string representing an IPv6 addr
+ * @return addr an InetAddress representation for the string
+ */
+ public static InetAddress hexToInet6Address(String addrHexString)
+ throws IllegalArgumentException {
+ try {
+ return numericToInetAddress(String.format("%s:%s:%s:%s:%s:%s:%s:%s",
+ addrHexString.substring(0,4), addrHexString.substring(4,8),
+ addrHexString.substring(8,12), addrHexString.substring(12,16),
+ addrHexString.substring(16,20), addrHexString.substring(20,24),
+ addrHexString.substring(24,28), addrHexString.substring(28,32)));
+ } catch (Exception e) {
+ Log.e("NetworkUtils", "error in hexToInet6Address(" + addrHexString + "): " + e);
+ throw new IllegalArgumentException(e);
+ }
+ }
}
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 39e708a8d96d..8e5ddda4a6bc 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -46,18 +46,16 @@ public class RouteInfo implements Parcelable {
public RouteInfo(LinkAddress destination, InetAddress gateway) {
if (destination == null) {
- try {
- if (gateway != null) {
- if (gateway instanceof Inet4Address) {
- destination = new LinkAddress(Inet4Address.ANY, 0);
- } else {
- destination = new LinkAddress(Inet6Address.ANY, 0);
- }
+ if (gateway != null) {
+ if (gateway instanceof Inet4Address) {
+ destination = new LinkAddress(Inet4Address.ANY, 0);
} else {
- // no destination, no gateway. invalid.
- throw new RuntimeException("Invalid arguments passed in.");
+ destination = new LinkAddress(Inet6Address.ANY, 0);
}
- } catch (Exception e) {}
+ } else {
+ // no destination, no gateway. invalid.
+ throw new RuntimeException("Invalid arguments passed in.");
+ }
}
if (gateway == null) {
if (destination.getAddress() instanceof Inet4Address) {
@@ -76,6 +74,20 @@ public class RouteInfo implements Parcelable {
this(null, gateway);
}
+ public static RouteInfo makeHostRoute(InetAddress host) {
+ return makeHostRoute(host, null);
+ }
+
+ public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway) {
+ if (host == null) return null;
+
+ if (host instanceof Inet4Address) {
+ return new RouteInfo(new LinkAddress(host, 32), gateway);
+ } else {
+ return new RouteInfo(new LinkAddress(host, 128), gateway);
+ }
+ }
+
private boolean isDefault() {
boolean val = false;
if (mGateway != null) {
@@ -128,6 +140,33 @@ public class RouteInfo implements Parcelable {
}
}
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+
+ if (!(obj instanceof RouteInfo)) return false;
+
+ RouteInfo target = (RouteInfo) obj;
+
+ boolean sameDestination = ( mDestination == null) ?
+ target.getDestination() == null
+ : mDestination.equals(target.getDestination());
+
+ boolean sameAddress = (mGateway == null) ?
+ target.getGateway() == null
+ : mGateway.equals(target.getGateway());
+
+ return sameDestination && sameAddress
+ && mIsDefault == target.mIsDefault;
+ }
+
+ @Override
+ public int hashCode() {
+ return (mDestination == null ? 0 : mDestination.hashCode())
+ + (mGateway == null ? 0 :mGateway.hashCode())
+ + (mIsDefault ? 3 : 7);
+ }
+
public static final Creator<RouteInfo> CREATOR =
new Creator<RouteInfo>() {
public RouteInfo createFromParcel(Parcel in) {
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index fe36786a43a8..ecc111b31b5d 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -20,6 +20,7 @@ package android.os;
import android.net.InterfaceConfiguration;
import android.net.INetworkManagementEventObserver;
import android.net.NetworkStats;
+import android.net.RouteInfo;
import android.net.wifi.WifiConfiguration;
/**
@@ -58,6 +59,22 @@ interface INetworkManagementService
void setInterfaceConfig(String iface, in InterfaceConfiguration cfg);
/**
+ * Retrieves the network routes currently configured on the specified
+ * interface
+ */
+ RouteInfo[] getRoutes(String iface);
+
+ /**
+ * Add the specified route to the interface.
+ */
+ void addRoute(String iface, in RouteInfo route);
+
+ /**
+ * Remove the specified route from the interface.
+ */
+ void removeRoute(String iface, in RouteInfo route);
+
+ /**
* Shuts down the service
*/
void shutdown();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 68caa536b664..1af0867e4f71 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8496,6 +8496,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
jumpDrawablesToCurrentState();
+ // We are supposing here that the parent directionality will be resolved before its children
+ // View horizontalDirection public attribute resolution to an internal var.
// Resolving the layout direction. LTR is set initially.
mPrivateFlags2 &= ~RESOLVED_LAYOUT_RTL;
switch (getHorizontalDirection()) {
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index fecf9df5afdc..7819b173c3df 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -341,7 +341,7 @@ public class AccessibilityRecord {
*
* @return An instance.
*/
- protected static AccessibilityRecord obtain() {
+ public static AccessibilityRecord obtain() {
synchronized (sPoolLock) {
if (sPool != null) {
AccessibilityRecord record = sPool;
diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp
index 4d73bf3d066d..68be9e1e7fee 100644
--- a/core/jni/android_hardware_UsbDeviceConnection.cpp
+++ b/core/jni/android_hardware_UsbDeviceConnection.cpp
@@ -83,6 +83,27 @@ android_hardware_UsbDeviceConnection_get_fd(JNIEnv *env, jobject thiz)
return usb_device_get_fd(device);
}
+static jbyteArray
+android_hardware_UsbDeviceConnection_get_desc(JNIEnv *env, jobject thiz)
+{
+ char buffer[16384];
+ int fd = android_hardware_UsbDeviceConnection_get_fd(env, thiz);
+ if (fd < 0) return NULL;
+ lseek(fd, 0, SEEK_SET);
+ int length = read(fd, buffer, sizeof(buffer));
+ if (length < 0) return NULL;
+
+ jbyteArray ret = env->NewByteArray(length);
+ if (ret) {
+ jbyte* bytes = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0);
+ if (bytes) {
+ memcpy(bytes, buffer, length);
+ env->ReleasePrimitiveArrayCritical(ret, bytes, 0);
+ }
+ }
+ return ret;
+}
+
static jboolean
android_hardware_UsbDeviceConnection_claim_interface(JNIEnv *env, jobject thiz,
int interfaceID, jboolean force)
@@ -211,6 +232,7 @@ static JNINativeMethod method_table[] = {
(void *)android_hardware_UsbDeviceConnection_open},
{"native_close", "()V", (void *)android_hardware_UsbDeviceConnection_close},
{"native_get_fd", "()I", (void *)android_hardware_UsbDeviceConnection_get_fd},
+ {"native_get_desc", "()[B", (void *)android_hardware_UsbDeviceConnection_get_desc},
{"native_claim_interface", "(IZ)Z",(void *)android_hardware_UsbDeviceConnection_claim_interface},
{"native_release_interface","(I)Z", (void *)android_hardware_UsbDeviceConnection_release_interface},
{"native_control_request", "(IIII[BII)I",
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 548376df5296..ddae50527d4d 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -26,10 +26,6 @@
extern "C" {
int ifc_enable(const char *ifname);
int ifc_disable(const char *ifname);
-int ifc_add_route(const char *ifname, const char *destStr, uint32_t prefixLen, const char *gwStr);
-int ifc_remove_host_routes(const char *ifname);
-int ifc_get_default_route(const char *ifname);
-int ifc_remove_default_route(const char *ifname);
int ifc_reset_connections(const char *ifname);
int dhcp_do_request(const char *ifname,
@@ -94,56 +90,6 @@ static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstri
return (jint)result;
}
-static jint android_net_utils_addRoute(JNIEnv* env, jobject clazz, jstring ifname,
- jstring dst, jint prefixLength, jstring gw)
-{
- int result;
-
- const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- const char *dstStr = env->GetStringUTFChars(dst, NULL);
- const char *gwStr = NULL;
- if (gw != NULL) {
- gwStr = env->GetStringUTFChars(gw, NULL);
- }
- result = ::ifc_add_route(nameStr, dstStr, prefixLength, gwStr);
- env->ReleaseStringUTFChars(ifname, nameStr);
- env->ReleaseStringUTFChars(dst, dstStr);
- if (gw != NULL) {
- env->ReleaseStringUTFChars(gw, gwStr);
- }
- return (jint)result;
-}
-
-static jint android_net_utils_removeHostRoutes(JNIEnv* env, jobject clazz, jstring ifname)
-{
- int result;
-
- const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- result = ::ifc_remove_host_routes(nameStr);
- env->ReleaseStringUTFChars(ifname, nameStr);
- return (jint)result;
-}
-
-static jint android_net_utils_getDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname)
-{
- int result;
-
- const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- result = ::ifc_get_default_route(nameStr);
- env->ReleaseStringUTFChars(ifname, nameStr);
- return (jint)result;
-}
-
-static jint android_net_utils_removeDefaultRoute(JNIEnv* env, jobject clazz, jstring ifname)
-{
- int result;
-
- const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- result = ::ifc_remove_default_route(nameStr);
- env->ReleaseStringUTFChars(ifname, nameStr);
- return (jint)result;
-}
-
static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz, jstring ifname)
{
int result;
@@ -260,12 +206,6 @@ static JNINativeMethod gNetworkUtilMethods[] = {
{ "enableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_enableInterface },
{ "disableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_disableInterface },
- { "addRoute", "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;)I",
- (void *)android_net_utils_addRoute },
- { "removeHostRoutes", "(Ljava/lang/String;)I", (void *)android_net_utils_removeHostRoutes },
- { "getDefaultRouteNative", "(Ljava/lang/String;)I",
- (void *)android_net_utils_getDefaultRoute },
- { "removeDefaultRoute", "(Ljava/lang/String;)I", (void *)android_net_utils_removeDefaultRoute },
{ "resetConnections", "(Ljava/lang/String;)I", (void *)android_net_utils_resetConnections },
{ "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z", (void *)android_net_utils_runDhcp },
{ "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpInfoInternal;)Z", (void *)android_net_utils_runDhcpRenew },
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 041d9e65926d..e405f20cd1af 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -19,9 +19,9 @@
-->
<resources>
<!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_width">64dp</dimen>
+ <dimen name="thumbnail_width">120dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_height">100dp</dimen>
+ <dimen name="thumbnail_height">120dp</dimen>
<!-- The standard size (both width and height) of an application icon that
will be displayed in the app launcher and elsewhere. -->
<dimen name="app_icon_size">48dip</dimen>
diff --git a/data/fonts/MTLc3m.ttf b/data/fonts/MTLc3m.ttf
index 3cc5c96051b2..86bdcc722220 100644
--- a/data/fonts/MTLc3m.ttf
+++ b/data/fonts/MTLc3m.ttf
Binary files differ
diff --git a/data/fonts/MTLmr3m.ttf b/data/fonts/MTLmr3m.ttf
index 05b9093f57ba..76fe7370cd97 100644
--- a/data/fonts/MTLmr3m.ttf
+++ b/data/fonts/MTLmr3m.ttf
Binary files differ
diff --git a/docs/html/guide/appendix/install-location.jd b/docs/html/guide/appendix/install-location.jd
index 7f96809104ac..617f4fc5aa0e 100644
--- a/docs/html/guide/appendix/install-location.jd
+++ b/docs/html/guide/appendix/install-location.jd
@@ -171,6 +171,11 @@ persist after external storage is remounted.</dd>
<dd>The system delivers the {@link android.content.Intent#ACTION_BOOT_COMPLETED} broadcast
before the external storage is mounted to the device. If your application is installed on the
external storage, it can never receive this broadcast.</dd>
+ <dt>Copy Protection</dt>
+ <dd>Your application cannot be installed to a device's SD card if it uses Android Market's
+ Copy Protection feature. However, if you use Android Market's
+ <a href="{@docRoot}guide/publishing/licensing.html">Application Licensing</a> instead, your
+ application <em>can</em> be installed to internal or external storage, including SD cards.</dd>
</dl>
<p>If your application uses any of the features listed above, you <strong>should not</strong> allow
diff --git a/docs/html/guide/publishing/licensing.jd b/docs/html/guide/publishing/licensing.jd
index a9b182eff837..d89a8ca810dc 100644
--- a/docs/html/guide/publishing/licensing.jd
+++ b/docs/html/guide/publishing/licensing.jd
@@ -263,15 +263,15 @@ licensed for all users. If your application is already published as free,
you won't be able to upload a new version that uses licensing.</li>
</ul>
-<h4>Replacement for copy protection</h4>
+<h4>Replacement for Copy Protection</h4>
<p>Android Market Licensing is a flexible, secure mechanism for controlling
-access to your applications. It effectively replaces the copy-protection
+access to your applications. It effectively replaces the Copy Protection
mechanism offered on Android Market and gives you wider distribution
potential for your applications. </p>
<ul>
-<li>A limitation of the legacy copy-protection mechanism on Android Market is
+<li>A limitation of the legacy Copy Protection mechanism on Android Market is
that applications using it can be installed only on compatible devices that
provide a secure internal storage environment. For example, a copy-protected
application cannot be downloaded from Market to a device that provides root
diff --git a/docs/html/guide/topics/manifest/manifest-element.jd b/docs/html/guide/topics/manifest/manifest-element.jd
index a8125b3384cb..598e88ffda66 100644
--- a/docs/html/guide/topics/manifest/manifest-element.jd
+++ b/docs/html/guide/topics/manifest/manifest-element.jd
@@ -141,9 +141,14 @@ either internal or external storage through the system settings.</td>
</tr>
</table>
+<p class="caution"><strong>Caution:</strong> If your application uses the Android Market's Copy
+ Protection feature, it cannot be installed to a device's SD card. However, if you use Android
+ Market's <a href="{@docRoot}guide/publishing/licensing.html">Application Licensing</a> instead,
+ your application <em>can</em> be installed to internal or external storage, including SD cards.</p>
+
<p class="note"><strong>Note:</strong> By default, your application will be installed on the
-internal storage and cannot be installed on the external storage unless you define this attribute
-to be either "{@code auto}" or "{@code preferExternal}".</p>
+ internal storage and cannot be installed on the external storage unless you define this attribute
+ to be either "{@code auto}" or "{@code preferExternal}".</p>
<p>When an application is installed on the external storage:</p>
<ul>
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 352579a51e55..a54cf2887fcc 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -176,6 +176,7 @@ public class MediaFile {
addFileType("OGG", FILE_TYPE_OGG, "application/ogg", MtpConstants.FORMAT_OGG);
addFileType("OGA", FILE_TYPE_OGG, "application/ogg", MtpConstants.FORMAT_OGG);
addFileType("AAC", FILE_TYPE_AAC, "audio/aac", MtpConstants.FORMAT_AAC);
+ addFileType("AAC", FILE_TYPE_AAC, "audio/aac-adts", MtpConstants.FORMAT_AAC);
addFileType("MKA", FILE_TYPE_MKA, "audio/x-matroska");
addFileType("MID", FILE_TYPE_MID, "audio/midi");
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 0f5a440b0dbb..38202f24682d 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -347,7 +347,7 @@ public class MediaRecorder
}
/**
- * Store the geodata (latitude and longitude) in the output file.
+ * Set and store the geodata (latitude and longitude) in the output file.
* This method should be called before prepare(). The geodata is
* stored in udta box if the output format is OutputFormat.THREE_GPP
* or OutputFormat.MPEG_4, and is ignored for other output formats.
@@ -361,18 +361,17 @@ public class MediaRecorder
* @throws IllegalArgumentException if the given latitude or
* longitude is out of range.
*
- * {@hide}
*/
- public void setGeoData(float latitude, float longitude) {
+ public void setLocation(float latitude, float longitude) {
int latitudex10000 = (int) (latitude * 10000 + 0.5);
int longitudex10000 = (int) (longitude * 10000 + 0.5);
if (latitudex10000 > 900000 || latitudex10000 < -900000) {
- String msg = "Unsupported latitude: " + latitude;
+ String msg = "Latitude: " + latitude + " out of range.";
throw new IllegalArgumentException(msg);
}
if (longitudex10000 > 1800000 || longitudex10000 < -1800000) {
- String msg = "Unsupported longitude: " + longitude;
+ String msg = "Longitude: " + longitude + " out of range";
throw new IllegalArgumentException(msg);
}
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index b45e5d349c0f..fb7a8710bfbe 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -24,11 +24,13 @@
#include "include/ARTSPController.h"
#include "include/AwesomePlayer.h"
+#include "include/DRMExtractor.h"
#include "include/SoftwareRenderer.h"
#include "include/NuCachedSource2.h"
#include "include/ThrottledSource.h"
#include "include/MPEG2TSExtractor.h"
#include "include/TimedTextPlayer.h"
+#include "include/WVMExtractor.h"
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -447,6 +449,7 @@ void AwesomePlayer::reset_l() {
cancelPlayerEvents();
+ mWVMExtractor.clear();
mCachedSource.clear();
mAudioTrack.clear();
mVideoTrack.clear();
@@ -545,6 +548,11 @@ bool AwesomePlayer::getCachedDuration_l(int64_t *durationUs, bool *eos) {
*durationUs = cachedDataRemaining * 8000000ll / bitrate;
*eos = (finalStatus != OK);
return true;
+ } else if (mWVMExtractor != NULL) {
+ status_t finalStatus;
+ *durationUs = mWVMExtractor->getCachedDurationUs(&finalStatus);
+ *eos = (finalStatus != OK);
+ return true;
}
return false;
@@ -637,6 +645,30 @@ void AwesomePlayer::onBufferingUpdate() {
}
}
}
+ } else if (mWVMExtractor != NULL) {
+ status_t finalStatus;
+
+ int64_t cachedDurationUs
+ = mWVMExtractor->getCachedDurationUs(&finalStatus);
+
+ bool eos = (finalStatus != OK);
+
+ if (eos) {
+ if (finalStatus == ERROR_END_OF_STREAM) {
+ notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
+ }
+ if (mFlags & PREPARING) {
+ LOGV("cache has reached EOS, prepare is done.");
+ finishAsyncPrepare_l();
+ }
+ } else {
+ int percentage = 100.0 * (double)cachedDurationUs / mDurationUs;
+ if (percentage > 100) {
+ percentage = 100;
+ }
+
+ notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage);
+ }
}
int64_t cachedDurationUs;
@@ -1425,7 +1457,7 @@ void AwesomePlayer::onVideoEvent() {
mVideoBuffer = NULL;
}
- if (mSeeking == SEEK && mCachedSource != NULL && mAudioSource != NULL
+ if (mSeeking == SEEK && isStreamingHTTP() && mAudioSource != NULL
&& !(mFlags & SEEK_PREVIEW)) {
// We're going to seek the video source first, followed by
// the audio source.
@@ -1760,8 +1792,19 @@ status_t AwesomePlayer::prepareAsync_l() {
status_t AwesomePlayer::finishSetDataSource_l() {
sp<DataSource> dataSource;
+ bool isWidevineStreaming = false;
+ if (!strncasecmp("widevine://", mUri.string(), 11)) {
+ isWidevineStreaming = true;
+
+ String8 newURI = String8("http://");
+ newURI.append(mUri.string() + 11);
+
+ mUri = newURI;
+ }
+
if (!strncasecmp("http://", mUri.string(), 7)
- || !strncasecmp("https://", mUri.string(), 8)) {
+ || !strncasecmp("https://", mUri.string(), 8)
+ || isWidevineStreaming) {
mConnectingDataSource = HTTPBase::Create(
(mFlags & INCOGNITO)
? HTTPBase::kFlagIncognito
@@ -1778,16 +1821,24 @@ status_t AwesomePlayer::finishSetDataSource_l() {
return err;
}
+ if (!isWidevineStreaming) {
+ // The widevine extractor does its own caching.
+
#if 0
- mCachedSource = new NuCachedSource2(
- new ThrottledSource(
- mConnectingDataSource, 50 * 1024 /* bytes/sec */));
+ mCachedSource = new NuCachedSource2(
+ new ThrottledSource(
+ mConnectingDataSource, 50 * 1024 /* bytes/sec */));
#else
- mCachedSource = new NuCachedSource2(mConnectingDataSource);
+ mCachedSource = new NuCachedSource2(mConnectingDataSource);
#endif
+
+ dataSource = mCachedSource;
+ } else {
+ dataSource = mConnectingDataSource;
+ }
+
mConnectingDataSource.clear();
- dataSource = mCachedSource;
String8 contentType = dataSource->getMIMEType();
@@ -1801,28 +1852,35 @@ status_t AwesomePlayer::finishSetDataSource_l() {
// could block on the datasource for a significant amount of time.
// During that time we'd be unable to abort the preparation phase
// without this prefill.
+ if (mCachedSource != NULL) {
+ // We're going to prefill the cache before trying to instantiate
+ // the extractor below, as the latter is an operation that otherwise
+ // could block on the datasource for a significant amount of time.
+ // During that time we'd be unable to abort the preparation phase
+ // without this prefill.
+
+ mLock.unlock();
+
+ for (;;) {
+ status_t finalStatus;
+ size_t cachedDataRemaining =
+ mCachedSource->approxDataRemaining(&finalStatus);
+
+ if (finalStatus != OK || cachedDataRemaining >= kHighWaterMarkBytes
+ || (mFlags & PREPARE_CANCELLED)) {
+ break;
+ }
- mLock.unlock();
-
- for (;;) {
- status_t finalStatus;
- size_t cachedDataRemaining =
- mCachedSource->approxDataRemaining(&finalStatus);
-
- if (finalStatus != OK || cachedDataRemaining >= kHighWaterMarkBytes
- || (mFlags & PREPARE_CANCELLED)) {
- break;
+ usleep(200000);
}
- usleep(200000);
+ mLock.lock();
}
- mLock.lock();
- }
-
- if (mFlags & PREPARE_CANCELLED) {
- LOGI("Prepare cancelled while waiting for initial cache fill.");
- return UNKNOWN_ERROR;
+ if (mFlags & PREPARE_CANCELLED) {
+ LOGI("Prepare cancelled while waiting for initial cache fill.");
+ return UNKNOWN_ERROR;
+ }
}
} else if (!strncasecmp("rtsp://", mUri.string(), 7)) {
if (mLooper == NULL) {
@@ -1856,10 +1914,29 @@ status_t AwesomePlayer::finishSetDataSource_l() {
return UNKNOWN_ERROR;
}
- sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+ sp<MediaExtractor> extractor;
- if (extractor == NULL) {
- return UNKNOWN_ERROR;
+ if (isWidevineStreaming) {
+ String8 mimeType;
+ float confidence;
+ sp<AMessage> dummy;
+ bool success = SniffDRM(dataSource, &mimeType, &confidence, &dummy);
+
+ if (!success
+ || strcasecmp(
+ mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ mWVMExtractor = new WVMExtractor(dataSource);
+ mWVMExtractor->setAdaptiveStreamingMode(true);
+ extractor = mWVMExtractor;
+ } else {
+ extractor = MediaExtractor::Create(dataSource);
+
+ if (extractor == NULL) {
+ return UNKNOWN_ERROR;
+ }
}
dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient);
@@ -1871,7 +1948,15 @@ status_t AwesomePlayer::finishSetDataSource_l() {
}
}
- return setDataSource_l(extractor);
+ status_t err = setDataSource_l(extractor);
+
+ if (err != OK) {
+ mWVMExtractor.clear();
+
+ return err;
+ }
+
+ return OK;
}
void AwesomePlayer::abortPrepare(status_t err) {
@@ -1932,7 +2017,7 @@ void AwesomePlayer::onPrepareAsyncEvent() {
mFlags |= PREPARING_CONNECTED;
- if (mCachedSource != NULL || mRTSPController != NULL) {
+ if (isStreamingHTTP() || mRTSPController != NULL) {
postBufferingEvent_l();
} else {
finishAsyncPrepare_l();
@@ -1985,4 +2070,9 @@ status_t AwesomePlayer::setParameter(int key, const Parcel &request) {
status_t AwesomePlayer::getParameter(int key, Parcel *reply) {
return OK;
}
+
+bool AwesomePlayer::isStreamingHTTP() const {
+ return mCachedSource != NULL || mWVMExtractor != NULL;
+}
+
} // namespace android
diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp
index 7c7285257925..83a1eaa43983 100644
--- a/media/libstagefright/WVMExtractor.cpp
+++ b/media/libstagefright/WVMExtractor.cpp
@@ -45,7 +45,8 @@ namespace android {
static Mutex gWVMutex;
WVMExtractor::WVMExtractor(const sp<DataSource> &source)
- : mDataSource(source) {
+ : mDataSource(source),
+ mUseAdaptiveStreaming(false) {
{
Mutex::Autolock autoLock(gWVMutex);
if (gVendorLibHandle == NULL) {
@@ -100,5 +101,21 @@ sp<MetaData> WVMExtractor::getMetaData() {
return mImpl->getMetaData();
}
+int64_t WVMExtractor::getCachedDurationUs(status_t *finalStatus) {
+ // TODO: Fill this with life.
+
+ *finalStatus = OK;
+
+ return 0;
+}
+
+void WVMExtractor::setAdaptiveStreamingMode(bool adaptive) {
+ mUseAdaptiveStreaming = adaptive;
+}
+
+bool WVMExtractor::getAdaptiveStreamingMode() const {
+ return mUseAdaptiveStreaming;
+}
+
} //namespace android
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 835d2bbc402b..3c9a12163270 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -45,6 +45,7 @@ class DrmManagerClinet;
class DecryptHandle;
class TimedTextPlayer;
+struct WVMExtractor;
struct AwesomeRenderer : public RefBase {
AwesomeRenderer() {}
@@ -231,6 +232,8 @@ private:
int64_t mLastVideoTimeUs;
TimedTextPlayer *mTextPlayer;
+ sp<WVMExtractor> mWVMExtractor;
+
status_t setDataSource_l(
const char *uri,
const KeyedVector<String8, String8> *headers = NULL);
@@ -286,6 +289,8 @@ private:
void shutdownVideoDecoder_l();
void setNativeWindow_l(const sp<ANativeWindow> &native);
+ bool isStreamingHTTP() const;
+
AwesomePlayer(const AwesomePlayer &);
AwesomePlayer &operator=(const AwesomePlayer &);
};
diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h
index 0da45a8d7d93..62e5aa562eb1 100644
--- a/media/libstagefright/include/WVMExtractor.h
+++ b/media/libstagefright/include/WVMExtractor.h
@@ -19,6 +19,7 @@
#define WVM_EXTRACTOR_H_
#include <media/stagefright/MediaExtractor.h>
+#include <utils/Errors.h>
namespace android {
@@ -33,12 +34,31 @@ public:
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
+ // Return the amount of data cached from the current
+ // playback positiion (in us).
+ // While more data is still being fetched *finalStatus == OK,
+ // Once fetching is completed (no more data available), *finalStatus != OK
+ // If fetching completed normally (i.e. reached EOS instead of IO error)
+ // *finalStatus == ERROR_END_OF_STREAM
+ int64_t getCachedDurationUs(status_t *finalStatus);
+
+ // Set to use adaptive streaming mode by the WV component.
+ // If adaptive == true, adaptive streaming mode will be used.
+ // Default mode is non-adaptive streaming mode.
+ // Should set to use adaptive streaming mode only if widevine:// protocol
+ // is used.
+ void setAdaptiveStreamingMode(bool adaptive);
+
+ // Retrieve the adaptive streaming mode used by the WV component.
+ bool getAdaptiveStreamingMode() const;
+
protected:
virtual ~WVMExtractor();
private:
sp<DataSource> mDataSource;
sp<MediaExtractor> mImpl;
+ bool mUseAdaptiveStreaming;
WVMExtractor(const WVMExtractor &);
WVMExtractor &operator=(const WVMExtractor &);
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-hdpi/recents_bg_protect_tile.png
new file mode 100644
index 000000000000..87c7be69a357
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_bg_protect_tile.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.png
new file mode 100644
index 000000000000..4f4ae788e3f1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-hdpi/recents_callout_line.png
new file mode 100644
index 000000000000..5f4c0357e674
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_callout_line.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.png
new file mode 100644
index 000000000000..87a67c9cf474
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-mdpi/recents_bg_protect_tile.png
new file mode 100644
index 000000000000..87c7be69a357
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_bg_protect_tile.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.png
new file mode 100644
index 000000000000..4f4ae788e3f1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-mdpi/recents_callout_line.png
new file mode 100644
index 000000000000..5f4c0357e674
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_callout_line.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.png
new file mode 100644
index 000000000000..87a67c9cf474
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.png b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.png
new file mode 100644
index 000000000000..a1c39e6ec046
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
new file mode 100644
index 000000000000..ce72f046c101
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- android:background="@drawable/status_bar_closed_default_background" -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="@dimen/status_bar_recents_thumbnail_view_width">
+
+ <ImageView android:id="@+id/app_thumbnail"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
+ android:scaleType="center"
+ android:background="@drawable/recents_thumbnail_bg_selector"
+ />
+
+ <ImageView android:id="@+id/app_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignLeft="@id/app_thumbnail"
+ android:layout_alignTop="@id/app_thumbnail"
+ android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_border_width"
+ android:layout_marginTop="@dimen/status_bar_recents_thumbnail_border_height"
+ android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width"
+ android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height"
+ android:adjustViewBounds="true"
+ />
+
+ <TextView android:id="@+id/app_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/status_bar_recents_app_label_text_size"
+ android:fadingEdge="horizontal"
+ android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:scrollHorizontally="true"
+ android:layout_alignLeft="@id/app_thumbnail"
+ android:layout_below="@id/app_thumbnail"
+ android:layout_marginTop="@dimen/status_bar_recents_text_description_padding"
+ android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_border_width"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ />
+
+ <TextView android:id="@+id/app_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/status_bar_recents_app_description_text_size"
+ android:fadingEdge="horizontal"
+ android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:scrollHorizontally="true"
+ android:layout_alignLeft="@id/app_thumbnail"
+ android:layout_below="@id/app_label"
+ android:layout_marginTop="@dimen/status_bar_recents_text_description_padding"
+ android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_border_width"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ />
+
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
new file mode 100644
index 000000000000..75f5ee493a54
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2010, 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.
+*/
+-->
+
+<com.android.systemui.recent.RecentsPanelView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/recents_root"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content">
+
+ <FrameLayout
+ android:id="@+id/recents_bg_protect"
+ android:background="@drawable/recents_bg_protect_tile"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+ android:paddingBottom="@*android:dimen/status_bar_height"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <LinearLayout android:id="@+id/recents_glow"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|right"
+ android:background="@drawable/recents_blue_glow"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ >
+ <com.android.systemui.recent.RecentsHorizontalScrollView android:id="@+id/recents_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="@dimen/status_bar_recents_right_glow_margin"
+ android:divider="@null"
+ android:stackFromBottom="true"
+ android:fadingEdge="horizontal"
+ android:scrollbars="none"
+ android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:listSelector="@drawable/recents_thumbnail_bg_selector"
+ android:layout_gravity="bottom|left"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <LinearLayout android:id="@+id/recents_linear_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+ </LinearLayout>
+
+ </com.android.systemui.recent.RecentsHorizontalScrollView>
+
+ </LinearLayout>
+
+ </FrameLayout>
+
+ <View android:id="@+id/recents_dismiss_button"
+ android:layout_width="80px"
+ android:layout_height="@*android:dimen/status_bar_height"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:background="@drawable/ic_sysbar_back_ime"
+ />
+
+</com.android.systemui.recent.RecentsPanelView>
diff --git a/packages/SystemUI/res/layout-large/status_bar_recent_item.xml b/packages/SystemUI/res/layout-large/status_bar_recent_item.xml
index 3f172e68f3f8..cd42d7eabb44 100644
--- a/packages/SystemUI/res/layout-large/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-large/status_bar_recent_item.xml
@@ -60,9 +60,9 @@
<TextView android:id="@+id/app_label"
android:layout_width="97dip"
android:layout_height="wrap_content"
- android:textSize="18dip"
+ android:textSize="@dimen/status_bar_recents_app_description_text_size"
android:fadingEdge="horizontal"
- android:fadingEdgeLength="10dip"
+ android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
android:scrollHorizontally="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
@@ -75,9 +75,9 @@
<TextView android:id="@+id/app_description"
android:layout_width="97dip"
android:layout_height="wrap_content"
- android:textSize="18dip"
+ android:textSize="@dimen/status_bar_recents_app_description_text_size"
android:fadingEdge="horizontal"
- android:fadingEdgeLength="10dip"
+ android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
android:scrollHorizontally="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
diff --git a/packages/SystemUI/res/layout-large/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-large/status_bar_recent_panel.xml
index f019e2d84a71..75fdc67cf534 100644
--- a/packages/SystemUI/res/layout-large/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-large/status_bar_recent_panel.xml
@@ -18,32 +18,23 @@
*/
-->
-<com.android.systemui.statusbar.tablet.RecentAppsPanel
+<com.android.systemui.recent.RecentsPanelView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recents_root"
android:layout_height="match_parent"
- android:layout_width="wrap_content">
-
- <CheckBox android:id="@+id/recents_compat_mode"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentRight="true"
- android:layout_marginLeft="16dp"
- android:layout_marginBottom="@*android:dimen/status_bar_height"
- android:background="@drawable/hd"
- android:button="@null"
- />
+ android:layout_width="wrap_content"
+ android:clipToPadding="false"
+ android:clipChildren="false">
<FrameLayout
android:id="@+id/recents_bg_protect"
android:background="@drawable/recents_bg_protect_tile"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_toLeftOf="@id/recents_compat_mode"
android:layout_alignParentBottom="true"
android:paddingBottom="@*android:dimen/status_bar_height"
- android:clipToPadding="false">
+ android:clipToPadding="false"
+ android:clipChildren="false">
<LinearLayout android:id="@+id/recents_glow"
android:layout_width="wrap_content"
@@ -52,20 +43,32 @@
android:layout_gravity="bottom"
android:background="@drawable/recents_blue_glow"
android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false"
>
-
- <ListView android:id="@+id/recents_container"
+ <com.android.systemui.recent.RecentsVerticalScrollView android:id="@+id/recents_container"
android:layout_width="@dimen/status_bar_recents_width"
android:layout_height="wrap_content"
- android:layout_marginRight="100dip"
+ android:layout_marginRight="@dimen/status_bar_recents_right_glow_margin"
android:divider="@null"
- android:scrollingCache="true"
android:stackFromBottom="true"
android:fadingEdge="vertical"
android:scrollbars="none"
android:fadingEdgeLength="20dip"
+ android:layout_gravity="bottom|left"
android:listSelector="@drawable/recents_thumbnail_bg_selector"
- />
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <LinearLayout android:id="@+id/recents_linear_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+ </LinearLayout>
+
+ </com.android.systemui.recent.RecentsVerticalScrollView>
</LinearLayout>
@@ -79,4 +82,4 @@
android:background="@drawable/ic_sysbar_back_ime"
/>
-</com.android.systemui.statusbar.tablet.RecentAppsPanel>
+</com.android.systemui.recent.RecentsPanelView>
diff --git a/packages/SystemUI/res/layout/status_bar_recent_item.xml b/packages/SystemUI/res/layout/status_bar_recent_item.xml
new file mode 100644
index 000000000000..cd42d7eabb44
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_recent_item.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- android:background="@drawable/status_bar_closed_default_background" -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="@dimen/status_bar_recents_thumbnail_view_width">
+
+ <ImageView android:id="@+id/app_thumbnail"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
+ android:scaleType="center"
+ />
+
+ <ImageView android:id="@+id/app_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="131dip"
+ android:layout_marginTop="13dip"
+ android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width"
+ android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height"
+ android:adjustViewBounds="true"
+ />
+
+ <View android:id="@+id/recents_callout_line"
+ android:layout_width="97dip"
+ android:layout_height="1dip"
+ android:layout_alignParentTop="true"
+ android:layout_marginTop="61dip"
+ android:layout_alignParentLeft="true"
+ android:layout_marginLeft="16dip"
+ android:layout_toLeftOf="@id/app_thumbnail"
+ android:layout_marginRight="3dip"
+ android:background="@drawable/recents_callout_line"
+ />
+
+ <TextView android:id="@+id/app_label"
+ android:layout_width="97dip"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/status_bar_recents_app_description_text_size"
+ android:fadingEdge="horizontal"
+ android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:scrollHorizontally="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="16dip"
+ android:layout_marginTop="32dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ />
+
+ <TextView android:id="@+id/app_description"
+ android:layout_width="97dip"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/status_bar_recents_app_description_text_size"
+ android:fadingEdge="horizontal"
+ android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:scrollHorizontally="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="16dip"
+ android:layout_marginTop="61dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ />
+
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_recent_panel.xml b/packages/SystemUI/res/layout/status_bar_recent_panel.xml
new file mode 100644
index 000000000000..703cbc1f79d3
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_recent_panel.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2010, 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.
+*/
+-->
+
+<com.android.systemui.recent.RecentsPanelView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/recents_root"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content">
+
+ <FrameLayout
+ android:id="@+id/recents_bg_protect"
+ android:background="@drawable/recents_bg_protect_tile"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentBottom="true"
+ android:paddingBottom="@*android:dimen/status_bar_height"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <LinearLayout android:id="@+id/recents_glow"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="-49dip"
+ android:layout_gravity="bottom"
+ android:background="@drawable/recents_blue_glow"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ >
+ <com.android.systemui.recent.RecentsVerticalScrollView android:id="@+id/recents_container"
+ android:layout_width="@dimen/status_bar_recents_width"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="@dimen/status_bar_recents_right_glow_margin"
+ android:divider="@null"
+ android:stackFromBottom="true"
+ android:fadingEdge="vertical"
+ android:scrollbars="none"
+ android:fadingEdgeLength="20dip"
+ android:listSelector="@drawable/recents_thumbnail_bg_selector"
+ android:layout_gravity="bottom|left"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <LinearLayout android:id="@+id/recents_linear_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+ </LinearLayout>
+
+ </com.android.systemui.recent.RecentsVerticalScrollView>
+
+
+ </LinearLayout>
+
+ </FrameLayout>
+
+ <View android:id="@+id/recents_dismiss_button"
+ android:layout_width="80px"
+ android:layout_height="@*android:dimen/status_bar_height"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:background="@drawable/ic_sysbar_back_ime"
+ />
+
+</com.android.systemui.recent.RecentsPanelView>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index bcc8da1f8595..6f1453e4df96 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -18,4 +18,20 @@
<resources>
<!-- thickness (width) of the navigation bar on phones that require it -->
<dimen name="navigation_bar_size">42dp</dimen>
+
+ <!-- Recent Applications parameters -->
+ <!-- Width of a recent app view, including all content -->
+ <dimen name="status_bar_recents_thumbnail_view_width">156dp</dimen>
+ <!-- How far the thumbnail for a recent app appears from left edge -->
+ <dimen name="status_bar_recents_thumbnail_left_margin">0dp</dimen>
+ <!-- Width of scrollable area in recents -->
+ <dimen name="status_bar_recents_width">128dp</dimen>
+ <!-- Thumbnail border width -->
+ <dimen name="status_bar_recents_thumbnail_border_width">8dp</dimen>
+ <!-- Thumbnail border height -->
+ <dimen name="status_bar_recents_thumbnail_border_height">12dp</dimen>
+ <!-- Padding for text descriptions -->
+ <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
+ <!-- Margin between recents container and glow on the right -->
+ <dimen name="status_bar_recents_right_glow_margin">0dip</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-large/dimens.xml b/packages/SystemUI/res/values-large/dimens.xml
index 9d89e21761ab..f8a4a1c45a9c 100644
--- a/packages/SystemUI/res/values-large/dimens.xml
+++ b/packages/SystemUI/res/values-large/dimens.xml
@@ -22,6 +22,32 @@
<dimen name="status_bar_panel_bottom_offset">36dp</dimen>
<!-- gap on either side of status bar notification icons -->
<dimen name="status_bar_icon_padding">8dp</dimen>
+
+ <!-- Recent Applications parameters -->
+ <!-- Width of a recent app view, including all content -->
+ <dimen name="status_bar_recents_thumbnail_view_width">156dp</dimen>
+ <!-- How far the thumbnail for a recent app appears from left edge -->
+ <dimen name="status_bar_recents_thumbnail_left_margin">110dp</dimen>
+ <!-- Upper width limit for application icon -->
+ <dimen name="status_bar_recents_thumbnail_max_width">64dp</dimen>
+ <!-- Upper height limit for application icon -->
+ <dimen name="status_bar_recents_thumbnail_max_height">64dp</dimen>
+ <!-- Width of scrollable area in recents -->
+ <dimen name="status_bar_recents_width">356dp</dimen>
+ <!-- Thumbnail border width -->
+ <dimen name="status_bar_recents_thumbnail_border_width">12dp</dimen>
+ <!-- Thumbnail border height -->
+ <dimen name="status_bar_recents_thumbnail_border_height">12dp</dimen>
+ <!-- Padding for text descriptions -->
+ <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
+ <!-- Size of application label text -->
+ <dimen name="status_bar_recents_app_label_text_size">18dip</dimen>
+ <!-- Size of application description text -->
+ <dimen name="status_bar_recents_app_description_text_size">18dip</dimen>
+ <!-- Size of fading edge for scroll effect -->
+ <dimen name="status_bar_recents_fading_edge_length">20dip</dimen>
+ <!-- Margin between recents container and glow on the right -->
+ <dimen name="status_bar_recents_right_glow_margin">100dip</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a2577cb738c1..657dc46887e0 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -30,6 +30,20 @@
<dimen name="status_bar_recents_thumbnail_max_height">64dp</dimen>
<!-- Width of scrollable area in recents -->
<dimen name="status_bar_recents_width">356dp</dimen>
+ <!-- Thumbnail border width -->
+ <dimen name="status_bar_recents_thumbnail_border_width">12dp</dimen>
+ <!-- Thumbnail border height -->
+ <dimen name="status_bar_recents_thumbnail_border_height">12dp</dimen>
+ <!-- Padding for text descriptions -->
+ <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
+ <!-- Size of application label text -->
+ <dimen name="status_bar_recents_app_label_text_size">18dip</dimen>
+ <!-- Size of application description text -->
+ <dimen name="status_bar_recents_app_description_text_size">18dip</dimen>
+ <!-- Size of fading edge for scroll effect -->
+ <dimen name="status_bar_recents_fading_edge_length">20dip</dimen>
+ <!-- Margin between recents container and glow on the right -->
+ <dimen name="status_bar_recents_right_glow_margin">100dip</dimen>
<!-- thickness (height) of the navigation bar on phones that require it -->
<dimen name="navigation_bar_size">42dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
new file mode 100644
index 000000000000..b876075565aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011 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.systemui.recent;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.util.Log;
+import android.util.Slog;
+import android.view.View;
+
+/* package */ class Choreographer implements Animator.AnimatorListener {
+ // should group this into a multi-property animation
+ private static final int OPEN_DURATION = 136;
+ private static final int CLOSE_DURATION = 250;
+ private static final String TAG = RecentsPanelView.TAG;
+ private static final boolean DEBUG = RecentsPanelView.DEBUG;
+
+ boolean mVisible;
+ int mPanelHeight;
+ View mRootView;
+ View mScrimView;
+ View mContentView;
+ AnimatorSet mContentAnim;
+
+ // the panel will start to appear this many px from the end
+ final int HYPERSPACE_OFFRAMP = 200;
+
+ public Choreographer(View root, View scrim, View content) {
+ mRootView = root;
+ mScrimView = scrim;
+ mContentView = content;
+ }
+
+ void createAnimation(boolean appearing) {
+ float start, end;
+
+ if (RecentsPanelView.DEBUG) Log.e(TAG, "createAnimation()", new Exception());
+
+ // 0: on-screen
+ // height: off-screen
+ float y = mContentView.getTranslationY();
+ if (appearing) {
+ // we want to go from near-the-top to the top, unless we're half-open in the right
+ // general vicinity
+ start = (y < HYPERSPACE_OFFRAMP) ? y : HYPERSPACE_OFFRAMP;
+ end = 0;
+ } else {
+ start = y;
+ end = y + HYPERSPACE_OFFRAMP;
+ }
+
+ Animator posAnim = ObjectAnimator.ofFloat(mContentView, "translationY",
+ start, end);
+ posAnim.setInterpolator(appearing
+ ? new android.view.animation.DecelerateInterpolator(2.5f)
+ : new android.view.animation.AccelerateInterpolator(2.5f));
+
+ Animator glowAnim = ObjectAnimator.ofFloat(mContentView, "alpha",
+ mContentView.getAlpha(), appearing ? 1.0f : 0.0f);
+ glowAnim.setInterpolator(appearing
+ ? new android.view.animation.AccelerateInterpolator(1.0f)
+ : new android.view.animation.DecelerateInterpolator(1.0f));
+
+ Animator bgAnim = ObjectAnimator.ofInt(mScrimView.getBackground(),
+ "alpha", appearing ? 0 : 255, appearing ? 255 : 0);
+
+ mContentAnim = new AnimatorSet();
+ mContentAnim
+ .play(bgAnim)
+ .with(glowAnim)
+ .with(posAnim);
+ mContentAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
+ mContentAnim.addListener(this);
+ }
+
+ void startAnimation(boolean appearing) {
+ if (DEBUG) Slog.d(TAG, "startAnimation(appearing=" + appearing + ")");
+
+ createAnimation(appearing);
+
+ mContentView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mContentAnim.start();
+
+ mVisible = appearing;
+ }
+
+ void jumpTo(boolean appearing) {
+ mContentView.setTranslationY(appearing ? 0 : mPanelHeight);
+ }
+
+ public void setPanelHeight(int h) {
+ if (DEBUG) Slog.d(TAG, "panelHeight=" + h);
+ mPanelHeight = h;
+ }
+
+ public void onAnimationCancel(Animator animation) {
+ if (DEBUG) Slog.d(TAG, "onAnimationCancel");
+ // force this to zero so we close the window
+ mVisible = false;
+ }
+
+ public void onAnimationEnd(Animator animation) {
+ if (DEBUG) Slog.d(TAG, "onAnimationEnd");
+ if (!mVisible) {
+ mRootView.setVisibility(View.GONE);
+ }
+ mContentView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mContentAnim = null;
+ }
+
+ public void onAnimationRepeat(Animator animation) {
+ }
+
+ public void onAnimationStart(Animator animation) {
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java
new file mode 100644
index 000000000000..5d29e2aedf22
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 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.systemui.recent;
+
+import android.view.View;
+
+public interface RecentsCallback {
+ static final int SWIPE_LEFT = 0;
+ static final int SWIPE_RIGHT = 1;
+ static final int SWIPE_UP = 2;
+ static final int SWIPE_DOWN = 3;
+
+ void handleOnClick(View selectedView);
+ void handleSwipe(View selectedView, int direction);
+ void handleLongPress(View selectedView);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
new file mode 100644
index 000000000000..194c9d18f960
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2011 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.systemui.recent;
+
+import com.android.systemui.recent.RecentsPanelView.ActvityDescriptionAdapter;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.LayoutTransition;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
+import android.widget.HorizontalScrollView;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+
+public class RecentsHorizontalScrollView extends HorizontalScrollView
+ implements View.OnClickListener, View.OnTouchListener {
+ private static final float FADE_CONSTANT = 0.5f;
+ private static final int SNAP_BACK_DURATION = 250;
+ private static final int ESCAPE_VELOCITY = 100; // speed of item required to "curate" it
+ private static final String TAG = RecentsPanelView.TAG;
+ private static final float THRESHHOLD = 50;
+ private static final boolean DEBUG_INVALIDATE = false;
+ private LinearLayout mLinearLayout;
+ private ActvityDescriptionAdapter mAdapter;
+ private RecentsCallback mCallback;
+ protected int mLastScrollPosition;
+ private View mCurrentView;
+ private float mLastY;
+ private boolean mDragging;
+ private VelocityTracker mVelocityTracker;
+
+ public RecentsHorizontalScrollView(Context context) {
+ this(context, null);
+ }
+
+ public RecentsHorizontalScrollView(Context context, AttributeSet attrs) {
+ super(context, attrs, 0);
+ }
+
+ private int scrollPositionOfMostRecent() {
+ return mLinearLayout.getWidth() - getWidth();
+ }
+
+ public void update() {
+ mLinearLayout.removeAllViews();
+ for (int i = 0; i < mAdapter.getCount(); i++) {
+ View view = mAdapter.getView(i, null, mLinearLayout);
+ view.setClickable(true);
+ view.setOnClickListener(this);
+ view.setOnTouchListener(this);
+ mLinearLayout.addView(view);
+ }
+ // Scroll to end after layout.
+ post(new Runnable() {
+ public void run() {
+ mLastScrollPosition = scrollPositionOfMostRecent();
+ scrollTo(mLastScrollPosition, 0);
+ }
+ });
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mDragging = false;
+ mLastY = ev.getY();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ float delta = ev.getY() - mLastY;
+ if (Math.abs(delta) > THRESHHOLD) {
+ mDragging = true;
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ mDragging = false;
+ break;
+ }
+ return mDragging ? true : super.onInterceptTouchEvent(ev);
+ }
+
+ private float getAlphaForOffset(View view, float thumbHeight) {
+ final float fadeHeight = FADE_CONSTANT * thumbHeight;
+ float result = 1.0f;
+ if (view.getY() >= thumbHeight) {
+ result = 1.0f - (view.getY() - thumbHeight) / fadeHeight;
+ } else if (view.getY() < 0.0f) {
+ result = 1.0f + (thumbHeight + view.getY()) / fadeHeight;
+ }
+ return result;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (!mDragging) {
+ return super.onTouchEvent(ev);
+ }
+
+ mVelocityTracker.addMovement(ev);
+
+ final View animView = mCurrentView;
+ // TODO: Cache thumbnail
+ final View thumb = animView.findViewById(R.id.app_thumbnail);
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_MOVE:
+ if (animView != null) {
+ final float delta = ev.getY() - mLastY;
+ animView.setY(animView.getY() + delta);
+ animView.setAlpha(getAlphaForOffset(animView, thumb.getHeight()));
+ invalidateGlobalRegion(animView);
+ }
+ mLastY = ev.getY();
+ break;
+
+ case MotionEvent.ACTION_UP:
+ final ObjectAnimator anim;
+ if (animView != null) {
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, 10000);
+ final float velocityX = velocityTracker.getXVelocity();
+ final float velocityY = velocityTracker.getYVelocity();
+ final float curY = animView.getY();
+ final float newY = (velocityY >= 0.0f ? 1 : -1) * animView.getHeight();
+
+ if (Math.abs(velocityY) > Math.abs(velocityX)
+ && Math.abs(velocityY) > ESCAPE_VELOCITY
+ && (velocityY >= 0.0f) == (animView.getY() >= 0)) {
+ final long duration =
+ (long) (Math.abs(newY - curY) * 1000.0f / Math.abs(velocityY));
+ anim = ObjectAnimator.ofFloat(animView, "y", curY, newY);
+ anim.setInterpolator(new LinearInterpolator());
+ final int swipeDirection = animView.getY() >= 0.0f ?
+ RecentsCallback.SWIPE_RIGHT : RecentsCallback.SWIPE_LEFT;
+ anim.addListener(new AnimatorListener() {
+ public void onAnimationStart(Animator animation) {
+ }
+ public void onAnimationRepeat(Animator animation) {
+ }
+ public void onAnimationEnd(Animator animation) {
+ mLinearLayout.removeView(mCurrentView);
+ mCallback.handleSwipe(animView, swipeDirection);
+ }
+ public void onAnimationCancel(Animator animation) {
+ mLinearLayout.removeView(mCurrentView);
+ mCallback.handleSwipe(animView, swipeDirection);
+ }
+ });
+ anim.setDuration(duration);
+ } else { // Animate back to position
+ final long duration = Math.abs(velocityY) > 0.0f ?
+ (long) (Math.abs(newY - curY) * 1000.0f / Math.abs(velocityY))
+ : SNAP_BACK_DURATION;
+ anim = ObjectAnimator.ofFloat(animView, "y", animView.getY(), 0.0f);
+ anim.setInterpolator(new DecelerateInterpolator(2.0f));
+ anim.setDuration(duration);
+ }
+
+ anim.addUpdateListener(new AnimatorUpdateListener() {
+ public void onAnimationUpdate(ValueAnimator animation) {
+ animView.setAlpha(getAlphaForOffset(animView, thumb.getHeight()));
+ invalidateGlobalRegion(animView);
+ }
+ });
+ anim.start();
+ }
+
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ break;
+ }
+ return true;
+ }
+
+ void invalidateGlobalRegion(View view) {
+ RectF childBounds
+ = new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+ childBounds.offset(view.getX(), view.getY());
+ if (DEBUG_INVALIDATE) Log.v(TAG, "-------------");
+ while (view.getParent() != null && view.getParent() instanceof View) {
+ view = (View) view.getParent();
+ view.getMatrix().mapRect(childBounds);
+ view.invalidate((int) Math.floor(childBounds.left),
+ (int) Math.floor(childBounds.top),
+ (int) Math.ceil(childBounds.right),
+ (int) Math.ceil(childBounds.bottom));
+ if (DEBUG_INVALIDATE) {
+ Log.v(TAG, "INVALIDATE(" + (int) Math.floor(childBounds.left)
+ + "," + (int) Math.floor(childBounds.top)
+ + "," + (int) Math.ceil(childBounds.right)
+ + "," + (int) Math.ceil(childBounds.bottom));
+ }
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ LayoutInflater inflater = (LayoutInflater)
+ mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ setScrollbarFadingEnabled(true);
+
+ mLinearLayout = (LinearLayout) findViewById(R.id.recents_linear_layout);
+
+ final int leftPadding = mContext.getResources()
+ .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
+ setOverScrollEffectPadding(leftPadding, 0);
+ }
+
+ private void setOverScrollEffectPadding(int leftPadding, int i) {
+ // TODO Add to RecentsHorizontalScrollView
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ // Keep track of the last visible item in the list so we can restore it
+ // to the bottom when the orientation changes.
+ mLastScrollPosition = scrollPositionOfMostRecent();
+
+ // This has to happen post-layout, so run it "in the future"
+ post(new Runnable() {
+ public void run() {
+ scrollTo(0, mLastScrollPosition);
+ }
+ });
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ // scroll to bottom after reloading
+ if (visibility == View.VISIBLE && changedView == this) {
+ post(new Runnable() {
+ public void run() {
+ update();
+ }
+ });
+ }
+ }
+
+ public void setAdapter(ActvityDescriptionAdapter adapter) {
+ mAdapter = adapter;
+ mAdapter.registerDataSetObserver(new DataSetObserver() {
+ public void onChanged() {
+ update();
+ }
+
+ public void onInvalidated() {
+ update();
+ }
+ });
+ }
+
+ @Override
+ public void setLayoutTransition(LayoutTransition transition) {
+ // The layout transition applies to our embedded LinearLayout
+ mLinearLayout.setLayoutTransition(transition);
+ }
+
+ public void onClick(View view) {
+ mCallback.handleOnClick(view);
+ }
+
+ public void setCallback(RecentsCallback callback) {
+ mCallback = callback;
+ }
+
+ public boolean onTouch(View v, MotionEvent event) {
+ mCurrentView = v;
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsListView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsListView.java
new file mode 100644
index 000000000000..d8b086b6c3cb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsListView.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011 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.systemui.recent;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ListView;
+
+import com.android.systemui.R;
+
+public class RecentsListView extends ListView {
+ private int mLastVisiblePosition;
+ private RecentsCallback mCallback;
+
+ public RecentsListView(Context context) {
+ this(context, null);
+ }
+
+ public RecentsListView(Context context, AttributeSet attrs) {
+ super(context, attrs, 0);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ LayoutInflater inflater = (LayoutInflater)
+ mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ View footer = inflater.inflate(R.layout.status_bar_recent_panel_footer, this, false);
+ setScrollbarFadingEnabled(true);
+ addFooterView(footer, null, false);
+ final int leftPadding = mContext.getResources()
+ .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
+ setOverScrollEffectPadding(leftPadding, 0);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ // Keep track of the last visible item in the list so we can restore it
+ // to the bottom when the orientation changes.
+ final int childCount = getChildCount();
+ if (childCount > 0) {
+ mLastVisiblePosition = getFirstVisiblePosition() + childCount - 1;
+ View view = getChildAt(childCount - 1);
+ final int distanceFromBottom = getHeight() - view.getTop();
+
+ // This has to happen post-layout, so run it "in the future"
+ post(new Runnable() {
+ public void run() {
+ setSelectionFromTop(mLastVisiblePosition, getHeight() - distanceFromBottom);
+ }
+ });
+ }
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ // scroll to bottom after reloading
+ int count = getAdapter().getCount();
+ mLastVisiblePosition = count - 1;
+ if (visibility == View.VISIBLE && changedView == this) {
+ post(new Runnable() {
+ public void run() {
+ setSelection(mLastVisiblePosition);
+ }
+ });
+ }
+ }
+
+ public void setCallback(RecentsCallback callback) {
+ mCallback = callback;
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index d0e6551523e0..e2b3446ef3af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2011 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.
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.tablet;
+package com.android.systemui.recent;
import java.util.ArrayList;
import java.util.List;
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
+import android.animation.LayoutTransition;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
@@ -43,25 +41,25 @@ import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
-import android.widget.CheckBox;
import android.widget.ImageView;
-import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.systemui.R;
+import com.android.systemui.statusbar.tablet.StatusBarPanel;
+import com.android.systemui.statusbar.tablet.TabletStatusBar;
-public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, OnItemClickListener {
+public class RecentsPanelView extends RelativeLayout
+ implements OnItemClickListener, RecentsCallback, StatusBarPanel {
private static final int GLOW_PADDING = 15;
- private static final String TAG = "RecentAppsPanel";
- private static final boolean DEBUG = TabletStatusBar.DEBUG;
+ static final String TAG = "RecentsListView";
+ static final boolean DEBUG = TabletStatusBar.DEBUG;
private static final int DISPLAY_TASKS = 20;
private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps
private TabletStatusBar mBar;
@@ -69,17 +67,15 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
private int mIconDpi;
private View mRecentsScrim;
private View mRecentsGlowView;
- private ListView mRecentsContainer;
- private CheckBox mCompatMode;
+ private View mRecentsContainer;
private Bitmap mGlowBitmap;
private boolean mShowing;
private Choreographer mChoreo;
private View mRecentsDismissButton;
private ActvityDescriptionAdapter mListAdapter;
- protected int mLastVisibleItem;
- static class ActivityDescription {
- int id;
+ /* package */ final static class ActivityDescription {
+ int taskId; // application task id for curating apps
Bitmap thumbnail; // generated by Activity.onCreateThumbnail()
Drawable icon; // application package icon
String label; // application package label
@@ -98,18 +94,18 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
label = _label;
description = _desc;
intent = _intent;
- id = _id;
+ taskId = _id;
position = _pos;
packageName = _packageName;
}
};
/* package */ final static class ViewHolder {
- private ImageView thumbnailView;
- private ImageView iconView;
- private TextView labelView;
- private TextView descriptionView;
- private ActivityDescription activityDescription;
+ ImageView thumbnailView;
+ ImageView iconView;
+ TextView labelView;
+ TextView descriptionView;
+ ActivityDescription activityDescription;
}
/* package */ final class ActvityDescriptionAdapter extends BaseAdapter {
@@ -205,120 +201,15 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
return mShowing;
}
- private static class Choreographer implements Animator.AnimatorListener {
- // should group this into a multi-property animation
- private static final int OPEN_DURATION = 136;
- private static final int CLOSE_DURATION = 250;
-
- boolean mVisible;
- int mPanelHeight;
- View mRootView;
- View mScrimView;
- View mContentView;
- AnimatorSet mContentAnim;
-
- // the panel will start to appear this many px from the end
- final int HYPERSPACE_OFFRAMP = 200;
-
- public Choreographer(View root, View scrim, View content) {
- mRootView = root;
- mScrimView = scrim;
- mContentView = content;
- }
-
- void createAnimation(boolean appearing) {
- float start, end;
-
- if (DEBUG) Log.e(TAG, "createAnimation()", new Exception());
-
- // 0: on-screen
- // height: off-screen
- float y = mContentView.getTranslationY();
- if (appearing) {
- // we want to go from near-the-top to the top, unless we're half-open in the right
- // general vicinity
- start = (y < HYPERSPACE_OFFRAMP) ? y : HYPERSPACE_OFFRAMP;
- end = 0;
- } else {
- start = y;
- end = y + HYPERSPACE_OFFRAMP;
- }
-
- Animator posAnim = ObjectAnimator.ofFloat(mContentView, "translationY",
- start, end);
- posAnim.setInterpolator(appearing
- ? new android.view.animation.DecelerateInterpolator(2.5f)
- : new android.view.animation.AccelerateInterpolator(2.5f));
-
- Animator glowAnim = ObjectAnimator.ofFloat(mContentView, "alpha",
- mContentView.getAlpha(), appearing ? 1.0f : 0.0f);
- glowAnim.setInterpolator(appearing
- ? new android.view.animation.AccelerateInterpolator(1.0f)
- : new android.view.animation.DecelerateInterpolator(1.0f));
-
- Animator bgAnim = ObjectAnimator.ofInt(mScrimView.getBackground(),
- "alpha", appearing ? 0 : 255, appearing ? 255 : 0);
-
- mContentAnim = new AnimatorSet();
- mContentAnim
- .play(bgAnim)
- .with(glowAnim)
- .with(posAnim);
- mContentAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
- mContentAnim.addListener(this);
- }
-
- void startAnimation(boolean appearing) {
- if (DEBUG) Slog.d(TAG, "startAnimation(appearing=" + appearing + ")");
-
- createAnimation(appearing);
-
- mContentView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mContentAnim.start();
-
- mVisible = appearing;
- }
-
- void jumpTo(boolean appearing) {
- mContentView.setTranslationY(appearing ? 0 : mPanelHeight);
- }
-
- public void setPanelHeight(int h) {
- if (DEBUG) Slog.d(TAG, "panelHeight=" + h);
- mPanelHeight = h;
- }
-
- public void onAnimationCancel(Animator animation) {
- if (DEBUG) Slog.d(TAG, "onAnimationCancel");
- // force this to zero so we close the window
- mVisible = false;
- }
-
- public void onAnimationEnd(Animator animation) {
- if (DEBUG) Slog.d(TAG, "onAnimationEnd");
- if (!mVisible) {
- mRootView.setVisibility(View.GONE);
- }
- mContentView.setLayerType(View.LAYER_TYPE_NONE, null);
- mContentAnim = null;
- }
-
- public void onAnimationRepeat(Animator animation) {
- }
-
- public void onAnimationStart(Animator animation) {
- }
- }
-
public void setBar(TabletStatusBar bar) {
mBar = bar;
}
- public RecentAppsPanel(Context context, AttributeSet attrs) {
+ public RecentsPanelView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
- public RecentAppsPanel(Context context, AttributeSet attrs, int defStyle) {
+ public RecentsPanelView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Resources res = context.getResources();
@@ -330,52 +221,34 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
}
@Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- // Keep track of the last visible item in the list so we can restore it
- // to the bottom when the orientation changes.
- int childCount = mRecentsContainer.getChildCount();
- if (childCount > 0) {
- mLastVisibleItem = mRecentsContainer.getFirstVisiblePosition() + childCount - 1;
- View view = mRecentsContainer.getChildAt(childCount - 1);
- final int distanceFromBottom = mRecentsContainer.getHeight() - view.getTop();
- //final int distanceFromBottom = view.getHeight() + BOTTOM_OFFSET;
-
- // This has to happen post-layout, so run it "in the future"
- post(new Runnable() {
- public void run() {
- mRecentsContainer.setSelectionFromTop(mLastVisibleItem,
- mRecentsContainer.getHeight() - distanceFromBottom);
- }
- });
- }
- }
-
- @Override
protected void onFinishInflate() {
super.onFinishInflate();
- LayoutInflater inflater = (LayoutInflater)
mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mRecentsContainer = findViewById(R.id.recents_container);
+ mListAdapter = new ActvityDescriptionAdapter(mContext);
+ if (mRecentsContainer instanceof RecentsListView) {
+ RecentsListView listView = (RecentsListView) mRecentsContainer;
+ listView.setAdapter(mListAdapter);
+ listView.setOnItemClickListener(this);
+ listView.setCallback(this);
+ } else if (mRecentsContainer instanceof RecentsHorizontalScrollView){
+ RecentsHorizontalScrollView scrollView
+ = (RecentsHorizontalScrollView) mRecentsContainer;
+ scrollView.setAdapter(mListAdapter);
+ scrollView.setCallback(this);
+ } else if (mRecentsContainer instanceof RecentsVerticalScrollView){
+ RecentsVerticalScrollView scrollView
+ = (RecentsVerticalScrollView) mRecentsContainer;
+ scrollView.setAdapter(mListAdapter);
+ scrollView.setCallback(this);
+ }
+ else {
+ throw new IllegalArgumentException("missing RecentsListView/RecentsScrollView");
+ }
- mRecentsContainer = (ListView) findViewById(R.id.recents_container);
- mCompatMode = (CheckBox) findViewById(R.id.recents_compat_mode);
- mCompatMode.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- final ActivityManager am = (ActivityManager)
- mContext.getSystemService(Context.ACTIVITY_SERVICE);
- am.setFrontActivityScreenCompatMode(ActivityManager.COMPAT_MODE_TOGGLE);
- hide(true);
- }
- });
- View footer = inflater.inflate(R.layout.status_bar_recent_panel_footer,
- mRecentsContainer, false);
- mRecentsContainer.setScrollbarFadingEnabled(true);
- mRecentsContainer.addFooterView(footer, null, false);
- mRecentsContainer.setAdapter(mListAdapter = new ActvityDescriptionAdapter(mContext));
- mRecentsContainer.setOnItemClickListener(this);
- final int leftPadding = mContext.getResources()
- .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
- mRecentsContainer.setOverScrollEffectPadding(leftPadding, 0);
+ final LayoutTransition transitioner = new LayoutTransition();
+ ((ViewGroup)mRecentsContainer).setLayoutTransition(transitioner);
+ createCustomAnimations(transitioner);
mRecentsGlowView = findViewById(R.id.recents_glow);
mRecentsScrim = (View) findViewById(R.id.recents_bg_protect);
@@ -393,17 +266,16 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
}
}
+ private void createCustomAnimations(LayoutTransition transitioner) {
+ transitioner.setDuration(LayoutTransition.DISAPPEARING, 250);
+ }
+
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + changedView + ", " + visibility + ")");
if (visibility == View.VISIBLE && changedView == this) {
refreshApplicationList();
- post(new Runnable() {
- public void run() {
- mRecentsContainer.setSelection(mActivityDescriptions.size() - 1);
- }
- });
}
}
@@ -496,7 +368,7 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
ActivityDescription desc = null;
for (int i = 0; i < mActivityDescriptions.size(); i++) {
ActivityDescription item = mActivityDescriptions.get(i);
- if (item != null && item.id == id) {
+ if (item != null && item.taskId == id) {
desc = item;
break;
}
@@ -504,34 +376,15 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
return desc;
}
- private void updateShownCompatMode() {
- final ActivityManager am = (ActivityManager)
- mContext.getSystemService(Context.ACTIVITY_SERVICE);
- int mode = am.getFrontActivityScreenCompatMode();
- switch (mode) {
- case ActivityManager.COMPAT_MODE_DISABLED:
- mCompatMode.setVisibility(View.VISIBLE);
- mCompatMode.setChecked(true);
- break;
- case ActivityManager.COMPAT_MODE_ENABLED:
- mCompatMode.setVisibility(View.VISIBLE);
- mCompatMode.setChecked(false);
- break;
- default:
- mCompatMode.setVisibility(View.GONE);
- break;
- }
- }
-
private void refreshApplicationList() {
mActivityDescriptions = getRecentTasks();
mListAdapter.notifyDataSetInvalidated();
if (mActivityDescriptions.size() > 0) {
- mLastVisibleItem = mActivityDescriptions.size() - 1; // scroll to bottom after reloading
+ Log.v(TAG, "Showing " + mActivityDescriptions.size() + " apps");
updateUiElements(getResources().getConfiguration());
- updateShownCompatMode();
} else {
// Immediately hide this panel
+ Log.v(TAG, "Nothing to show");
hide(false);
}
}
@@ -546,6 +399,7 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
paint.setAlpha(255);
final int srcWidth = thumbnail.getWidth();
final int srcHeight = thumbnail.getHeight();
+ Log.v(TAG, "Source thumb: " + srcWidth + "x" + srcHeight);
canvas.drawBitmap(thumbnail,
new Rect(0, 0, srcWidth-1, srcHeight-1),
new RectF(GLOW_PADDING,
@@ -563,27 +417,57 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
mRecentsGlowView.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
}
- private void hide(boolean animate) {
+ public void hide(boolean animate) {
if (!animate) {
setVisibility(View.GONE);
}
- mBar.animateCollapse();
+ if (mBar != null) {
+ mBar.animateCollapse();
+ }
}
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ public void handleOnClick(View view) {
ActivityDescription ad = ((ViewHolder) view.getTag()).activityDescription;
+ final Context context = view.getContext();
final ActivityManager am = (ActivityManager)
- getContext().getSystemService(Context.ACTIVITY_SERVICE);
- if (ad.id >= 0) {
+ context.getSystemService(Context.ACTIVITY_SERVICE);
+ if (ad.taskId >= 0) {
// This is an active task; it should just go to the foreground.
- am.moveTaskToFront(ad.id, ActivityManager.MOVE_TASK_WITH_HOME);
+ am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME);
} else {
Intent intent = ad.intent;
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
| Intent.FLAG_ACTIVITY_TASK_ON_HOME);
if (DEBUG) Log.v(TAG, "Starting activity " + intent);
- getContext().startActivity(intent);
+ context.startActivity(intent);
}
hide(true);
}
+
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ handleOnClick(view);
+ }
+
+ public void handleSwipe(View view, int direction) {
+ ActivityDescription ad = ((ViewHolder) view.getTag()).activityDescription;
+ Log.v(TAG, "Jettison " + ad.label);
+ mActivityDescriptions.remove(ad);
+
+ // Handled by widget containers to enable LayoutTransitions properly
+ // mListAdapter.notifyDataSetChanged();
+
+ if (mActivityDescriptions.size() == 0) {
+ hide(false);
+ }
+
+ // Currently, either direction means the same thing, so ignore direction and remove
+ // the task.
+ final ActivityManager am = (ActivityManager)
+ mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ am.removeTask(ad.taskId, 0);
+ }
+
+ public void handleLongPress(View selectedView) {
+ // TODO show context menu : "Remove from list", "Show properties"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
new file mode 100644
index 000000000000..54ec6b59b133
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2011 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.systemui.recent;
+
+import com.android.systemui.recent.RecentsPanelView.ActvityDescriptionAdapter;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.LayoutTransition;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+
+import com.android.systemui.R;
+
+public class RecentsVerticalScrollView extends ScrollView
+ implements View.OnClickListener, View.OnTouchListener {
+ private static final float FADE_CONSTANT = 0.5f;
+ private static final int SNAP_BACK_DURATION = 250;
+ private static final int ESCAPE_VELOCITY = 100; // speed of item required to "curate" it
+ private static final String TAG = RecentsPanelView.TAG;
+ private static final float THRESHHOLD = 50;
+ private static final boolean DEBUG_INVALIDATE = false;
+ private LinearLayout mLinearLayout;
+ private ActvityDescriptionAdapter mAdapter;
+ private RecentsCallback mCallback;
+ protected int mLastScrollPosition;
+ private View mCurrentView;
+ private float mLastX;
+ private boolean mDragging;
+ private VelocityTracker mVelocityTracker;
+
+ public RecentsVerticalScrollView(Context context) {
+ this(context, null);
+ }
+
+ public RecentsVerticalScrollView(Context context, AttributeSet attrs) {
+ super(context, attrs, 0);
+ }
+
+ private int scrollPositionOfMostRecent() {
+ return mLinearLayout.getHeight() - getHeight();
+ }
+
+ public void update() {
+ mLinearLayout.removeAllViews();
+ for (int i = 0; i < mAdapter.getCount(); i++) {
+ View view = mAdapter.getView(i, null, mLinearLayout);
+ view.setClickable(true);
+ view.setOnClickListener(this);
+ view.setOnTouchListener(this);
+ mLinearLayout.addView(view);
+ }
+ // Scroll to end after layout.
+ post(new Runnable() {
+ public void run() {
+ mLastScrollPosition = scrollPositionOfMostRecent();
+ scrollTo(0, mLastScrollPosition);
+ }
+ });
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mDragging = false;
+ mLastX = ev.getX();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ float delta = ev.getX() - mLastX;
+ Log.v(TAG, "ACTION_MOVE : " + delta);
+ if (Math.abs(delta) > THRESHHOLD) {
+ mDragging = true;
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ mDragging = false;
+ break;
+ }
+ return mDragging ? true : super.onInterceptTouchEvent(ev);
+ }
+
+ private float getAlphaForOffset(View view, float thumbWidth) {
+ final float fadeWidth = FADE_CONSTANT * thumbWidth;
+ float result = 1.0f;
+ if (view.getX() >= thumbWidth) {
+ result = 1.0f - (view.getX() - thumbWidth) / fadeWidth;
+ } else if (view.getX() < 0.0f) {
+ result = 1.0f + (thumbWidth + view.getX()) / fadeWidth;
+ }
+ Log.v(TAG, "FADE AMOUNT: " + result);
+ return result;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (!mDragging) {
+ return super.onTouchEvent(ev);
+ }
+
+ mVelocityTracker.addMovement(ev);
+
+ final View animView = mCurrentView;
+ // TODO: Cache thumbnail
+ final View thumb = animView.findViewById(R.id.app_thumbnail);
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_MOVE:
+ if (animView != null) {
+ final float delta = ev.getX() - mLastX;
+ animView.setX(animView.getX() + delta);
+ animView.setAlpha(getAlphaForOffset(animView, thumb.getWidth()));
+ invalidateGlobalRegion(animView);
+ }
+ mLastX = ev.getX();
+ break;
+
+ case MotionEvent.ACTION_UP:
+ final ObjectAnimator anim;
+ if (animView != null) {
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, 10000);
+ final float velocityX = velocityTracker.getXVelocity();
+ final float velocityY = velocityTracker.getYVelocity();
+ final float curX = animView.getX();
+ final float newX = (velocityX >= 0.0f ? 1 : -1) * animView.getWidth();
+
+ if (Math.abs(velocityX) > Math.abs(velocityY)
+ && Math.abs(velocityX) > ESCAPE_VELOCITY
+ && (velocityX > 0.0f) == (animView.getX() >= 0)) {
+ final long duration =
+ (long) (Math.abs(newX-curX) * 1000.0f / Math.abs(velocityX));
+ anim = ObjectAnimator.ofFloat(animView, "x", curX, newX);
+ anim.setInterpolator(new LinearInterpolator());
+ final int swipeDirection = animView.getX() >= 0.0f ?
+ RecentsCallback.SWIPE_RIGHT : RecentsCallback.SWIPE_LEFT;
+ anim.addListener(new AnimatorListener() {
+ public void onAnimationStart(Animator animation) {
+ }
+ public void onAnimationRepeat(Animator animation) {
+ }
+ public void onAnimationEnd(Animator animation) {
+ mLinearLayout.removeView(mCurrentView);
+ mCallback.handleSwipe(animView, swipeDirection);
+ }
+ public void onAnimationCancel(Animator animation) {
+ mLinearLayout.removeView(mCurrentView);
+ mCallback.handleSwipe(animView, swipeDirection);
+ }
+ });
+ anim.setDuration(duration);
+ } else { // Animate back to position
+ final long duration = Math.abs(velocityX) > 0.0f ?
+ (long) (Math.abs(newX-curX) * 1000.0f / Math.abs(velocityX))
+ : SNAP_BACK_DURATION;
+ anim = ObjectAnimator.ofFloat(animView, "x", animView.getX(), 0.0f);
+ anim.setInterpolator(new DecelerateInterpolator(4.0f));
+ anim.setDuration(duration);
+ }
+
+ anim.addUpdateListener(new AnimatorUpdateListener() {
+ public void onAnimationUpdate(ValueAnimator animation) {
+ animView.setAlpha(getAlphaForOffset(animView, thumb.getWidth()));
+ invalidateGlobalRegion(animView);
+ }
+ });
+ anim.start();
+ }
+
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ break;
+ }
+ return true;
+ }
+
+ void invalidateGlobalRegion(View view) {
+ RectF childBounds
+ = new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+ childBounds.offset(view.getX(), view.getY());
+ if (DEBUG_INVALIDATE) Log.v(TAG, "-------------");
+ while (view.getParent() != null && view.getParent() instanceof View) {
+ view = (View) view.getParent();
+ view.getMatrix().mapRect(childBounds);
+ view.invalidate((int) Math.floor(childBounds.left),
+ (int) Math.floor(childBounds.top),
+ (int) Math.ceil(childBounds.right),
+ (int) Math.ceil(childBounds.bottom));
+ if (DEBUG_INVALIDATE) {
+ Log.v(TAG, "INVALIDATE(" + (int) Math.floor(childBounds.left)
+ + "," + (int) Math.floor(childBounds.top)
+ + "," + (int) Math.ceil(childBounds.right)
+ + "," + (int) Math.ceil(childBounds.bottom));
+ }
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ LayoutInflater inflater = (LayoutInflater)
+ mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ setScrollbarFadingEnabled(true);
+
+ mLinearLayout = (LinearLayout) findViewById(R.id.recents_linear_layout);
+
+ final int leftPadding = mContext.getResources()
+ .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
+ setOverScrollEffectPadding(leftPadding, 0);
+ }
+
+ private void setOverScrollEffectPadding(int leftPadding, int i) {
+ // TODO Add to (Vertical)ScrollView
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ // Keep track of the last visible item in the list so we can restore it
+ // to the bottom when the orientation changes.
+ mLastScrollPosition = scrollPositionOfMostRecent();
+
+ // This has to happen post-layout, so run it "in the future"
+ post(new Runnable() {
+ public void run() {
+ scrollTo(0, mLastScrollPosition);
+ }
+ });
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ // scroll to bottom after reloading
+ if (visibility == View.VISIBLE && changedView == this) {
+ post(new Runnable() {
+ public void run() {
+ update();
+ }
+ });
+ }
+ }
+
+ public void setAdapter(ActvityDescriptionAdapter adapter) {
+ mAdapter = adapter;
+ mAdapter.registerDataSetObserver(new DataSetObserver() {
+ public void onChanged() {
+ update();
+ }
+
+ public void onInvalidated() {
+ update();
+ }
+ });
+ }
+
+ @Override
+ public void setLayoutTransition(LayoutTransition transition) {
+ // The layout transition applies to our embedded LinearLayout
+ mLinearLayout.setLayoutTransition(transition);
+ }
+
+ public void onClick(View view) {
+ mCallback.handleOnClick(view);
+ }
+
+ public void setCallback(RecentsCallback callback) {
+ mCallback = callback;
+ }
+
+ public boolean onTouch(View v, MotionEvent event) {
+ mCurrentView = v;
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/carousel/RecentApplicationsActivity.java
index 7d4629e89357..45e230f493d8 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/carousel/RecentApplicationsActivity.java
@@ -15,7 +15,7 @@
*/
-package com.android.systemui.recent;
+package com.android.systemui.recent.carousel;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsCarouselView.java b/packages/SystemUI/src/com/android/systemui/recent/carousel/RecentApplicationsCarouselView.java
index 1c8ec95cf4ca..1afb08663362 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsCarouselView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/carousel/RecentApplicationsCarouselView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recent;
+package com.android.systemui.recent.carousel;
import android.content.Context;
import android.util.AttributeSet;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index f74fcc6db899..91c3cc18dadf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -71,7 +71,8 @@ import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.Prefs;
-import com.android.systemui.recent.RecentApplicationsActivity;
+import com.android.systemui.recent.RecentsPanelView;
+import com.android.systemui.recent.carousel.RecentApplicationsActivity;
public class TabletStatusBar extends StatusBar implements
HeightReceiver.OnBarHeightChangedListener,
@@ -162,7 +163,7 @@ public class TabletStatusBar extends StatusBar implements
// for disabling the status bar
int mDisabled = 0;
- private RecentAppsPanel mRecentsPanel;
+ private RecentsPanelView mRecentsPanel;
private InputMethodsPanel mInputMethodsPanel;
public Context getContext() { return mContext; }
@@ -253,7 +254,7 @@ public class TabletStatusBar extends StatusBar implements
WindowManagerImpl.getDefault().addView(mNotificationPeekWindow, lp);
// Recents Panel
- mRecentsPanel = (RecentAppsPanel) View.inflate(context,
+ mRecentsPanel = (RecentsPanelView) View.inflate(context,
R.layout.status_bar_recent_panel, null);
mRecentsPanel.setVisibility(View.GONE);
mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL,
@@ -1195,7 +1196,7 @@ public class TabletStatusBar extends StatusBar implements
if (mVT != null) {
if (action == MotionEvent.ACTION_UP
// was this a sloppy tap?
- && Math.abs(event.getX() - mInitialTouchX) < mTouchSlop
+ && Math.abs(event.getX() - mInitialTouchX) < mTouchSlop
&& Math.abs(event.getY() - mInitialTouchY) < (mTouchSlop / 3)
// dragging off the bottom doesn't count
&& (int)event.getY() < v.getBottom()) {
@@ -1298,7 +1299,7 @@ public class TabletStatusBar extends StatusBar implements
if (!peeking) {
if (action == MotionEvent.ACTION_UP
// was this a sloppy tap?
- && Math.abs(event.getX() - mInitialTouchX) < mTouchSlop
+ && Math.abs(event.getX() - mInitialTouchX) < mTouchSlop
&& Math.abs(event.getY() - mInitialTouchY) < (mTouchSlop / 3)
// dragging off the bottom doesn't count
&& (int)event.getY() < v.getBottom()) {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index db831c79b29e..ca2adb46612f 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -26,6 +26,7 @@ import android.net.ConnectivityManager;
import android.net.DummyDataStateTracker;
import android.net.EthernetDataTracker;
import android.net.IConnectivityManager;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MobileDataStateTracker;
import android.net.NetworkConfig;
@@ -61,6 +62,7 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
+import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
@@ -125,6 +127,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private AtomicBoolean mBackgroundDataEnabled = new AtomicBoolean(true);
+ private INetworkManagementService mNetd;
+
private static final int ENABLED = 1;
private static final int DISABLED = 0;
@@ -933,10 +937,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* @return {@code true} on success, {@code false} on failure
*/
private boolean addHostRoute(NetworkStateTracker nt, InetAddress hostAddress, int cycleCount) {
- if (nt.getNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI) {
- return false;
- }
-
LinkProperties lp = nt.getLinkProperties();
if ((lp == null) || (hostAddress == null)) return false;
@@ -951,20 +951,28 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), hostAddress);
- InetAddress gateway = null;
+ InetAddress gatewayAddress = null;
if (bestRoute != null) {
- gateway = bestRoute.getGateway();
+ gatewayAddress = bestRoute.getGateway();
// if the best route is ourself, don't relf-reference, just add the host route
- if (hostAddress.equals(gateway)) gateway = null;
+ if (hostAddress.equals(gatewayAddress)) gatewayAddress = null;
}
- if (gateway != null) {
+ if (gatewayAddress != null) {
if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) {
loge("Error adding hostroute - too much recursion");
return false;
}
- if (!addHostRoute(nt, gateway, cycleCount+1)) return false;
+ if (!addHostRoute(nt, gatewayAddress, cycleCount+1)) return false;
+ }
+
+ RouteInfo route = RouteInfo.makeHostRoute(hostAddress, gatewayAddress);
+
+ try {
+ mNetd.addRoute(interfaceName, route);
+ return true;
+ } catch (Exception ex) {
+ return false;
}
- return NetworkUtils.addHostRoute(interfaceName, hostAddress, gateway);
}
// TODO support the removal of single host routes. Keep a ref count of them so we
@@ -1291,6 +1299,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
void systemReady() {
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ mNetd = INetworkManagementService.Stub.asInterface(b);
+
synchronized(this) {
mSystemReady = true;
if (mInitialBroadcast != null) {
@@ -1399,6 +1410,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
addPrivateDnsRoutes(mNetTrackers[netType]);
}
+
+ /** Notify TetheringService if interface name has been changed. */
+ if (TextUtils.equals(mNetTrackers[netType].getNetworkInfo().getReason(),
+ Phone.REASON_LINK_PROPERTIES_CHANGED)) {
+ handleTetherIfaceChange(netType);
+ }
} else {
if (mNetConfigs[netType].isDefault()) {
removeDefaultRoute(mNetTrackers[netType]);
@@ -1421,7 +1438,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (interfaceName != null && !privateDnsRouteSet) {
Collection<InetAddress> dnsList = p.getDnses();
for (InetAddress dns : dnsList) {
- if (DBG) log(" adding " + dns);
addHostRoute(nt, dns, 0);
}
nt.privateDnsRouteSet(true);
@@ -1429,8 +1445,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
private void removePrivateDnsRoutes(NetworkStateTracker nt) {
- // TODO - we should do this explicitly but the NetUtils api doesnt
- // support this yet - must remove all. No worse than before
LinkProperties p = nt.getLinkProperties();
if (p == null) return;
String interfaceName = p.getInterfaceName();
@@ -1440,7 +1454,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
log("removePrivateDnsRoutes for " + nt.getNetworkInfo().getTypeName() +
" (" + interfaceName + ")");
}
- NetworkUtils.removeHostRoutes(interfaceName);
+
+ Collection<InetAddress> dnsList = p.getDnses();
+ for (InetAddress dns : dnsList) {
+ if (DBG) log(" removing " + dns);
+ RouteInfo route = RouteInfo.makeHostRoute(dns);
+ try {
+ mNetd.removeRoute(interfaceName, route);
+ } catch (Exception ex) {
+ loge("error (" + ex + ") removing dns route " + route);
+ }
+ }
nt.privateDnsRouteSet(false);
}
}
@@ -1451,19 +1475,27 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (p == null) return;
String interfaceName = p.getInterfaceName();
if (TextUtils.isEmpty(interfaceName)) return;
- for (RouteInfo route : p.getRoutes()) {
+ for (RouteInfo route : p.getRoutes()) {
//TODO - handle non-default routes
if (route.isDefaultRoute()) {
+ if (DBG) log("adding default route " + route);
InetAddress gateway = route.getGateway();
- if (addHostRoute(nt, gateway, 0) &&
- NetworkUtils.addDefaultRoute(interfaceName, gateway)) {
+ if (addHostRoute(nt, gateway, 0)) {
+ try {
+ mNetd.addRoute(interfaceName, route);
+ } catch (Exception e) {
+ loge("error adding default route " + route);
+ continue;
+ }
if (DBG) {
NetworkInfo networkInfo = nt.getNetworkInfo();
log("addDefaultRoute for " + networkInfo.getTypeName() +
" (" + interfaceName + "), GatewayAddr=" +
gateway.getHostAddress());
}
+ } else {
+ loge("error adding host route for default route " + route);
}
}
}
@@ -1475,8 +1507,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (p == null) return;
String interfaceName = p.getInterfaceName();
- if (interfaceName != null) {
- if (NetworkUtils.removeDefaultRoute(interfaceName) >= 0) {
+ if (interfaceName == null) return;
+
+ for (RouteInfo route : p.getRoutes()) {
+ //TODO - handle non-default routes
+ if (route.isDefaultRoute()) {
+ try {
+ mNetd.removeRoute(interfaceName, route);
+ } catch (Exception ex) {
+ loge("error (" + ex + ") removing default route " + route);
+ continue;
+ }
if (DBG) {
NetworkInfo networkInfo = nt.getNetworkInfo();
log("removeDefaultRoute for " + networkInfo.getTypeName() + " (" +
@@ -2211,6 +2252,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
+ private void handleTetherIfaceChange(int type) {
+ String iface = mNetTrackers[type].getLinkProperties().getInterfaceName();
+
+ if (isTetheringSupported()) {
+ mTethering.handleTetherIfaceChange(iface);
+ }
+ }
+
private void log(String s) {
Slog.d(TAG, s);
}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index d931350047ce..7c613c165b48 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -29,6 +29,7 @@ import android.net.InterfaceConfiguration;
import android.net.INetworkManagementEventObserver;
import android.net.LinkAddress;
import android.net.NetworkUtils;
+import android.net.RouteInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.os.INetworkManagementService;
@@ -46,6 +47,8 @@ import android.provider.Settings;
import android.content.ContentResolver;
import android.database.ContentObserver;
+import java.io.BufferedReader;
+import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
@@ -55,8 +58,8 @@ import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.lang.IllegalStateException;
-
import java.net.InetAddress;
+import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.concurrent.CountDownLatch;
@@ -71,6 +74,9 @@ class NetworkManagementService extends INetworkManagementService.Stub {
private static final boolean DBG = false;
private static final String NETD_TAG = "NetdConnector";
+ private static final int ADD = 1;
+ private static final int REMOVE = 2;
+
class NetdResponseCode {
public static final int InterfaceListResult = 110;
public static final int TetherInterfaceListResult = 111;
@@ -320,6 +326,164 @@ class NetworkManagementService extends INetworkManagementService.Stub {
}
}
+ public void addRoute(String interfaceName, RouteInfo route) {
+ modifyRoute(interfaceName, ADD, route);
+ }
+
+ public void removeRoute(String interfaceName, RouteInfo route) {
+ modifyRoute(interfaceName, REMOVE, route);
+ }
+
+ private void modifyRoute(String interfaceName, int action, RouteInfo route) {
+ ArrayList<String> rsp;
+
+ StringBuilder cmd;
+
+ switch (action) {
+ case ADD:
+ {
+ cmd = new StringBuilder("interface route add " + interfaceName);
+ break;
+ }
+ case REMOVE:
+ {
+ cmd = new StringBuilder("interface route remove " + interfaceName);
+ break;
+ }
+ default:
+ throw new IllegalStateException("Unknown action type " + action);
+ }
+
+ // create triplet: dest-ip-addr prefixlength gateway-ip-addr
+ LinkAddress la = route.getDestination();
+ cmd.append(' ');
+ cmd.append(la.getAddress().getHostAddress());
+ cmd.append(' ');
+ cmd.append(la.getNetworkPrefixLength());
+ cmd.append(' ');
+ if (route.getGateway() == null) {
+ if (la.getAddress() instanceof Inet4Address) {
+ cmd.append("0.0.0.0");
+ } else {
+ cmd.append ("::0");
+ }
+ } else {
+ cmd.append(route.getGateway().getHostAddress());
+ }
+ try {
+ rsp = mConnector.doCommand(cmd.toString());
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate with native dameon to add routes - "
+ + e);
+ }
+
+ for (String line : rsp) {
+ Log.v(TAG, "add route response is " + line);
+ }
+ }
+
+ private ArrayList<String> readRouteList(String filename) {
+ FileInputStream fstream = null;
+ ArrayList<String> list = new ArrayList<String>();
+
+ try {
+ fstream = new FileInputStream(filename);
+ DataInputStream in = new DataInputStream(fstream);
+ BufferedReader br = new BufferedReader(new InputStreamReader(in));
+ String s;
+
+ // throw away the title line
+
+ while (((s = br.readLine()) != null) && (s.length() != 0)) {
+ list.add(s);
+ }
+ } catch (IOException ex) {
+ // return current list, possibly empty
+ } finally {
+ if (fstream != null) {
+ try {
+ fstream.close();
+ } catch (IOException ex) {}
+ }
+ }
+
+ return list;
+ }
+
+ public RouteInfo[] getRoutes(String interfaceName) {
+ ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
+
+ // v4 routes listed as:
+ // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT
+ for (String s : readRouteList("/proc/net/route")) {
+ String[] fields = s.split("\t");
+
+ if (fields.length > 7) {
+ String iface = fields[0];
+
+ if (interfaceName.equals(iface)) {
+ String dest = fields[1];
+ String gate = fields[2];
+ String flags = fields[3]; // future use?
+ String mask = fields[7];
+ try {
+ // address stored as a hex string, ex: 0014A8C0
+ InetAddress destAddr =
+ NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16));
+ int prefixLength =
+ NetworkUtils.netmaskIntToPrefixLength(
+ (int)Long.parseLong(mask, 16));
+ LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
+
+ // address stored as a hex string, ex 0014A8C0
+ InetAddress gatewayAddr =
+ NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16));
+
+ RouteInfo route = new RouteInfo(linkAddress, gatewayAddr);
+ routes.add(route);
+ } catch (Exception e) {
+ Log.e(TAG, "Error parsing route " + s + " : " + e);
+ continue;
+ }
+ }
+ }
+ }
+
+ // v6 routes listed as:
+ // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface
+ for (String s : readRouteList("/proc/net/ipv6_route")) {
+ String[]fields = s.split("\\s+");
+ if (fields.length > 9) {
+ String iface = fields[9].trim();
+ if (interfaceName.equals(iface)) {
+ String dest = fields[0];
+ String prefix = fields[1];
+ String gate = fields[4];
+
+ try {
+ // prefix length stored as a hex string, ex 40
+ int prefixLength = Integer.parseInt(prefix, 16);
+
+ // address stored as a 32 char hex string
+ // ex fe800000000000000000000000000000
+ InetAddress destAddr = NetworkUtils.hexToInet6Address(dest);
+ LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
+
+ InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate);
+
+ RouteInfo route = new RouteInfo(linkAddress, gateAddr);
+ routes.add(route);
+ } catch (Exception e) {
+ Log.e(TAG, "Error parsing route " + s + " : " + e);
+ continue;
+ }
+ }
+ }
+ }
+ return (RouteInfo[]) routes.toArray(new RouteInfo[0]);
+ }
+
public void shutdown() {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SHUTDOWN)
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 316db2e73569..e8155b480444 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -636,6 +636,16 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
return retVal;
}
+ public void handleTetherIfaceChange(String iface) {
+ // check if iface is white listed
+ for (String regex : mUpstreamIfaceRegexs) {
+ if (iface.matches(regex)) {
+ if (DEBUG) Log.d(TAG, "Tethering got Interface Change");
+ mTetherMasterSM.sendMessage(TetherMasterSM.CMD_IFACE_CHANGED, iface);
+ break;
+ }
+ }
+ }
class TetherInterfaceSM extends StateMachine {
// notification from the master SM that it's not in tether mode
@@ -1034,6 +1044,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
static final int CMD_CELL_CONNECTION_RENEW = 4;
// we don't have a valid upstream conn, check again after a delay
static final int CMD_RETRY_UPSTREAM = 5;
+ // received an indication that upstream interface has changed
+ static final int CMD_IFACE_CHANGED = 6;
// This indicates what a timeout event relates to. A state that
// sends itself a delayed timeout event and handles incoming timeout events
@@ -1377,13 +1389,18 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
turnOnMobileConnection();
}
break;
- case CMD_RETRY_UPSTREAM:
- chooseUpstreamType(mTryCell);
- mTryCell = !mTryCell;
- break;
- default:
- retValue = false;
- break;
+ case CMD_RETRY_UPSTREAM:
+ chooseUpstreamType(mTryCell);
+ mTryCell = !mTryCell;
+ break;
+ case CMD_IFACE_CHANGED:
+ String iface = (String)message.obj;
+ if (DEBUG) Log.d(TAG, "Activie upstream interface changed: " + iface);
+ notifyTetheredOfNewUpstreamIface(iface);
+ break;
+ default:
+ retValue = false;
+ break;
}
return retValue;
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 0b3f613bcda3..9890bec61647 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -6502,8 +6502,22 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (mBaseDisplayWidth < mInitialDisplayWidth
|| mBaseDisplayHeight < mInitialDisplayHeight) {
- Rect outer = new Rect(0, 0, mInitialDisplayWidth, mInitialDisplayHeight);
- Rect inner = new Rect(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
+ int initW, initH, baseW, baseH;
+ final boolean rotated = (mRotation == Surface.ROTATION_90
+ || mRotation == Surface.ROTATION_270);
+ if (rotated) {
+ initW = mInitialDisplayHeight;
+ initH = mInitialDisplayWidth;
+ baseW = mBaseDisplayHeight;
+ baseH = mBaseDisplayWidth;
+ } else {
+ initW = mInitialDisplayWidth;
+ initH = mInitialDisplayHeight;
+ baseW = mBaseDisplayWidth;
+ baseH = mBaseDisplayHeight;
+ }
+ Rect outer = new Rect(0, 0, initW, initH);
+ Rect inner = new Rect(0, 0, baseW, baseH);
try {
mBlackFrame = new BlackFrame(mFxSession, outer, inner, MASK_LAYER);
} catch (Surface.OutOfResourcesException e) {
diff --git a/telephony/java/com/android/internal/telephony/ApnContext.java b/telephony/java/com/android/internal/telephony/ApnContext.java
index ce1a3b6ed9b9..496c43c5cd55 100644
--- a/telephony/java/com/android/internal/telephony/ApnContext.java
+++ b/telephony/java/com/android/internal/telephony/ApnContext.java
@@ -85,12 +85,10 @@ public class ApnContext {
public synchronized DataConnectionAc getDataConnectionAc() {
- log("getDataConnectionAc dcac=" + mDataConnectionAc);
return mDataConnectionAc;
}
public synchronized void setDataConnectionAc(DataConnectionAc dcac) {
- log("setDataConnectionAc dcac=" + dcac);
mDataConnectionAc = dcac;
}
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 0188cf176f05..2e781b2a3d8d 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -509,24 +509,56 @@ public abstract class DataConnection extends StateMachine {
result.mFailCause = FailCause.fromInt(response.status);
} else {
log("onSetupConnectionCompleted received DataCallState: " + response);
-
- // Check if system property dns usable
- boolean okToUseSystemPropertyDns = false;
cid = response.cid;
- String propertyPrefix = "net." + response.ifname + ".";
- String dnsServers[] = new String[2];
- dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
- dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
- okToUseSystemPropertyDns = isDnsOk(dnsServers);
-
// set link properties based on data call response
- result = response.setLinkProperties(mLinkProperties,
- okToUseSystemPropertyDns);
+ result = setLinkProperties(response, mLinkProperties);
}
return result;
}
+ private DataCallState.SetupResult setLinkProperties(DataCallState response,
+ LinkProperties lp) {
+ // Check if system property dns usable
+ boolean okToUseSystemPropertyDns = false;
+ String propertyPrefix = "net." + response.ifname + ".";
+ String dnsServers[] = new String[2];
+ dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
+ dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
+ okToUseSystemPropertyDns = isDnsOk(dnsServers);
+
+ // set link properties based on data call response
+ return response.setLinkProperties(lp, okToUseSystemPropertyDns);
+ }
+
+ private boolean updateLinkProperty(DataCallState newState) {
+ boolean changed = false;
+
+ if (newState == null) return changed;
+
+ DataCallState.SetupResult result;
+ LinkProperties newLp = new LinkProperties();
+
+ // set link properties based on data call response
+ result = setLinkProperties(newState, newLp);
+ if (result != DataCallState.SetupResult.SUCCESS) {
+ if (DBG) log("UpdateLinkProperty failed : " + result);
+ return changed;
+ }
+ // copy HTTP proxy as it is not part DataCallState.
+ newLp.setHttpProxy(mLinkProperties.getHttpProxy());
+
+ if (DBG) log("old LP=" + mLinkProperties);
+ if (DBG) log("new LP=" + newLp);
+
+ if (mLinkProperties == null || !mLinkProperties.equals(newLp)) {
+ mLinkProperties = newLp;
+ changed = true;
+ }
+
+ return changed;
+ }
+
/**
* The parent state for all other states.
*/
@@ -597,17 +629,25 @@ public abstract class DataConnection extends StateMachine {
mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_LINK_PROPERTIES_HTTP_PROXY);
break;
}
+ case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: {
+ DataCallState newState = (DataCallState) msg.obj;
+ int updated = updateLinkProperty(newState) ? 1 : 0;
+ if (DBG) {
+ log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE updated="
+ + (updated == 1)
+ + " newState=" + newState);
+ }
+ mAc.replyToMessage(msg,
+ DataConnectionAc.RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE,
+ updated);
+ break;
+ }
case DataConnectionAc.REQ_GET_LINK_CAPABILITIES: {
LinkCapabilities lc = new LinkCapabilities(mCapabilities);
log("REQ_GET_LINK_CAPABILITIES linkCapabilities" + lc);
mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_CAPABILITIES, lc);
break;
}
- case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: {
- Bundle data = msg.getData();
- mLinkProperties = (LinkProperties) data.get("linkProperties");
- break;
- }
case DataConnectionAc.REQ_RESET:
if (DBG) log("DcDefaultState: msg.what=REQ_RESET");
clearSettings();
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionAc.java b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
index a9796dde26ab..2ab6184409b9 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionAc.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
@@ -226,6 +226,38 @@ public class DataConnectionAc extends AsyncChannel {
}
/**
+ * Request update LinkProperties from DataCallState
+ * Response {@link #rspUpdateLinkPropertiesDataCallState}
+ */
+ public void reqUpdateLinkPropertiesDataCallState(DataCallState newState) {
+ sendMessage(REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, newState);
+ if (DBG) log("reqUpdateLinkPropertiesDataCallState");
+ }
+
+ public boolean rspUpdateLinkPropertiesDataCallState(Message response) {
+ boolean retVal = response.arg1 == 1;
+ if (DBG) log("rspUpdateLinkPropertiesState=" + retVal);
+ return retVal;
+ }
+
+ /**
+ * Update link properties in the data connection
+ *
+ * @return true if link property has been updated. false otherwise.
+ */
+ public boolean updateLinkPropertiesDataCallStateSync(DataCallState newState) {
+ Message response =
+ sendMessageSynchronously(REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, newState);
+ if ((response != null) &&
+ (response.what == RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE)) {
+ return rspUpdateLinkPropertiesDataCallState(response);
+ } else {
+ log("getLinkProperties error response=" + response);
+ return false;
+ }
+ }
+
+ /**
* Request the connections LinkCapabilities.
* Response {@link #rspLinkCapabilities}
*/
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index ddf066eefe53..48c5318c2943 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -178,6 +178,7 @@ public interface Phone {
static final String REASON_NW_TYPE_CHANGED = "nwTypeChanged";
static final String REASON_DATA_DEPENDENCY_MET = "dependencyMet";
static final String REASON_DATA_DEPENDENCY_UNMET = "dependencyUnmet";
+ static final String REASON_LINK_PROPERTIES_CHANGED = "linkPropertiesChanged";
// Used for band mode selection methods
static final int BM_UNSPECIFIED = 0; // selected by baseband automatically
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index e61af318d752..6b4054a9bd4e 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -970,23 +970,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
return true;
}
- private boolean dataCallStatesHasCID (ArrayList<DataCallState> states, int cid) {
- for (int i = 0, s = states.size() ; i < s ; i++) {
- if (states.get(i).cid == cid) return true;
- }
- return false;
- }
-
- private boolean dataCallStatesHasActiveCID (ArrayList<DataCallState> states, int cid) {
- for (int i = 0, s = states.size() ; i < s ; i++) {
- if ((states.get(i).cid == cid) && (states.get(i).active != 0)) {
- return true;
- }
- }
-
- return false;
- }
-
/**
* Handles changes to the APN database.
*/
@@ -1014,15 +997,13 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
}
/**
- * @param explicitPoll if true, indicates that *we* polled for this
- * update while state == CONNECTED rather than having it delivered
- * via an unsolicited response (which could have happened at any
- * previous state
+ * @param ar is the result of RIL_REQUEST_DATA_CALL_LIST
+ * or RIL_UNSOL_DATA_CALL_LIST_CHANGED
*/
private void onDataStateChanged (AsyncResult ar) {
ArrayList<DataCallState> dataCallStates;
- if (DBG) log("onDataStateChanged(ar) E");
+ if (DBG) log("onDataStateChanged(ar): E");
dataCallStates = (ArrayList<DataCallState>)(ar.result);
if (ar.exception != null) {
@@ -1033,65 +1014,79 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
return;
}
- for (ApnContext apnContext : mApnContexts.values()) {
- onDataStateChanged(dataCallStates, apnContext);
- }
- if (DBG) log("onDataStateChanged(ar) X");
- }
-
- private void onDataStateChanged (ArrayList<DataCallState> dataCallStates,
- ApnContext apnContext) {
- if (DBG) log("onDataStateChanged(dataCallState, apnContext): apnContext=" + apnContext);
-
- if (apnContext == null) {
- // Should not happen
- if (DBG) log("onDataStateChanged(dataCallState, apnContext): ignore apnContext=null");
- return;
+ // Create a hash map to store the dataCallState of each call id
+ // TODO: Depends on how frequent the DATA_CALL_LIST got updated,
+ // may cache response to reduce comparison.
+ HashMap<Integer, DataCallState> response;
+ response = new HashMap<Integer, DataCallState>();
+ if (DBG) log("onDataStateChanged(ar): DataCallState size=" + dataCallStates.size());
+ for (DataCallState dc : dataCallStates) {
+ response.put(dc.cid, dc);
+ if (DBG) log("onDataStateChanged(ar): " + dc.cid + ": " + dc.toString());
}
- if (apnContext.getState() == State.CONNECTED) {
- // The way things are supposed to work, the PDP list
- // should not contain the CID after it disconnects.
- // However, the way things really work, sometimes the PDP
- // context is still listed with active = false, which
- // makes it hard to distinguish an activating context from
- // an activated-and-then deactivated one.
+ // For each connected apn, check if there is a matched active
+ // data call state, which has the same link properties.
+ if (DBG) log(" ApnContext size=" + mApnContexts.values().size());
+ for (ApnContext apnContext : mApnContexts.values()) {
+ if (DBG){
+ log("onDataStateChanged(ar): " + apnContext.toString());
+ if (apnContext.getDataConnection() != null) {
+ log("onDataStateChanged(ar): " + apnContext.getDataConnection().toString());
+ }
+ }
DataConnectionAc dcac = apnContext.getDataConnectionAc();
if (dcac == null) {
- if (DBG) log("onDataStateChanged(dataCallState, apnContext): dcac==null BAD NEWS");
- return;
+ continue;
}
- int cid = dcac.getCidSync();
- if (!dataCallStatesHasCID(dataCallStates, cid)) {
- // It looks like the PDP context has deactivated.
- // Tear everything down and try to reconnect.
+ int connectionId = dcac.getCidSync();
- if (DBG) {
- log("onDataStateChanged(dataCallStates,apnContext) " +
- "PDP connection has dropped. Reconnecting");
+ if (apnContext.getState() == State.CONNECTED) {
+ // The way things are supposed to work, the PDP list
+ // should not contain the CID after it disconnects.
+ // However, the way things really work, sometimes the PDP
+ // context is still listed with active = false, which
+ // makes it hard to distinguish an activating context from
+ // an activated-and-then de-activated one.
+ if (response.containsKey(connectionId)) {
+ DataCallState newState = response.get(connectionId);
+ if (DBG) log("onDataStateChanged(ar): Found ConnId=" + connectionId
+ + " newState=" + newState.toString());
+ if (newState.active != 0) {
+ boolean changed
+ = dcac.updateLinkPropertiesDataCallStateSync(newState);
+ if (changed) {
+ if (DBG) log("onDataStateChanged(ar): Found and changed, notify");
+ mPhone.notifyDataConnection(Phone.REASON_LINK_PROPERTIES_CHANGED,
+ apnContext.getApnType());
+ // Temporary hack, if false we'll reset connections and at this
+ // time a transition from CDMA -> Global fails. The DEACTIVATE
+ // fails with a GENERIC_FAILURE and the VZWINTERNET connection is
+ // never setup. @see bug/
+ if (SystemProperties.getBoolean("telephony.ignore-state-changes",
+ true)) {
+ log("onDataStateChanged(ar): STOPSHIP don't reset, continue");
+ continue;
+ }
+ } else {
+ if (DBG) log("onDataStateChanged(ar): Found but no change, skip");
+ continue;
+ }
+ }
}
- // Add an event log when the network drops PDP
- int cellLocationId = getCellLocationId();
- EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cellLocationId,
- TelephonyManager.getDefault().getNetworkType());
- cleanUpConnection(true, apnContext);
- } else if (!dataCallStatesHasActiveCID(dataCallStates,
- apnContext.getDataConnectionAc().getCidSync())) {
+ if (DBG) log("onDataStateChanged(ar): reset connection.");
- if (DBG) {
- log("onDataStateChanged(dataCallStates,apnContext) " +
- "PDP connection has dropped (active=false case). Reconnecting");
- }
-
- // Log the network drop on the event log.
- int cellLocationId = getCellLocationId();
- EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cellLocationId,
+ // Add an event log when the network drops PDP
+ int cid = getCellLocationId();
+ EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid,
TelephonyManager.getDefault().getNetworkType());
cleanUpConnection(true, apnContext);
}
}
+
+ if (DBG) log("onDataStateChanged(ar): X");
}
private void notifyDefaultData(ApnContext apnContext) {
@@ -2121,7 +2116,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
@Override
protected void log(String s) {
- Log.d(LOG_TAG, "[GsmDCT] " + s);
+ Log.d(LOG_TAG, "[GsmDCT] "+ s);
}
@Override
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index 27cde78470fc..891c7cb0c3cd 100755
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -496,11 +496,6 @@ public final class SIMRecords extends IccRecords {
return null;
}
- // STOPSHIP: to be removed
- if (BaseCommands.getLteOnCdmaModeStatic() == Phone.LTE_ON_CDMA_TRUE) {
- Log.e(LOG_TAG, "getOperatorNumeric: STOPSHIP bad numeric operators in lte");
- return SystemProperties.get("ro.cdma.home.operator.numeric", "310004");
- }
// Length = length of MCC + length of MNC
// length of mcc = 3 (TS 23.003 Section 2.2)
return imsi.substring(0, 3 + mncLength);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 3c0c9a468c96..144ec428a752 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -201,7 +201,8 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
Capability.VIEW_MANIPULATION,
Capability.PLAY_ANIMATION,
Capability.ANIMATED_VIEW_MANIPULATION,
- Capability.ADAPTER_BINDING);
+ Capability.ADAPTER_BINDING,
+ Capability.EXTENDED_VIEWINFO);
BridgeAssetManager.initSystem();
@@ -399,15 +400,6 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
throw new IllegalArgumentException("viewObject is not a View");
}
- @Override
- public int getViewBaseline(Object viewObject) {
- if (viewObject instanceof View) {
- return ((View) viewObject).getBaseline();
- }
-
- throw new IllegalArgumentException("viewObject is not a View");
- }
-
/**
* Returns the lock for the bridge
*/
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
index 1ca3182058ec..3d50b2a8c0e7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
@@ -19,21 +19,23 @@ package com.android.layoutlib.bridge;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.widget.TextView;
/**
* Base class for mocked views.
- *
+ *
* TODO: implement onDraw and draw a rectangle in a random color with the name of the class
* (or better the id of the view).
*/
public class MockView extends TextView {
-
+
public MockView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
-
+
setText(this.getClass().getSimpleName());
setTextColor(0xFF000000);
+ setGravity(Gravity.CENTER);
}
@Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index c53cb23e1e77..b8005199c889 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -74,6 +74,7 @@ import android.view.ViewGroup;
import android.view.View.AttachInfo;
import android.view.View.MeasureSpec;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewGroup.MarginLayoutParams;
import android.widget.AbsListView;
import android.widget.AbsSpinner;
import android.widget.AdapterView;
@@ -513,7 +514,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
mViewRoot.draw(mCanvas);
}
- mViewInfoList = startVisitingViews(mViewRoot, 0);
+ mViewInfoList = startVisitingViews(mViewRoot, 0, params.getExtendedViewInfoMode());
// success!
return SUCCESS.createResult();
@@ -1255,7 +1256,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
}
- private List<ViewInfo> startVisitingViews(View view, int offset) {
+ private List<ViewInfo> startVisitingViews(View view, int offset, boolean setExtendedInfo) {
if (view == null) {
return null;
}
@@ -1264,7 +1265,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
offset += view.getTop();
if (view == mContentRoot) {
- return visitAllChildren(mContentRoot, offset);
+ return visitAllChildren(mContentRoot, offset, setExtendedInfo);
}
// otherwise, look for mContentRoot in the children
@@ -1272,7 +1273,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
ViewGroup group = ((ViewGroup) view);
for (int i = 0; i < group.getChildCount(); i++) {
- List<ViewInfo> list = startVisitingViews(group.getChildAt(i), offset);
+ List<ViewInfo> list = startVisitingViews(group.getChildAt(i), offset,
+ setExtendedInfo);
if (list != null) {
return list;
}
@@ -1287,8 +1289,9 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
* bounds of all the views.
* @param view the root View
* @param offset an offset for the view bounds.
+ * @param setExtendedInfo whether to set the extended view info in the {@link ViewInfo} object.
*/
- private ViewInfo visit(View view, int offset) {
+ private ViewInfo visit(View view, int offset, boolean setExtendedInfo) {
if (view == null) {
return null;
}
@@ -1298,9 +1301,22 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
view.getLeft(), view.getTop() + offset, view.getRight(), view.getBottom() + offset,
view, view.getLayoutParams());
+ if (setExtendedInfo) {
+ MarginLayoutParams marginParams = null;
+ LayoutParams params = view.getLayoutParams();
+ if (params instanceof MarginLayoutParams) {
+ marginParams = (MarginLayoutParams) params;
+ }
+ result.setExtendedInfo(view.getBaseline(),
+ marginParams != null ? marginParams.leftMargin : 0,
+ marginParams != null ? marginParams.topMargin : 0,
+ marginParams != null ? marginParams.rightMargin : 0,
+ marginParams != null ? marginParams.bottomMargin : 0);
+ }
+
if (view instanceof ViewGroup) {
ViewGroup group = ((ViewGroup) view);
- result.setChildren(visitAllChildren(group, 0 /*offset*/));
+ result.setChildren(visitAllChildren(group, 0 /*offset*/, setExtendedInfo));
}
return result;
@@ -1311,15 +1327,17 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
* containing the bounds of all the views.
* @param view the root View
* @param offset an offset for the view bounds.
+ * @param setExtendedInfo whether to set the extended view info in the {@link ViewInfo} object.
*/
- private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int offset) {
+ private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int offset,
+ boolean setExtendedInfo) {
if (viewGroup == null) {
return null;
}
List<ViewInfo> children = new ArrayList<ViewInfo>();
for (int i = 0; i < viewGroup.getChildCount(); i++) {
- children.add(visit(viewGroup.getChildAt(i), offset));
+ children.add(visit(viewGroup.getChildAt(i), offset, setExtendedInfo));
}
return children;
}