diff options
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·t/(T<sub>n</sub>+t) + * t<sub>d</sub>(t,RH) = T<sub>n</sub> · ------------------------------ + * m - [ln(RH/100%) + m·t/(T<sub>n</sub>+t)] + * </pre></center> + * <dl> + * <dt>t<sub>d</sub></dt> <dd>dew point temperature in °C</dd> + * <dt>t</dt> <dd>actual temperature in °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 °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%·A·exp(m·t/(T<sub>n</sub>+t)) + * d<sub>v</sub>(t,RH) = 216.7 · ------------------------- + * 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 °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 °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 Binary files differindex 3cc5c96051b2..86bdcc722220 100644 --- a/data/fonts/MTLc3m.ttf +++ b/data/fonts/MTLc3m.ttf diff --git a/data/fonts/MTLmr3m.ttf b/data/fonts/MTLmr3m.ttf Binary files differindex 05b9093f57ba..76fe7370cd97 100644 --- a/data/fonts/MTLmr3m.ttf +++ b/data/fonts/MTLmr3m.ttf 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 Binary files differnew file mode 100644 index 000000000000..87c7be69a357 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/recents_bg_protect_tile.png diff --git a/packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.png Binary files differnew file mode 100644 index 000000000000..4f4ae788e3f1 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/recents_blue_glow.9.png diff --git a/packages/SystemUI/res/drawable-hdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-hdpi/recents_callout_line.png Binary files differnew file mode 100644 index 000000000000..5f4c0357e674 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/recents_callout_line.png diff --git a/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.png Binary files differnew file mode 100644 index 000000000000..87a67c9cf474 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/recents_thumbnail_bg.png diff --git a/packages/SystemUI/res/drawable-mdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-mdpi/recents_bg_protect_tile.png Binary files differnew file mode 100644 index 000000000000..87c7be69a357 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/recents_bg_protect_tile.png diff --git a/packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.png Binary files differnew file mode 100644 index 000000000000..4f4ae788e3f1 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/recents_blue_glow.9.png diff --git a/packages/SystemUI/res/drawable-mdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-mdpi/recents_callout_line.png Binary files differnew file mode 100644 index 000000000000..5f4c0357e674 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/recents_callout_line.png diff --git a/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.png Binary files differnew file mode 100644 index 000000000000..87a67c9cf474 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg.png diff --git a/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.png b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.png Binary files differnew file mode 100644 index 000000000000..a1c39e6ec046 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/recents_thumbnail_bg_press.png 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; } |