From f579adc8d5e4173e106757db13095989eaa374ab Mon Sep 17 00:00:00 2001 From: Cuihtlauac ALVARADO Date: Mon, 25 Mar 2013 17:08:52 +0100 Subject: [PATCH 01/75] Removed useless test Change-Id: I6269e4ca4ced5c9d2ef68522d26e94e419980c0a --- .../android/systemui/statusbar/phone/PhoneStatusBar.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index bbac4efcbd209..55dd7ce7d02fd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -2449,11 +2449,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { String action = intent.getAction(); if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { int flags = CommandQueue.FLAG_EXCLUDE_NONE; - if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { - String reason = intent.getStringExtra("reason"); - if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) { - flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL; - } + String reason = intent.getStringExtra("reason"); + if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) { + flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL; } animateCollapsePanels(flags); } From 0246363b223f539ad01898b8ad0093fc6a58c5a3 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 28 Jan 2014 09:33:53 -0800 Subject: [PATCH 02/75] GpsLocationProvider: look for config file in /etc/gps..conf The property ro.hardware.gps can be specifid to allow a single system image to work with differrent GPS chips. The HAL layer will use it to load /system/lib/hw/gps..so. Add support to GpsLocationProvider to use the same property to find /etc/gps..conf, falling back to /etc/gps.conf if the property is not set or the file is not present. Change-Id: Ib285c4d28b0d0be5e038a1e61822edd8bc6d97d9 --- .../server/location/GpsLocationProvider.java | 81 ++++++++++++------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index 9c76c19051e89..aba2a98113ac3 100644 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -53,6 +53,7 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; @@ -61,6 +62,7 @@ import android.provider.Telephony.Sms.Intents; import android.telephony.SmsMessage; import android.telephony.TelephonyManager; import android.telephony.gsm.GsmCellLocation; +import android.text.TextUtils; import android.util.Log; import android.util.NtpTrustedTime; @@ -84,6 +86,8 @@ import java.util.Date; import java.util.Map.Entry; import java.util.Properties; +import libcore.io.IoUtils; + /** * A GPS implementation of LocationProvider used by LocationManager. * @@ -194,7 +198,9 @@ public class GpsLocationProvider implements LocationProviderInterface { private static final int AGPS_SETID_TYPE_IMSI = 1; private static final int AGPS_SETID_TYPE_MSISDN = 2; - private static final String PROPERTIES_FILE = "/etc/gps.conf"; + private static final String PROPERTIES_FILE_PREFIX = "/etc/gps"; + private static final String PROPERTIES_FILE_SUFFIX = ".conf"; + private static final String DEFAULT_PROPERTIES_FILE = PROPERTIES_FILE_PREFIX + PROPERTIES_FILE_SUFFIX; private static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L; private static final int GPS_GEOFENCE_AVAILABLE = 1<<1L; @@ -444,6 +450,44 @@ public class GpsLocationProvider implements LocationProviderInterface { return native_is_supported(); } + private boolean loadPropertiesFile(String filename) { + mProperties = new Properties(); + try { + File file = new File(filename); + FileInputStream stream = null; + try { + stream = new FileInputStream(file); + mProperties.load(stream); + } finally { + IoUtils.closeQuietly(stream); + } + + mSuplServerHost = mProperties.getProperty("SUPL_HOST"); + String portString = mProperties.getProperty("SUPL_PORT"); + if (mSuplServerHost != null && portString != null) { + try { + mSuplServerPort = Integer.parseInt(portString); + } catch (NumberFormatException e) { + Log.e(TAG, "unable to parse SUPL_PORT: " + portString); + } + } + + mC2KServerHost = mProperties.getProperty("C2K_HOST"); + portString = mProperties.getProperty("C2K_PORT"); + if (mC2KServerHost != null && portString != null) { + try { + mC2KServerPort = Integer.parseInt(portString); + } catch (NumberFormatException e) { + Log.e(TAG, "unable to parse C2K_PORT: " + portString); + } + } + } catch (IOException e) { + Log.w(TAG, "Could not open GPS configuration file " + filename); + return false; + } + return true; + } + public GpsLocationProvider(Context context, ILocationManager ilocationManager, Looper looper) { mContext = context; @@ -472,34 +516,15 @@ public class GpsLocationProvider implements LocationProviderInterface { mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( BatteryStats.SERVICE_NAME)); - mProperties = new Properties(); - try { - File file = new File(PROPERTIES_FILE); - FileInputStream stream = new FileInputStream(file); - mProperties.load(stream); - stream.close(); + boolean propertiesLoaded = false; + final String gpsHardware = SystemProperties.get("ro.hardware.gps"); + if (!TextUtils.isEmpty(gpsHardware)) { + final String propFilename = PROPERTIES_FILE_PREFIX + "." + gpsHardware + PROPERTIES_FILE_SUFFIX; + propertiesLoaded = loadPropertiesFile(propFilename); + } - mSuplServerHost = mProperties.getProperty("SUPL_HOST"); - String portString = mProperties.getProperty("SUPL_PORT"); - if (mSuplServerHost != null && portString != null) { - try { - mSuplServerPort = Integer.parseInt(portString); - } catch (NumberFormatException e) { - Log.e(TAG, "unable to parse SUPL_PORT: " + portString); - } - } - - mC2KServerHost = mProperties.getProperty("C2K_HOST"); - portString = mProperties.getProperty("C2K_PORT"); - if (mC2KServerHost != null && portString != null) { - try { - mC2KServerPort = Integer.parseInt(portString); - } catch (NumberFormatException e) { - Log.e(TAG, "unable to parse C2K_PORT: " + portString); - } - } - } catch (IOException e) { - Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE); + if (!propertiesLoaded) { + loadPropertiesFile(DEFAULT_PROPERTIES_FILE); } // construct handler, listen for events From 20adb6ce4d52b15472e7e5ee953e06cc349a827c Mon Sep 17 00:00:00 2001 From: "henry.uh_chen" Date: Wed, 2 Jul 2014 19:36:56 +0800 Subject: [PATCH 03/75] [HWUI]: fix residual line on FrameBuffer Symptom: If app applies animation to enlarge a bitmap, there will be an residual line on the screen Root Cause: On platform which has Tile Rendering implementation (ex. Qualcomm CPU), startTiling() call will restrict the framebuffer region which GPU can affect. So the expansion of clear region by 1 will not take effect if startTiling region is not expanded. Solution: Expand the startTiling region by 1, too. Reproduce steps: Apply animation to enlarge (and then shrink) a bitmap icon. Change-Id: I7b4a59e180daa29dbe909d9e11f4093ae1d7396f --- libs/hwui/OpenGLRenderer.cpp | 24 +++++++++++++++++++----- libs/hwui/OpenGLRenderer.h | 4 ++-- 2 files changed, 21 insertions(+), 7 deletions(-) mode change 100644 => 100755 libs/hwui/OpenGLRenderer.cpp mode change 100644 => 100755 libs/hwui/OpenGLRenderer.h diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp old mode 100644 new mode 100755 index 4d76bed204245..ee4a054ab86ff --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -283,21 +283,34 @@ void OpenGLRenderer::syncState() { } } -void OpenGLRenderer::startTiling(const sp& s, bool opaque) { +void OpenGLRenderer::startTiling(const sp& s, bool opaque, bool expand) { if (!mSuppressTiling) { Rect* clip = &mTilingClip; if (s->flags & Snapshot::kFlagFboTarget) { clip = &(s->layer->clipRect); } - startTiling(*clip, s->height, opaque); + startTiling(*clip, s->height, opaque, expand); } } -void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque) { +void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque, bool expand) { if (!mSuppressTiling) { - mCaches.startTiling(clip.left, windowHeight - clip.bottom, + if(expand) { + // Expand the startTiling region by 1 + int leftNotZero = (clip.left > 0) ? 1 : 0; + int topNotZero = (windowHeight - clip.bottom > 0) ? 1 : 0; + + mCaches.startTiling( + clip.left - leftNotZero, + windowHeight - clip.bottom - topNotZero, + clip.right - clip.left + leftNotZero + 1, + clip.bottom - clip.top + topNotZero + 1, + opaque); + } else { + mCaches.startTiling(clip.left, windowHeight - clip.bottom, clip.right - clip.left, clip.bottom - clip.top, opaque); + } } } @@ -1003,7 +1016,8 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLui glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, layer->getTexture(), 0); - startTiling(mSnapshot, true); + // Expand the startTiling region by 1 + startTiling(mSnapshot, true, true); // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering mCaches.enableScissor(); diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h old mode 100644 new mode 100755 index 9afb7ad4f2d2f..2e03a1b8330e0 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -587,14 +587,14 @@ private: * This method needs to be invoked every time getTargetFbo() is * bound again. */ - void startTiling(const sp& snapshot, bool opaque = false); + void startTiling(const sp& snapshot, bool opaque = false, bool expand = false); /** * Tells the GPU what part of the screen is about to be redrawn. * This method needs to be invoked every time getTargetFbo() is * bound again. */ - void startTiling(const Rect& clip, int windowHeight, bool opaque = false); + void startTiling(const Rect& clip, int windowHeight, bool opaque = false, bool expand = false); /** * Tells the GPU that we are done drawing the frame or that we From 3a1bffa835f279628e50385af3c85789bc737f64 Mon Sep 17 00:00:00 2001 From: "henry.uh_chen" Date: Thu, 3 Jul 2014 18:01:37 +0800 Subject: [PATCH 04/75] [HWUI] Fix invisible views are still shown in popup window Symptom: Invisible views are still shown in popup window Root Cause: glClear isn't called if there is no draw command Solution: Even if there is no draw command, it still calls glClear by startFrame(). Reproduce steps: App set one view in a popup window to invisible, it will be still visible. Change-Id: I36bc7b752434ad388fbb80ea63341ac778f48d35 --- libs/hwui/OpenGLRenderer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) mode change 100644 => 100755 libs/hwui/OpenGLRenderer.cpp diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp old mode 100644 new mode 100755 index 4d76bed204245..f0adea9a338df --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -2057,7 +2057,9 @@ status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, return status | deferredList.flush(*this, dirty); } - return DrawGlInfo::kStatusDone; + // Even if there is no drawing command(Ex: invisible), + // it still needs startFrame to clear buffer and start tiling. + return startFrame(); } void OpenGLRenderer::outputDisplayList(DisplayList* displayList) { From 42856eb4f11a13391fbbeb80481d543568404cd7 Mon Sep 17 00:00:00 2001 From: "henry.uh_chen" Date: Thu, 3 Jul 2014 20:40:22 +0800 Subject: [PATCH 05/75] [Bitmap] Add null pointer protection in Bitmap_sameAs() Symptom: SkBitmap::getAddr(int, int) may return NULL due to unrecognized config (ex: kRLE_Index8_Config). This will cause memcmp method to crash. Since bm0 and bm1 both have pixel data() (have passed NULL == getPixels() check), those 2 bitmaps should be valid (only unrecognized), we return JNI_FALSE to warn user those 2 unrecognized config bitmaps may be different. Change-Id: I6970c27de412110a3035d0a783112c4cd3ebc35b --- core/jni/android/graphics/Bitmap.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) mode change 100644 => 100755 core/jni/android/graphics/Bitmap.cpp diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp old mode 100644 new mode 100755 index d97a945bbef88..040225e331601 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -775,7 +775,19 @@ static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, const int h = bm0->height(); const size_t size = bm0->width() * bm0->bytesPerPixel(); for (int y = 0; y < h; y++) { - if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) { + // SkBitmap::getAddr(int, int) may return NULL due to unrecognized config + // (ex: kRLE_Index8_Config). This will cause memcmp method to crash. Since bm0 + // and bm1 both have pixel data() (have passed NULL == getPixels() check), + // those 2 bitmaps should be valid (only unrecognized), we return JNI_FALSE + // to warn user those 2 unrecognized config bitmaps may be different. + void *bm0Addr = bm0->getAddr(0, y); + void *bm1Addr = bm1->getAddr(0, y); + + if(bm0Addr == NULL || bm1Addr == NULL) { + return JNI_FALSE; + } + + if (memcmp(bm0Addr, bm1Addr, size) != 0) { return JNI_FALSE; } } From 882e08759137a77090c3c7dff8cba425a3393474 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Mon, 7 Jul 2014 10:35:15 -0700 Subject: [PATCH 06/75] Add BlendComposite.java The class is adapted from a demo tool for Blending Modes written by Romain Guy (romainguy@android.com). The tool is available at http://www.curious-creature.org/2006/09/20/new-blendings-modes-for-java2d/ Change-Id: I8f7c7ca08d3078106056764a4e2f1ce95d990137 --- .../src/android/graphics/BlendComposite.java | 772 ++++++++++++++++++ 1 file changed, 772 insertions(+) create mode 100644 tools/layoutlib/bridge/src/android/graphics/BlendComposite.java diff --git a/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java b/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java new file mode 100644 index 0000000000000..a864f84a2ee6a --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java @@ -0,0 +1,772 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import java.awt.Composite; +import java.awt.CompositeContext; +import java.awt.RenderingHints; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + +/* + * (non-Javadoc) + * The class is adapted from a demo tool for Blending Modes written by + * Romain Guy (romainguy@android.com). The tool is available at + * http://www.curious-creature.org/2006/09/20/new-blendings-modes-for-java2d/ + */ +public final class BlendComposite implements Composite { + public enum BlendingMode { + NORMAL, + AVERAGE, + MULTIPLY, + SCREEN, + DARKEN, + LIGHTEN, + OVERLAY, + HARD_LIGHT, + SOFT_LIGHT, + DIFFERENCE, + NEGATION, + EXCLUSION, + COLOR_DODGE, + INVERSE_COLOR_DODGE, + SOFT_DODGE, + COLOR_BURN, + INVERSE_COLOR_BURN, + SOFT_BURN, + REFLECT, + GLOW, + FREEZE, + HEAT, + ADD, + SUBTRACT, + STAMP, + RED, + GREEN, + BLUE, + HUE, + SATURATION, + COLOR, + LUMINOSITY + } + + public static final BlendComposite Normal = new BlendComposite(BlendingMode.NORMAL); + public static final BlendComposite Average = new BlendComposite(BlendingMode.AVERAGE); + public static final BlendComposite Multiply = new BlendComposite(BlendingMode.MULTIPLY); + public static final BlendComposite Screen = new BlendComposite(BlendingMode.SCREEN); + public static final BlendComposite Darken = new BlendComposite(BlendingMode.DARKEN); + public static final BlendComposite Lighten = new BlendComposite(BlendingMode.LIGHTEN); + public static final BlendComposite Overlay = new BlendComposite(BlendingMode.OVERLAY); + public static final BlendComposite HardLight = new BlendComposite(BlendingMode.HARD_LIGHT); + public static final BlendComposite SoftLight = new BlendComposite(BlendingMode.SOFT_LIGHT); + public static final BlendComposite Difference = new BlendComposite(BlendingMode.DIFFERENCE); + public static final BlendComposite Negation = new BlendComposite(BlendingMode.NEGATION); + public static final BlendComposite Exclusion = new BlendComposite(BlendingMode.EXCLUSION); + public static final BlendComposite ColorDodge = new BlendComposite(BlendingMode.COLOR_DODGE); + public static final BlendComposite InverseColorDodge = new BlendComposite(BlendingMode.INVERSE_COLOR_DODGE); + public static final BlendComposite SoftDodge = new BlendComposite(BlendingMode.SOFT_DODGE); + public static final BlendComposite ColorBurn = new BlendComposite(BlendingMode.COLOR_BURN); + public static final BlendComposite InverseColorBurn = new BlendComposite(BlendingMode.INVERSE_COLOR_BURN); + public static final BlendComposite SoftBurn = new BlendComposite(BlendingMode.SOFT_BURN); + public static final BlendComposite Reflect = new BlendComposite(BlendingMode.REFLECT); + public static final BlendComposite Glow = new BlendComposite(BlendingMode.GLOW); + public static final BlendComposite Freeze = new BlendComposite(BlendingMode.FREEZE); + public static final BlendComposite Heat = new BlendComposite(BlendingMode.HEAT); + public static final BlendComposite Add = new BlendComposite(BlendingMode.ADD); + public static final BlendComposite Subtract = new BlendComposite(BlendingMode.SUBTRACT); + public static final BlendComposite Stamp = new BlendComposite(BlendingMode.STAMP); + public static final BlendComposite Red = new BlendComposite(BlendingMode.RED); + public static final BlendComposite Green = new BlendComposite(BlendingMode.GREEN); + public static final BlendComposite Blue = new BlendComposite(BlendingMode.BLUE); + public static final BlendComposite Hue = new BlendComposite(BlendingMode.HUE); + public static final BlendComposite Saturation = new BlendComposite(BlendingMode.SATURATION); + public static final BlendComposite Color = new BlendComposite(BlendingMode.COLOR); + public static final BlendComposite Luminosity = new BlendComposite(BlendingMode.LUMINOSITY); + + private float alpha; + private BlendingMode mode; + + private BlendComposite(BlendingMode mode) { + this(mode, 1.0f); + } + + private BlendComposite(BlendingMode mode, float alpha) { + this.mode = mode; + setAlpha(alpha); + } + + public static BlendComposite getInstance(BlendingMode mode) { + return new BlendComposite(mode); + } + + public static BlendComposite getInstance(BlendingMode mode, float alpha) { + return new BlendComposite(mode, alpha); + } + + public BlendComposite derive(BlendingMode mode) { + return this.mode == mode ? this : new BlendComposite(mode, getAlpha()); + } + + public BlendComposite derive(float alpha) { + return this.alpha == alpha ? this : new BlendComposite(getMode(), alpha); + } + + public float getAlpha() { + return alpha; + } + + public BlendingMode getMode() { + return mode; + } + + private void setAlpha(float alpha) { + if (alpha < 0.0f || alpha > 1.0f) { + throw new IllegalArgumentException( + "alpha must be comprised between 0.0f and 1.0f"); + } + + this.alpha = alpha; + } + + @Override + public int hashCode() { + return Float.floatToIntBits(alpha) * 31 + mode.ordinal(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BlendComposite)) { + return false; + } + + BlendComposite bc = (BlendComposite) obj; + + if (mode != bc.mode) { + return false; + } + + return alpha == bc.alpha; + } + + public CompositeContext createContext(ColorModel srcColorModel, + ColorModel dstColorModel, + RenderingHints hints) { + return new BlendingContext(this); + } + + private static final class BlendingContext implements CompositeContext { + private final Blender blender; + private final BlendComposite composite; + + private BlendingContext(BlendComposite composite) { + this.composite = composite; + this.blender = Blender.getBlenderFor(composite); + } + + public void dispose() { + } + + public void compose(Raster src, Raster dstIn, WritableRaster dstOut) { + if (src.getSampleModel().getDataType() != DataBuffer.TYPE_INT || + dstIn.getSampleModel().getDataType() != DataBuffer.TYPE_INT || + dstOut.getSampleModel().getDataType() != DataBuffer.TYPE_INT) { + throw new IllegalStateException( + "Source and destination must store pixels as INT."); + } + + int width = Math.min(src.getWidth(), dstIn.getWidth()); + int height = Math.min(src.getHeight(), dstIn.getHeight()); + + float alpha = composite.getAlpha(); + + int[] srcPixel = new int[4]; + int[] dstPixel = new int[4]; + int[] srcPixels = new int[width]; + int[] dstPixels = new int[width]; + + for (int y = 0; y < height; y++) { + src.getDataElements(0, y, width, 1, srcPixels); + dstIn.getDataElements(0, y, width, 1, dstPixels); + for (int x = 0; x < width; x++) { + // pixels are stored as INT_ARGB + // our arrays are [R, G, B, A] + int pixel = srcPixels[x]; + srcPixel[0] = (pixel >> 16) & 0xFF; + srcPixel[1] = (pixel >> 8) & 0xFF; + srcPixel[2] = (pixel ) & 0xFF; + srcPixel[3] = (pixel >> 24) & 0xFF; + + pixel = dstPixels[x]; + dstPixel[0] = (pixel >> 16) & 0xFF; + dstPixel[1] = (pixel >> 8) & 0xFF; + dstPixel[2] = (pixel ) & 0xFF; + dstPixel[3] = (pixel >> 24) & 0xFF; + + int[] result = blender.blend(srcPixel, dstPixel); + + // mixes the result with the opacity + dstPixels[x] = ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 | + ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 | + ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) << 8 | + (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF; + } + dstOut.setDataElements(0, y, width, 1, dstPixels); + } + } + } + + private static abstract class Blender { + public abstract int[] blend(int[] src, int[] dst); + + private static void RGBtoHSL(int r, int g, int b, float[] hsl) { + float var_R = (r / 255f); + float var_G = (g / 255f); + float var_B = (b / 255f); + + float var_Min; + float var_Max; + float del_Max; + + if (var_R > var_G) { + var_Min = var_G; + var_Max = var_R; + } else { + var_Min = var_R; + var_Max = var_G; + } + if (var_B > var_Max) { + var_Max = var_B; + } + if (var_B < var_Min) { + var_Min = var_B; + } + + del_Max = var_Max - var_Min; + + float H, S, L; + L = (var_Max + var_Min) / 2f; + + if (del_Max - 0.01f <= 0.0f) { + H = 0; + S = 0; + } else { + if (L < 0.5f) { + S = del_Max / (var_Max + var_Min); + } else { + S = del_Max / (2 - var_Max - var_Min); + } + + float del_R = (((var_Max - var_R) / 6f) + (del_Max / 2f)) / del_Max; + float del_G = (((var_Max - var_G) / 6f) + (del_Max / 2f)) / del_Max; + float del_B = (((var_Max - var_B) / 6f) + (del_Max / 2f)) / del_Max; + + if (var_R == var_Max) { + H = del_B - del_G; + } else if (var_G == var_Max) { + H = (1 / 3f) + del_R - del_B; + } else { + H = (2 / 3f) + del_G - del_R; + } + if (H < 0) { + H += 1; + } + if (H > 1) { + H -= 1; + } + } + + hsl[0] = H; + hsl[1] = S; + hsl[2] = L; + } + + private static void HSLtoRGB(float h, float s, float l, int[] rgb) { + int R, G, B; + + if (s - 0.01f <= 0.0f) { + R = (int) (l * 255.0f); + G = (int) (l * 255.0f); + B = (int) (l * 255.0f); + } else { + float var_1, var_2; + if (l < 0.5f) { + var_2 = l * (1 + s); + } else { + var_2 = (l + s) - (s * l); + } + var_1 = 2 * l - var_2; + + R = (int) (255.0f * hue2RGB(var_1, var_2, h + (1.0f / 3.0f))); + G = (int) (255.0f * hue2RGB(var_1, var_2, h)); + B = (int) (255.0f * hue2RGB(var_1, var_2, h - (1.0f / 3.0f))); + } + + rgb[0] = R; + rgb[1] = G; + rgb[2] = B; + } + + private static float hue2RGB(float v1, float v2, float vH) { + if (vH < 0.0f) { + vH += 1.0f; + } + if (vH > 1.0f) { + vH -= 1.0f; + } + if ((6.0f * vH) < 1.0f) { + return (v1 + (v2 - v1) * 6.0f * vH); + } + if ((2.0f * vH) < 1.0f) { + return (v2); + } + if ((3.0f * vH) < 2.0f) { + return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f); + } + return (v1); + } + + public static Blender getBlenderFor(BlendComposite composite) { + switch (composite.getMode()) { + case NORMAL: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return src; + } + }; + case ADD: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + Math.min(255, src[0] + dst[0]), + Math.min(255, src[1] + dst[1]), + Math.min(255, src[2] + dst[2]), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case AVERAGE: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + (src[0] + dst[0]) >> 1, + (src[1] + dst[1]) >> 1, + (src[2] + dst[2]) >> 1, + Math.min(255, src[3] + dst[3]) + }; + } + }; + case BLUE: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + dst[0], + src[1], + dst[2], + Math.min(255, src[3] + dst[3]) + }; + } + }; + case COLOR: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + float[] srcHSL = new float[3]; + RGBtoHSL(src[0], src[1], src[2], srcHSL); + float[] dstHSL = new float[3]; + RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); + + int[] result = new int[4]; + HSLtoRGB(srcHSL[0], srcHSL[1], dstHSL[2], result); + result[3] = Math.min(255, src[3] + dst[3]); + + return result; + } + }; + case COLOR_BURN: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + src[0] == 0 ? 0 : + Math.max(0, 255 - (((255 - dst[0]) << 8) / src[0])), + src[1] == 0 ? 0 : + Math.max(0, 255 - (((255 - dst[1]) << 8) / src[1])), + src[2] == 0 ? 0 : + Math.max(0, 255 - (((255 - dst[2]) << 8) / src[2])), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case COLOR_DODGE: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + src[0] == 255 ? 255 : + Math.min((dst[0] << 8) / (255 - src[0]), 255), + src[1] == 255 ? 255 : + Math.min((dst[1] << 8) / (255 - src[1]), 255), + src[2] == 255 ? 255 : + Math.min((dst[2] << 8) / (255 - src[2]), 255), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case DARKEN: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + Math.min(src[0], dst[0]), + Math.min(src[1], dst[1]), + Math.min(src[2], dst[2]), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case DIFFERENCE: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + Math.abs(dst[0] - src[0]), + Math.abs(dst[1] - src[1]), + Math.abs(dst[2] - src[2]), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case EXCLUSION: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + dst[0] + src[0] - (dst[0] * src[0] >> 7), + dst[1] + src[1] - (dst[1] * src[1] >> 7), + dst[2] + src[2] - (dst[2] * src[2] >> 7), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case FREEZE: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + src[0] == 0 ? 0 : Math.max(0, 255 - (255 - dst[0]) * (255 - dst[0]) / src[0]), + src[1] == 0 ? 0 : Math.max(0, 255 - (255 - dst[1]) * (255 - dst[1]) / src[1]), + src[2] == 0 ? 0 : Math.max(0, 255 - (255 - dst[2]) * (255 - dst[2]) / src[2]), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case GLOW: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + dst[0] == 255 ? 255 : Math.min(255, src[0] * src[0] / (255 - dst[0])), + dst[1] == 255 ? 255 : Math.min(255, src[1] * src[1] / (255 - dst[1])), + dst[2] == 255 ? 255 : Math.min(255, src[2] * src[2] / (255 - dst[2])), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case GREEN: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + dst[0], + dst[1], + src[2], + Math.min(255, src[3] + dst[3]) + }; + } + }; + case HARD_LIGHT: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + src[0] < 128 ? dst[0] * src[0] >> 7 : + 255 - ((255 - src[0]) * (255 - dst[0]) >> 7), + src[1] < 128 ? dst[1] * src[1] >> 7 : + 255 - ((255 - src[1]) * (255 - dst[1]) >> 7), + src[2] < 128 ? dst[2] * src[2] >> 7 : + 255 - ((255 - src[2]) * (255 - dst[2]) >> 7), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case HEAT: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + dst[0] == 0 ? 0 : Math.max(0, 255 - (255 - src[0]) * (255 - src[0]) / dst[0]), + dst[1] == 0 ? 0 : Math.max(0, 255 - (255 - src[1]) * (255 - src[1]) / dst[1]), + dst[2] == 0 ? 0 : Math.max(0, 255 - (255 - src[2]) * (255 - src[2]) / dst[2]), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case HUE: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + float[] srcHSL = new float[3]; + RGBtoHSL(src[0], src[1], src[2], srcHSL); + float[] dstHSL = new float[3]; + RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); + + int[] result = new int[4]; + HSLtoRGB(srcHSL[0], dstHSL[1], dstHSL[2], result); + result[3] = Math.min(255, src[3] + dst[3]); + + return result; + } + }; + case INVERSE_COLOR_BURN: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + dst[0] == 0 ? 0 : + Math.max(0, 255 - (((255 - src[0]) << 8) / dst[0])), + dst[1] == 0 ? 0 : + Math.max(0, 255 - (((255 - src[1]) << 8) / dst[1])), + dst[2] == 0 ? 0 : + Math.max(0, 255 - (((255 - src[2]) << 8) / dst[2])), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case INVERSE_COLOR_DODGE: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + dst[0] == 255 ? 255 : + Math.min((src[0] << 8) / (255 - dst[0]), 255), + dst[1] == 255 ? 255 : + Math.min((src[1] << 8) / (255 - dst[1]), 255), + dst[2] == 255 ? 255 : + Math.min((src[2] << 8) / (255 - dst[2]), 255), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case LIGHTEN: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + Math.max(src[0], dst[0]), + Math.max(src[1], dst[1]), + Math.max(src[2], dst[2]), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case LUMINOSITY: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + float[] srcHSL = new float[3]; + RGBtoHSL(src[0], src[1], src[2], srcHSL); + float[] dstHSL = new float[3]; + RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); + + int[] result = new int[4]; + HSLtoRGB(dstHSL[0], dstHSL[1], srcHSL[2], result); + result[3] = Math.min(255, src[3] + dst[3]); + + return result; + } + }; + case MULTIPLY: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + (src[0] * dst[0]) >> 8, + (src[1] * dst[1]) >> 8, + (src[2] * dst[2]) >> 8, + Math.min(255, src[3] + dst[3]) + }; + } + }; + case NEGATION: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + 255 - Math.abs(255 - dst[0] - src[0]), + 255 - Math.abs(255 - dst[1] - src[1]), + 255 - Math.abs(255 - dst[2] - src[2]), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case OVERLAY: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + dst[0] < 128 ? dst[0] * src[0] >> 7 : + 255 - ((255 - dst[0]) * (255 - src[0]) >> 7), + dst[1] < 128 ? dst[1] * src[1] >> 7 : + 255 - ((255 - dst[1]) * (255 - src[1]) >> 7), + dst[2] < 128 ? dst[2] * src[2] >> 7 : + 255 - ((255 - dst[2]) * (255 - src[2]) >> 7), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case RED: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + src[0], + dst[1], + dst[2], + Math.min(255, src[3] + dst[3]) + }; + } + }; + case REFLECT: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + src[0] == 255 ? 255 : Math.min(255, dst[0] * dst[0] / (255 - src[0])), + src[1] == 255 ? 255 : Math.min(255, dst[1] * dst[1] / (255 - src[1])), + src[2] == 255 ? 255 : Math.min(255, dst[2] * dst[2] / (255 - src[2])), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case SATURATION: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + float[] srcHSL = new float[3]; + RGBtoHSL(src[0], src[1], src[2], srcHSL); + float[] dstHSL = new float[3]; + RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); + + int[] result = new int[4]; + HSLtoRGB(dstHSL[0], srcHSL[1], dstHSL[2], result); + result[3] = Math.min(255, src[3] + dst[3]); + + return result; + } + }; + case SCREEN: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + 255 - ((255 - src[0]) * (255 - dst[0]) >> 8), + 255 - ((255 - src[1]) * (255 - dst[1]) >> 8), + 255 - ((255 - src[2]) * (255 - dst[2]) >> 8), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case SOFT_BURN: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + dst[0] + src[0] < 256 ? + (dst[0] == 255 ? 255 : + Math.min(255, (src[0] << 7) / (255 - dst[0]))) : + Math.max(0, 255 - (((255 - dst[0]) << 7) / src[0])), + dst[1] + src[1] < 256 ? + (dst[1] == 255 ? 255 : + Math.min(255, (src[1] << 7) / (255 - dst[1]))) : + Math.max(0, 255 - (((255 - dst[1]) << 7) / src[1])), + dst[2] + src[2] < 256 ? + (dst[2] == 255 ? 255 : + Math.min(255, (src[2] << 7) / (255 - dst[2]))) : + Math.max(0, 255 - (((255 - dst[2]) << 7) / src[2])), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case SOFT_DODGE: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + dst[0] + src[0] < 256 ? + (src[0] == 255 ? 255 : + Math.min(255, (dst[0] << 7) / (255 - src[0]))) : + Math.max(0, 255 - (((255 - src[0]) << 7) / dst[0])), + dst[1] + src[1] < 256 ? + (src[1] == 255 ? 255 : + Math.min(255, (dst[1] << 7) / (255 - src[1]))) : + Math.max(0, 255 - (((255 - src[1]) << 7) / dst[1])), + dst[2] + src[2] < 256 ? + (src[2] == 255 ? 255 : + Math.min(255, (dst[2] << 7) / (255 - src[2]))) : + Math.max(0, 255 - (((255 - src[2]) << 7) / dst[2])), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case SOFT_LIGHT: + break; + case STAMP: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + Math.max(0, Math.min(255, dst[0] + 2 * src[0] - 256)), + Math.max(0, Math.min(255, dst[1] + 2 * src[1] - 256)), + Math.max(0, Math.min(255, dst[2] + 2 * src[2] - 256)), + Math.min(255, src[3] + dst[3]) + }; + } + }; + case SUBTRACT: + return new Blender() { + @Override + public int[] blend(int[] src, int[] dst) { + return new int[] { + Math.max(0, src[0] + dst[0] - 256), + Math.max(0, src[1] + dst[1] - 256), + Math.max(0, src[2] + dst[2] - 256), + Math.min(255, src[3] + dst[3]) + }; + } + }; + } + throw new IllegalArgumentException("Blender not implement for " + + composite.getMode().name()); + } + } +} From 9ce074610413ce3a5dd0cef9295f0ae9061402b7 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Mon, 7 Jul 2014 14:22:19 -0700 Subject: [PATCH 07/75] Optimize Blend composites. Removed redundant array allocations to improve performance for various blending modes. Change-Id: Iaba1d6ff3ad03eebdc859c599b610cc593370438 --- .../src/android/graphics/BlendComposite.java | 277 +++++++++--------- 1 file changed, 133 insertions(+), 144 deletions(-) diff --git a/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java b/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java index a864f84a2ee6a..a3ec2ccd013b6 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java +++ b/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java @@ -197,42 +197,54 @@ public final class BlendComposite implements Composite { int[] srcPixel = new int[4]; int[] dstPixel = new int[4]; + int[] result = new int[4]; int[] srcPixels = new int[width]; int[] dstPixels = new int[width]; for (int y = 0; y < height; y++) { - src.getDataElements(0, y, width, 1, srcPixels); dstIn.getDataElements(0, y, width, 1, dstPixels); - for (int x = 0; x < width; x++) { - // pixels are stored as INT_ARGB - // our arrays are [R, G, B, A] - int pixel = srcPixels[x]; - srcPixel[0] = (pixel >> 16) & 0xFF; - srcPixel[1] = (pixel >> 8) & 0xFF; - srcPixel[2] = (pixel ) & 0xFF; - srcPixel[3] = (pixel >> 24) & 0xFF; + if (alpha != 0) { + src.getDataElements(0, y, width, 1, srcPixels); + for (int x = 0; x < width; x++) { + // pixels are stored as INT_ARGB + // our arrays are [R, G, B, A] + int pixel = srcPixels[x]; + srcPixel[0] = (pixel >> 16) & 0xFF; + srcPixel[1] = (pixel >> 8) & 0xFF; + srcPixel[2] = (pixel ) & 0xFF; + srcPixel[3] = (pixel >> 24) & 0xFF; - pixel = dstPixels[x]; - dstPixel[0] = (pixel >> 16) & 0xFF; - dstPixel[1] = (pixel >> 8) & 0xFF; - dstPixel[2] = (pixel ) & 0xFF; - dstPixel[3] = (pixel >> 24) & 0xFF; + pixel = dstPixels[x]; + dstPixel[0] = (pixel >> 16) & 0xFF; + dstPixel[1] = (pixel >> 8) & 0xFF; + dstPixel[2] = (pixel ) & 0xFF; + dstPixel[3] = (pixel >> 24) & 0xFF; - int[] result = blender.blend(srcPixel, dstPixel); + result = blender.blend(srcPixel, dstPixel, result); - // mixes the result with the opacity - dstPixels[x] = ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 | - ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 | - ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) << 8 | + // mixes the result with the opacity + if (alpha == 1) { + dstPixels[x] = (result[3] & 0xFF) << 24 | + (result[0] & 0xFF) << 16 | + (result[1] & 0xFF) << 8 | + result[2] & 0xFF; + } else { + dstPixels[x] = + ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 | + ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 | + ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) << 8 | (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF; - } + } + + } + } dstOut.setDataElements(0, y, width, 1, dstPixels); } } } private static abstract class Blender { - public abstract int[] blend(int[] src, int[] dst); + public abstract int[] blend(int[] src, int[] dst, int[] result); private static void RGBtoHSL(int r, int g, int b, float[] hsl) { float var_R = (r / 255f); @@ -346,56 +358,50 @@ public final class BlendComposite implements Composite { case NORMAL: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { - return src; + public int[] blend(int[] src, int[] dst, int[] result) { + System.arraycopy(src, 0, result, 0, 4); + return result; } }; case ADD: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { - return new int[] { - Math.min(255, src[0] + dst[0]), - Math.min(255, src[1] + dst[1]), - Math.min(255, src[2] + dst[2]), - Math.min(255, src[3] + dst[3]) - }; + public int[] blend(int[] src, int[] dst, int[] result) { + for (int i = 0; i < 4; i++) { + result[i] = Math.min(255, src[i] + dst[i]); + } + return result; } }; case AVERAGE: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { - return new int[] { - (src[0] + dst[0]) >> 1, - (src[1] + dst[1]) >> 1, - (src[2] + dst[2]) >> 1, - Math.min(255, src[3] + dst[3]) - }; + public int[] blend(int[] src, int[] dst, int[] result) { + for (int i = 0; i < 3; i++) { + result[i] = (src[i] + dst[i]) >> 1; + } + result[3] = Math.min(255, src[3] + dst[3]); + return result; } }; case BLUE: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { - return new int[] { - dst[0], - src[1], - dst[2], - Math.min(255, src[3] + dst[3]) - }; + public int[] blend(int[] src, int[] dst, int[] result) { + System.arraycopy(dst, 0, result, 0, 3); + result[3] = Math.min(255, src[3] + dst[3]); + return result; } }; case COLOR: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { float[] srcHSL = new float[3]; RGBtoHSL(src[0], src[1], src[2], srcHSL); float[] dstHSL = new float[3]; RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); - int[] result = new int[4]; HSLtoRGB(srcHSL[0], srcHSL[1], dstHSL[2], result); result[3] = Math.min(255, src[3] + dst[3]); @@ -405,97 +411,88 @@ public final class BlendComposite implements Composite { case COLOR_BURN: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { - return new int[] { - src[0] == 0 ? 0 : - Math.max(0, 255 - (((255 - dst[0]) << 8) / src[0])), - src[1] == 0 ? 0 : - Math.max(0, 255 - (((255 - dst[1]) << 8) / src[1])), - src[2] == 0 ? 0 : - Math.max(0, 255 - (((255 - dst[2]) << 8) / src[2])), - Math.min(255, src[3] + dst[3]) - }; + public int[] blend(int[] src, int[] dst, int[] result) { + for (int i = 0; i < 3; i++) { + result[i] = src[i] == 0 ? 0 : + Math.max(0, 255 - (((255 - dst[i]) << 8) / src[i])); + } + result[3] = Math.min(255, src[3] + dst[3]); + return result; } }; case COLOR_DODGE: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { - return new int[] { - src[0] == 255 ? 255 : - Math.min((dst[0] << 8) / (255 - src[0]), 255), - src[1] == 255 ? 255 : - Math.min((dst[1] << 8) / (255 - src[1]), 255), - src[2] == 255 ? 255 : - Math.min((dst[2] << 8) / (255 - src[2]), 255), - Math.min(255, src[3] + dst[3]) - }; + public int[] blend(int[] src, int[] dst, int[] result) { + for (int i = 0; i < 3; i++) { + result[i] = src[i] == 255 ? 255 : + Math.min((dst[i] << 8) / (255 - src[i]), 255); + } + result[3] = Math.min(255, src[3] + dst[3]); + return result; } }; case DARKEN: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { - return new int[] { - Math.min(src[0], dst[0]), - Math.min(src[1], dst[1]), - Math.min(src[2], dst[2]), - Math.min(255, src[3] + dst[3]) - }; + public int[] blend(int[] src, int[] dst, int[] result) { + for (int i = 0; i < 3; i++) { + result[i] = Math.min(src[i], dst[i]); + } + result[3] = Math.min(255, src[3] + dst[3]); + return result; } }; case DIFFERENCE: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { - return new int[] { - Math.abs(dst[0] - src[0]), - Math.abs(dst[1] - src[1]), - Math.abs(dst[2] - src[2]), - Math.min(255, src[3] + dst[3]) - }; + public int[] blend(int[] src, int[] dst, int[] result) { + for (int i = 0; i < 3; i++) { + result[i] = dst[i] + src[i] - (dst[i] * src[i] >> 7); + } + result[3] = Math.min(255, src[3] + dst[3]); + return result; } }; case EXCLUSION: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { - return new int[] { - dst[0] + src[0] - (dst[0] * src[0] >> 7), - dst[1] + src[1] - (dst[1] * src[1] >> 7), - dst[2] + src[2] - (dst[2] * src[2] >> 7), - Math.min(255, src[3] + dst[3]) - }; + public int[] blend(int[] src, int[] dst, int[] result) { + for (int i = 0; i < 3; i++) { + result[i] = dst[i] + src[i] - (dst[i] * src[i] >> 7); + } + result[3] = Math.min(255, src[3] + dst[3]); + return result; } }; case FREEZE: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { - return new int[] { - src[0] == 0 ? 0 : Math.max(0, 255 - (255 - dst[0]) * (255 - dst[0]) / src[0]), - src[1] == 0 ? 0 : Math.max(0, 255 - (255 - dst[1]) * (255 - dst[1]) / src[1]), - src[2] == 0 ? 0 : Math.max(0, 255 - (255 - dst[2]) * (255 - dst[2]) / src[2]), - Math.min(255, src[3] + dst[3]) - }; + public int[] blend(int[] src, int[] dst, int[] result) { + for (int i = 0; i < 3; i++) { + result[i] = src[i] == 0 ? 0 : + Math.max(0, 255 - (255 - dst[i]) * (255 - dst[i]) / src[i]); + } + result[3] = Math.min(255, src[3] + dst[3]); + return result; } }; case GLOW: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { - return new int[] { - dst[0] == 255 ? 255 : Math.min(255, src[0] * src[0] / (255 - dst[0])), - dst[1] == 255 ? 255 : Math.min(255, src[1] * src[1] / (255 - dst[1])), - dst[2] == 255 ? 255 : Math.min(255, src[2] * src[2] / (255 - dst[2])), - Math.min(255, src[3] + dst[3]) - }; + public int[] blend(int[] src, int[] dst, int[] result) { + for (int i = 0; i < 3; i++) { + result[i] = dst[i] == 255 ? 255 : + Math.min(255, src[i] * src[i] / (255 - dst[i])); + } + result[3] = Math.min(255, src[3] + dst[3]); + return result; } }; case GREEN: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { return new int[] { dst[0], dst[1], @@ -507,7 +504,7 @@ public final class BlendComposite implements Composite { case HARD_LIGHT: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { return new int[] { src[0] < 128 ? dst[0] * src[0] >> 7 : 255 - ((255 - src[0]) * (255 - dst[0]) >> 7), @@ -522,7 +519,7 @@ public final class BlendComposite implements Composite { case HEAT: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { return new int[] { dst[0] == 0 ? 0 : Math.max(0, 255 - (255 - src[0]) * (255 - src[0]) / dst[0]), dst[1] == 0 ? 0 : Math.max(0, 255 - (255 - src[1]) * (255 - src[1]) / dst[1]), @@ -534,13 +531,12 @@ public final class BlendComposite implements Composite { case HUE: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { float[] srcHSL = new float[3]; RGBtoHSL(src[0], src[1], src[2], srcHSL); float[] dstHSL = new float[3]; RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); - int[] result = new int[4]; HSLtoRGB(srcHSL[0], dstHSL[1], dstHSL[2], result); result[3] = Math.min(255, src[3] + dst[3]); @@ -550,7 +546,7 @@ public final class BlendComposite implements Composite { case INVERSE_COLOR_BURN: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { return new int[] { dst[0] == 0 ? 0 : Math.max(0, 255 - (((255 - src[0]) << 8) / dst[0])), @@ -565,7 +561,7 @@ public final class BlendComposite implements Composite { case INVERSE_COLOR_DODGE: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { return new int[] { dst[0] == 255 ? 255 : Math.min((src[0] << 8) / (255 - dst[0]), 255), @@ -580,25 +576,23 @@ public final class BlendComposite implements Composite { case LIGHTEN: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { - return new int[] { - Math.max(src[0], dst[0]), - Math.max(src[1], dst[1]), - Math.max(src[2], dst[2]), - Math.min(255, src[3] + dst[3]) - }; + public int[] blend(int[] src, int[] dst, int[] result) { + for (int i = 0; i < 3; i++) { + result[i] = Math.max(src[i], dst[i]); + } + result[3] = Math.min(255, src[3] + dst[3]); + return result; } }; case LUMINOSITY: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { float[] srcHSL = new float[3]; RGBtoHSL(src[0], src[1], src[2], srcHSL); float[] dstHSL = new float[3]; RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); - int[] result = new int[4]; HSLtoRGB(dstHSL[0], dstHSL[1], srcHSL[2], result); result[3] = Math.min(255, src[3] + dst[3]); @@ -608,19 +602,18 @@ public final class BlendComposite implements Composite { case MULTIPLY: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { - return new int[] { - (src[0] * dst[0]) >> 8, - (src[1] * dst[1]) >> 8, - (src[2] * dst[2]) >> 8, - Math.min(255, src[3] + dst[3]) - }; + public int[] blend(int[] src, int[] dst, int[] result) { + for (int i = 0; i < 3; i++) { + result[i] = (src[i] * dst[i]) >> 8; + } + result[3] = Math.min(255, src[3] + dst[3]); + return result; } }; case NEGATION: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { return new int[] { 255 - Math.abs(255 - dst[0] - src[0]), 255 - Math.abs(255 - dst[1] - src[1]), @@ -632,22 +625,19 @@ public final class BlendComposite implements Composite { case OVERLAY: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { - return new int[] { - dst[0] < 128 ? dst[0] * src[0] >> 7 : - 255 - ((255 - dst[0]) * (255 - src[0]) >> 7), - dst[1] < 128 ? dst[1] * src[1] >> 7 : - 255 - ((255 - dst[1]) * (255 - src[1]) >> 7), - dst[2] < 128 ? dst[2] * src[2] >> 7 : - 255 - ((255 - dst[2]) * (255 - src[2]) >> 7), - Math.min(255, src[3] + dst[3]) - }; + public int[] blend(int[] src, int[] dst, int[] result) { + for (int i = 0; i < 3; i++) { + result[i] = dst[i] < 128 ? dst[i] * src[i] >> 7 : + 255 - ((255 - dst[i]) * (255 - src[i]) >> 7); + } + result[3] = Math.min(255, src[3] + dst[3]); + return result; } }; case RED: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { return new int[] { src[0], dst[1], @@ -659,7 +649,7 @@ public final class BlendComposite implements Composite { case REFLECT: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { return new int[] { src[0] == 255 ? 255 : Math.min(255, dst[0] * dst[0] / (255 - src[0])), src[1] == 255 ? 255 : Math.min(255, dst[1] * dst[1] / (255 - src[1])), @@ -671,13 +661,12 @@ public final class BlendComposite implements Composite { case SATURATION: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { float[] srcHSL = new float[3]; RGBtoHSL(src[0], src[1], src[2], srcHSL); float[] dstHSL = new float[3]; RGBtoHSL(dst[0], dst[1], dst[2], dstHSL); - int[] result = new int[4]; HSLtoRGB(dstHSL[0], srcHSL[1], dstHSL[2], result); result[3] = Math.min(255, src[3] + dst[3]); @@ -687,7 +676,7 @@ public final class BlendComposite implements Composite { case SCREEN: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { return new int[] { 255 - ((255 - src[0]) * (255 - dst[0]) >> 8), 255 - ((255 - src[1]) * (255 - dst[1]) >> 8), @@ -699,7 +688,7 @@ public final class BlendComposite implements Composite { case SOFT_BURN: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { return new int[] { dst[0] + src[0] < 256 ? (dst[0] == 255 ? 255 : @@ -720,7 +709,7 @@ public final class BlendComposite implements Composite { case SOFT_DODGE: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { return new int[] { dst[0] + src[0] < 256 ? (src[0] == 255 ? 255 : @@ -743,7 +732,7 @@ public final class BlendComposite implements Composite { case STAMP: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { return new int[] { Math.max(0, Math.min(255, dst[0] + 2 * src[0] - 256)), Math.max(0, Math.min(255, dst[1] + 2 * src[1] - 256)), @@ -755,7 +744,7 @@ public final class BlendComposite implements Composite { case SUBTRACT: return new Blender() { @Override - public int[] blend(int[] src, int[] dst) { + public int[] blend(int[] src, int[] dst, int[] result) { return new int[] { Math.max(0, src[0] + dst[0] - 256), Math.max(0, src[1] + dst[1] - 256), From 4f8ce228195be123eeb9b5f066a6abc92b8d923a Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Wed, 9 Jul 2014 17:17:02 -0700 Subject: [PATCH 08/75] Fix style resolution for styles with '.' Bug: b.android.com/72698 Change-Id: I96fb549cf5787d9e8cde2245524a8173471a60ca --- .../bridge/android/BridgeContext.java | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index efd55bf292aec..bb67a1a359cbc 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -619,39 +619,36 @@ public final class BridgeContext extends Context { } if (value != null) { - if ((value.getFirst() == ResourceType.STYLE) - || (value.getFirst() == ResourceType.ATTR)) { - // look for the style in the current theme, and its parent: - ResourceValue item = mRenderResources.findItemInTheme(value.getSecond(), + if ((value.getFirst() == ResourceType.STYLE)) { + // look for the style in all resources: + StyleResourceValue item = mRenderResources.getStyle(value.getSecond(), isFrameworkRes); if (item != null) { - if (item instanceof StyleResourceValue) { - if (defaultPropMap != null) { - defaultPropMap.put("style", item.getName()); - } - - defStyleValues = (StyleResourceValue)item; + if (defaultPropMap != null) { + defaultPropMap.put("style", item.getName()); } + + defStyleValues = item; } else { Bridge.getLog().error(null, String.format( "Style with id 0x%x (resolved to '%s') does not exist.", defStyleRes, value.getSecond()), - null /*data*/); + null); } } else { Bridge.getLog().error(null, String.format( - "Resouce id 0x%x is not of type STYLE (instead %s)", + "Resource id 0x%x is not of type STYLE (instead %s)", defStyleRes, value.getFirst().toString()), - null /*data*/); + null); } } else { Bridge.getLog().error(null, String.format( "Failed to find style with id 0x%x in current theme", defStyleRes), - null /*data*/); + null); } } From 2db2b9aa362577853a21d2ec89446803d63f359b Mon Sep 17 00:00:00 2001 From: Henrik Baard Date: Thu, 10 Jul 2014 08:46:39 +0200 Subject: [PATCH 09/75] Logging in LocalTransport on as default The logging is LocalTransport.java is enabled by default. Change default to false to remove logs, as this class states that it is only for debug. Change-Id: Iae63c7b62edbd503c606f0aee671a9579e78843d --- core/java/com/android/internal/backup/LocalTransport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index 494bc7898e93d..26433d2da3e91 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -47,7 +47,7 @@ import java.util.ArrayList; public class LocalTransport extends IBackupTransport.Stub { private static final String TAG = "LocalTransport"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private static final String TRANSPORT_DIR_NAME = "com.android.internal.backup.LocalTransport"; From 7be75338a3c2be073eee934754ca5a4aeaab85ad Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Fri, 11 Jul 2014 11:50:24 +0100 Subject: [PATCH 10/75] Allow 3 letter language codes in InputMethodUtils. Replace locale.substring(0, 2) with a function that always returns the first component of the locale (assumed to be the language). bug: 10090157 (cherry picked from commit cefc79c6b18bb2e824c299e7b9e212071e5ebc43) Change-Id: I56b02dd0c6dc222daa3ceade841a30879449bb4a --- .../inputmethod/InputMethodUtils.java | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java index 63d018fce01c3..68260d2f0cb74 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java +++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java @@ -262,6 +262,7 @@ public class InputMethodUtils { final List subtypes = InputMethodUtils.getSubtypes(imi); final String systemLocale = res.getConfiguration().locale.toString(); if (TextUtils.isEmpty(systemLocale)) return new ArrayList(); + final String systemLanguage = res.getConfiguration().locale.getLanguage(); final HashMap applicableModeAndSubtypesMap = new HashMap(); final int N = subtypes.size(); @@ -282,15 +283,22 @@ public class InputMethodUtils { final InputMethodSubtype subtype = subtypes.get(i); final String locale = subtype.getLocale(); final String mode = subtype.getMode(); + final String language = getLanguageFromLocaleString(locale); // When system locale starts with subtype's locale, that subtype will be applicable - // for system locale + // for system locale. We need to make sure the languages are the same, to prevent + // locales like "fil" (Filipino) being matched by "fi" (Finnish). + // // For instance, it's clearly applicable for cases like system locale = en_US and // subtype = en, but it is not necessarily considered applicable for cases like system // locale = en and subtype = en_US. + // // We just call systemLocale.startsWith(locale) in this function because there is no // need to find applicable subtypes aggressively unlike // findLastResortApplicableSubtypeLocked. - if (systemLocale.startsWith(locale)) { + // + // TODO: This check is broken. It won't take scripts into account and doesn't + // account for the mandatory conversions performed by Locale#toString. + if (language.equals(systemLanguage) && systemLocale.startsWith(locale)) { final InputMethodSubtype applicableSubtype = applicableModeAndSubtypesMap.get(mode); // If more applicable subtypes are contained, skip. if (applicableSubtype != null) { @@ -334,6 +342,18 @@ public class InputMethodUtils { return InputMethodSubtype.sort(context, 0, imi, enabledSubtypes); } + /** + * Returns the language component of a given locale string. + */ + private static String getLanguageFromLocaleString(String locale) { + final int idx = locale.indexOf('_'); + if (idx < 0) { + return locale; + } else { + return locale.substring(0, idx); + } + } + /** * If there are no selected subtypes, tries finding the most applicable one according to the * given locale. @@ -353,7 +373,7 @@ public class InputMethodUtils { if (TextUtils.isEmpty(locale)) { locale = res.getConfiguration().locale.toString(); } - final String language = locale.substring(0, 2); + final String language = getLanguageFromLocaleString(locale); boolean partialMatchFound = false; InputMethodSubtype applicableSubtype = null; InputMethodSubtype firstMatchedModeSubtype = null; @@ -361,6 +381,7 @@ public class InputMethodUtils { for (int i = 0; i < N; ++i) { InputMethodSubtype subtype = subtypes.get(i); final String subtypeLocale = subtype.getLocale(); + final String subtypeLanguage = getLanguageFromLocaleString(subtypeLocale); // An applicable subtype should match "mode". If mode is null, mode will be ignored, // and all subtypes with all modes can be candidates. if (mode == null || subtypes.get(i).getMode().equalsIgnoreCase(mode)) { @@ -371,7 +392,7 @@ public class InputMethodUtils { // Exact match (e.g. system locale is "en_US" and subtype locale is "en_US") applicableSubtype = subtype; break; - } else if (!partialMatchFound && subtypeLocale.startsWith(language)) { + } else if (!partialMatchFound && language.equals(subtypeLanguage)) { // Partial match (e.g. system locale is "en_US" and subtype locale is "en") applicableSubtype = subtype; partialMatchFound = true; From 5b885e6c8946c6d0564ab33fb67bdc9afb90fa6e Mon Sep 17 00:00:00 2001 From: Jinsuk Kim Date: Mon, 14 Jul 2014 07:39:32 +0900 Subject: [PATCH 11/75] Make sure to finish routing control action After HdmiControlService performs routing control action, make sure it is finished all the time. Change-Id: I38e130ee5d8712a18aac7a46cad0950a23713469 --- .../android/server/hdmi/RoutingControlAction.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java index 46dc453115948..8296f695c1fcc 100644 --- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java +++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java @@ -114,9 +114,8 @@ final class RoutingControlAction extends FeatureAction { tv().updateActivePortId(tv().pathToPortId(mCurrentRoutingPath)); } } - invokeCallback(HdmiControlManager.RESULT_SUCCESS); - finish(); - } + finishWithCallback(HdmiControlManager.RESULT_SUCCESS); + } private int getTvPowerStatus() { return tv().getPowerStatus(); @@ -132,6 +131,11 @@ final class RoutingControlAction extends FeatureAction { mCurrentRoutingPath)); } + private void finishWithCallback(int result) { + invokeCallback(result); + finish(); + } + @Override public void handleTimerEvent(int timeoutState) { if (mState != timeoutState || mState == STATE_NONE) { @@ -152,6 +156,7 @@ final class RoutingControlAction extends FeatureAction { }); } else { tv().updateActivePortId(tv().pathToPortId(mCurrentRoutingPath)); + finishWithCallback(HdmiControlManager.RESULT_SUCCESS); } return; case STATE_WAIT_FOR_REPORT_POWER_STATUS: @@ -159,8 +164,7 @@ final class RoutingControlAction extends FeatureAction { tv().updateActivePortId(tv().pathToPortId(mCurrentRoutingPath)); sendSetStreamPath(); } - invokeCallback(HdmiControlManager.RESULT_SUCCESS); - finish(); + finishWithCallback(HdmiControlManager.RESULT_SUCCESS); return; } } @@ -177,6 +181,7 @@ final class RoutingControlAction extends FeatureAction { } else { tv().updateActivePortId(tv().pathToPortId(mCurrentRoutingPath)); sendSetStreamPath(); + finishWithCallback(HdmiControlManager.RESULT_SUCCESS); } } From 11eaf0017431e1276303d8259dab859eb72bdf2a Mon Sep 17 00:00:00 2001 From: Jinsuk Kim Date: Mon, 14 Jul 2014 08:20:47 +0900 Subject: [PATCH 12/75] Shortens the key length in HdmiControlService The key length for SystemProperties should not be over 31 Bug: 16274152 Change-Id: I25ffaf2fc502819a7aea12372717cd00497fd3be --- services/core/java/com/android/server/hdmi/Constants.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index 30281008d53b2..384cd4a0536ec 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -203,8 +203,8 @@ final class Constants { // in config.xml to allow customization. static final int IRT_MS = 300; - static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "hdmi_cec.preferred_address.playback"; - static final String PROPERTY_PREFERRED_ADDRESS_TV = "hdmi_cec.preferred_address.tv"; + static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "hdmi_cec.prefaddr.playback"; + static final String PROPERTY_PREFERRED_ADDRESS_TV = "hdmi_cec.prefaddr.tv"; private Constants() { /* cannot be instantiated */ } } From 7a48148a3d958d9e9e27049e9351b7c4e17c814c Mon Sep 17 00:00:00 2001 From: Dongwon Kang Date: Fri, 4 Jul 2014 09:32:40 +0900 Subject: [PATCH 13/75] Implement onVideoAvailable/Unavailable in TIF. Use cases: - VIDEO_UNAVAILABLE_REASON_TUNE: to show spinner on channel change. - VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL: to show a message and spinner on weak TV signal. - VIDEO_UNAVAILABLE_REASON_BUFFERING: to show spinner on buffering. - VIDEO_UNAVAILABLE_REASON_UNKNOWN: to show spinner. Bug: 14126559 Change-Id: Ide2b68c74bd96d4ea1b9d85ab099a277253d05c7 --- api/current.txt | 8 ++ .../java/android/media/tv/ITvInputClient.aidl | 2 + .../media/tv/ITvInputSessionCallback.aidl | 2 + .../java/android/media/tv/TvInputManager.java | 83 +++++++++++++++++++ .../java/android/media/tv/TvInputService.java | 54 +++++++++++- media/java/android/media/tv/TvView.java | 41 +++++++++ .../server/tv/TvInputManagerService.java | 34 ++++++++ 7 files changed, 223 insertions(+), 1 deletion(-) diff --git a/api/current.txt b/api/current.txt index c319ce8b8ccdc..8868c8565bf86 100644 --- a/api/current.txt +++ b/api/current.txt @@ -16166,6 +16166,10 @@ package android.media.tv { public final class TvInputManager { method public boolean getAvailability(java.lang.String); method public java.util.List getTvInputList(); + field public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; // 0x3 + field public static final int VIDEO_UNAVAILABLE_REASON_TUNE = 1; // 0x1 + field public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0; // 0x0 + field public static final int VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2; // 0x2 } public static abstract class TvInputManager.TvInputListener { @@ -16185,6 +16189,8 @@ package android.media.tv { ctor public TvInputService.Session(); method public void dispatchChannelRetuned(android.net.Uri); method public void dispatchTrackInfoChanged(java.util.List); + method public void dispatchVideoAvailable(); + method public void dispatchVideoUnavailable(int); method public android.view.View onCreateOverlayView(); method public boolean onGenericMotionEvent(android.view.MotionEvent); method public boolean onKeyDown(int, android.view.KeyEvent); @@ -16263,6 +16269,8 @@ package android.media.tv { method public void onError(java.lang.String, int); method public void onTrackInfoChanged(java.lang.String, java.util.List); method public void onVideoSizeChanged(java.lang.String, int, int); + method public void onVideoAvailable(java.lang.String); + method public void onVideoUnavailable(java.lang.String, int); } } diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl index 27360eda478c7..cac8a14687e6e 100644 --- a/media/java/android/media/tv/ITvInputClient.aidl +++ b/media/java/android/media/tv/ITvInputClient.aidl @@ -35,4 +35,6 @@ oneway interface ITvInputClient { void onSessionEvent(in String name, in Bundle args, int seq); void onChannelRetuned(in Uri channelUri, int seq); void onTrackInfoChanged(in List tracks, int seq); + void onVideoAvailable(int seq); + void onVideoUnavailable(int reason, int seq); } diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl index 4d7205916b45d..e0036e1366b8d 100644 --- a/media/java/android/media/tv/ITvInputSessionCallback.aidl +++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl @@ -31,4 +31,6 @@ oneway interface ITvInputSessionCallback { void onSessionEvent(in String name, in Bundle args); void onChannelRetuned(in Uri channelUri); void onTrackInfoChanged(in List tracks); + void onVideoAvailable(); + void onVideoUnavailable(int reason); } diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index f1a63adc1cf64..867b0db6ab6c1 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -47,6 +47,24 @@ import java.util.Map; public final class TvInputManager { private static final String TAG = "TvInputManager"; + /** + * A generic reason. Video is not available due to an unspecified error. + */ + public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0; + /** + * Video is not available because the TV input is tuning to another channel. + */ + public static final int VIDEO_UNAVAILABLE_REASON_TUNE = 1; + /** + * Video is not available due to the weak TV signal. + */ + public static final int VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2; + /** + * Video is not available because the TV input stopped the playback temporarily to buffer more + * data. + */ + public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; + private final ITvInputManager mService; // A mapping from an input to the list of its TvInputListenerRecords. @@ -107,6 +125,29 @@ public final class TvInputManager { public void onTrackInfoChanged(Session session, List tracks) { } + /** + * This is called when the video is available, so the TV input starts the playback. + * + * @param session A {@link TvInputManager.Session} associated with this callback + */ + public void onVideoAvailable(Session session) { + } + + /** + * This is called when the video is not available, so the TV input stops the playback. + * + * @param session A {@link TvInputManager.Session} associated with this callback + * @param reason The reason why the TV input stopped the playback: + *
    + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNE} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING} + *
+ */ + public void onVideoUnavailable(Session session, int reason) { + } + /** * This is called when a custom event has been sent from this session. * @@ -168,6 +209,24 @@ public final class TvInputManager { }); } + public void postVideoAvailable() { + mHandler.post(new Runnable() { + @Override + public void run() { + mSessionCallback.onVideoAvailable(mSession); + } + }); + } + + public void postVideoUnavailable(final int reason) { + mHandler.post(new Runnable() { + @Override + public void run() { + mSessionCallback.onVideoUnavailable(mSession, reason); + } + }); + } + public void postSessionEvent(final String eventType, final Bundle eventArgs) { mHandler.post(new Runnable() { @Override @@ -279,6 +338,30 @@ public final class TvInputManager { } } + @Override + public void onVideoAvailable(int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq " + seq); + return; + } + record.postVideoAvailable(); + } + } + + @Override + public void onVideoUnavailable(int reason, int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq " + seq); + return; + } + record.postVideoUnavailable(reason); + } + } + @Override public void onSessionEvent(String eventType, Bundle eventArgs, int seq) { synchronized (mSessionCallbackRecordMap) { diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index ba12e2e224590..a994f5472271d 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -266,6 +266,56 @@ public abstract class TvInputService extends Service { }); } + /** + * Informs the application that video is available and the playback of the TV stream has + * been started. + */ + public void dispatchVideoAvailable() { + mHandler.post(new Runnable() { + @Override + public void run() { + try { + if (DEBUG) Log.d(TAG, "dispatchVideoAvailable"); + mSessionCallback.onVideoAvailable(); + } catch (RemoteException e) { + Log.w(TAG, "error in dispatchVideoAvailable"); + } + } + }); + } + + /** + * Informs the application that video is not available, so the TV input cannot continue + * playing the TV stream. + * + * @param reason The reason why the TV input stopped the playback: + *
    + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNE} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING} + *
+ */ + public void dispatchVideoUnavailable(final int reason) { + if (reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN + && reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNE + && reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL + && reason != TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING) { + throw new IllegalArgumentException("Unknown reason: " + reason); + } + mHandler.post(new Runnable() { + @Override + public void run() { + try { + if (DEBUG) Log.d(TAG, "dispatchVideoUnavailable"); + mSessionCallback.onVideoUnavailable(reason); + } catch (RemoteException e) { + Log.w(TAG, "error in dispatchVideoUnavailable"); + } + } + }); + } + /** * Called when the session is released. */ @@ -289,7 +339,9 @@ public abstract class TvInputService extends Service { public abstract void onSetStreamVolume(float volume); /** - * Tunes to a given channel. + * Tunes to a given channel. When the video is available, {@link #dispatchVideoAvailable()} + * should be called. Also, {@link #dispatchVideoUnavailable(int)} should be called when the + * TV input cannot continue playing the given channel. * * @param channelUri The URI of the channel. * @return {@code true} the tuning was successful, {@code false} otherwise. diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java index 7eb27b6c6cd0c..4ac1ba422a7a6 100644 --- a/media/java/android/media/tv/TvView.java +++ b/media/java/android/media/tv/TvView.java @@ -524,6 +524,29 @@ public class TvView extends ViewGroup { public void onTrackInfoChanged(String inputId, List tracks) { } + /** + * This is called when the video is available, so the TV input starts the playback. + * + * @param inputId The ID of the TV input bound to this view. + */ + public void onVideoAvailable(String inputId) { + } + + /** + * This is called when the video is not available, so the TV input stops the playback. + * + * @param inputId The ID of the TV input bound to this view. + * @param reason The reason why the TV input stopped the playback: + *
    + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNE} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL} + *
  • {@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING} + *
+ */ + public void onVideoUnavailable(String inputId, int reason) { + } + /** * This is invoked when a custom event from the bound TV input is sent to this view. * @@ -625,6 +648,24 @@ public class TvView extends ViewGroup { } } + public void onVideoAvailable(Session session) { + if (DEBUG) { + Log.d(TAG, "onVideoAvailable()"); + } + if (mListener != null) { + mListener.onVideoAvailable(mInputId); + } + } + + public void onVideoUnavailable(Session session, int reason) { + if (DEBUG) { + Log.d(TAG, "onVideoUnavailable(" + reason + ")"); + } + if (mListener != null) { + mListener.onVideoUnavailable(mInputId, reason); + } + } + @Override public void onSessionEvent(TvInputManager.Session session, String eventType, Bundle eventArgs) { diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index fac78a9be3e25..5e95af4368718 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -471,6 +471,40 @@ public final class TvInputManagerService extends SystemService { } } + @Override + public void onVideoAvailable() { + synchronized (mLock) { + if (DEBUG) { + Slog.d(TAG, "onVideoAvailable()"); + } + if (sessionState.mSession == null || sessionState.mClient == null) { + return; + } + try { + sessionState.mClient.onVideoAvailable(sessionState.mSeq); + } catch (RemoteException e) { + Slog.e(TAG, "error in onVideoAvailable"); + } + } + } + + @Override + public void onVideoUnavailable(int reason) { + synchronized (mLock) { + if (DEBUG) { + Slog.d(TAG, "onVideoUnavailable(" + reason + ")"); + } + if (sessionState.mSession == null || sessionState.mClient == null) { + return; + } + try { + sessionState.mClient.onVideoUnavailable(reason, sessionState.mSeq); + } catch (RemoteException e) { + Slog.e(TAG, "error in onVideoUnavailable"); + } + } + } + @Override public void onSessionEvent(String eventType, Bundle eventArgs) { synchronized (mLock) { From 0afb70c66de1fe6b1308656938d4355e707412e5 Mon Sep 17 00:00:00 2001 From: John Spurlock Date: Sun, 13 Jul 2014 16:07:57 -0400 Subject: [PATCH 14/75] QS: Cellular usage warning marker + policy cycle. Add warning marker to graph from policy, and use the cycle day from policy to determine the usage period. Change-Id: I31711eea6a0bf54ad0eb5841d2aa9b8e5927ed58 --- packages/SystemUI/res/values/colors.xml | 1 + packages/SystemUI/res/values/strings.xml | 2 + .../android/systemui/qs/DataUsageGraph.java | 62 +++++++++---------- .../systemui/qs/tiles/CellularTile.java | 20 +++--- .../policy/MobileDataController.java | 58 ++++++++++++++--- .../statusbar/policy/NetworkController.java | 1 - 6 files changed, 95 insertions(+), 49 deletions(-) diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index bae7ed730815e..3d53f9cc2b248 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -44,6 +44,7 @@ #24B0BEC5 #99FFFFFF #33FFFFFF + #FFFFFFFF #33FFFFFF diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 6a7b450a3c812..b79dbbeb77f0a 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -572,6 +572,8 @@ %s used %s limit + + %s warning No recent apps diff --git a/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java b/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java index fa11af61fe7c0..d55ceaa544af7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java +++ b/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java @@ -28,34 +28,34 @@ import com.android.systemui.R; public class DataUsageGraph extends View { - private final int mBackgroundColor; private final int mTrackColor; private final int mUsageColor; private final int mOverlimitColor; + private final int mWarningColor; private final int mMarkerWidth; private final RectF mTmpRect = new RectF(); private final Paint mTmpPaint = new Paint(); - private long mMaxLevel = 1; private long mLimitLevel; private long mWarningLevel; private long mUsageLevel; + private long mMaxLevel; public DataUsageGraph(Context context, AttributeSet attrs) { super(context, attrs); final Resources res = context.getResources(); - mBackgroundColor = res.getColor(R.color.system_primary_color); mTrackColor = res.getColor(R.color.data_usage_graph_track); mUsageColor = res.getColor(R.color.system_accent_color); mOverlimitColor = res.getColor(R.color.system_warning_color); + mWarningColor = res.getColor(R.color.data_usage_graph_warning); mMarkerWidth = res.getDimensionPixelSize(R.dimen.data_usage_graph_marker_width); } - public void setLevels(long maxLevel, long limitLevel, long warningLevel, long usageLevel) { - mMaxLevel = Math.max(maxLevel, 1); - mLimitLevel = limitLevel; - mWarningLevel = warningLevel; - mUsageLevel = usageLevel; + public void setLevels(long limitLevel, long warningLevel, long usageLevel) { + mLimitLevel = Math.max(0, limitLevel); + mWarningLevel = Math.max(0, warningLevel); + mUsageLevel = Math.max(0, usageLevel); + mMaxLevel = Math.max(Math.max(Math.max(mLimitLevel, mWarningLevel), mUsageLevel), 1); postInvalidate(); } @@ -68,21 +68,22 @@ public class DataUsageGraph extends View { final int w = getWidth(); final int h = getHeight(); - // draw track - r.set(0, 0, w, h); - p.setColor(mTrackColor); - canvas.drawRect(r, p); - - final boolean hasLimit = mLimitLevel > 0; - final boolean overLimit = hasLimit && mUsageLevel > mLimitLevel; - - final long maxLevel = hasLimit ? Math.max(mUsageLevel, mLimitLevel) : mMaxLevel; - final long usageLevel = hasLimit ? Math.min(mUsageLevel, mLimitLevel) : mUsageLevel; - float usageRight = w * (usageLevel / (float) maxLevel); + final boolean overLimit = mLimitLevel > 0 && mUsageLevel > mLimitLevel; + float usageRight = w * (mUsageLevel / (float) mMaxLevel); if (overLimit) { - usageRight -= (mMarkerWidth / 2); - usageRight = Math.min(usageRight, w - mMarkerWidth * 2); - usageRight = Math.max(usageRight, mMarkerWidth); + // compute the gap + usageRight = w * (mLimitLevel / (float) mMaxLevel) - (mMarkerWidth / 2); + usageRight = Math.min(Math.max(usageRight, mMarkerWidth), w - mMarkerWidth * 2); + + // draw overlimit + r.set(usageRight + mMarkerWidth, 0, w, h); + p.setColor(mOverlimitColor); + canvas.drawRect(r, p); + } else { + // draw track + r.set(0, 0, w, h); + p.setColor(mTrackColor); + canvas.drawRect(r, p); } // draw usage @@ -90,16 +91,11 @@ public class DataUsageGraph extends View { p.setColor(mUsageColor); canvas.drawRect(r, p); - if (overLimit) { - // draw gap - r.set(usageRight, 0, usageRight + mMarkerWidth, h); - p.setColor(mBackgroundColor); - canvas.drawRect(r, p); - - // draw overlimit - r.set(usageRight + mMarkerWidth, 0, w, h); - p.setColor(mOverlimitColor); - canvas.drawRect(r, p); - } + // draw warning marker + float warningLeft = w * (mWarningLevel / (float) mMaxLevel) - mMarkerWidth / 2; + warningLeft = Math.min(Math.max(warningLeft, 0), w - mMarkerWidth); + r.set(warningLeft, 0, warningLeft + mMarkerWidth, h); + p.setColor(mWarningColor); + canvas.drawRect(r, p); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index 1f12b2af1d8c5..d76a2fedca108 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -216,21 +216,27 @@ public class CellularTile extends QSTile { final DataUsageInfo info = mController.getDataUsageInfo(); if (info == null) return v; final Resources res = mContext.getResources(); - int titleId; - long bytes; + final int titleId; + final long bytes; int usageColor = R.color.system_accent_color; - String top = null, bottom = null; - if (info.limitLevel <= 0) { // no limit + final String top; + String bottom = null; + if (info.usageLevel < info.warningLevel || info.limitLevel <= 0) { + // under warning, or no limit titleId = R.string.quick_settings_cellular_detail_data_usage; bytes = info.usageLevel; - } else if (info.usageLevel <= info.limitLevel) { // under limit + top = res.getString(R.string.quick_settings_cellular_detail_data_warning, + formatBytes(info.warningLevel)); + } else if (info.usageLevel <= info.limitLevel) { + // over warning, under limit titleId = R.string.quick_settings_cellular_detail_remaining_data; bytes = info.limitLevel - info.usageLevel; top = res.getString(R.string.quick_settings_cellular_detail_data_used, formatBytes(info.usageLevel)); bottom = res.getString(R.string.quick_settings_cellular_detail_data_limit, formatBytes(info.limitLevel)); - } else { // over limit + } else { + // over limit titleId = R.string.quick_settings_cellular_detail_over_limit; bytes = info.usageLevel - info.limitLevel; top = res.getString(R.string.quick_settings_cellular_detail_data_used, @@ -246,7 +252,7 @@ public class CellularTile extends QSTile { usage.setText(formatBytes(bytes)); usage.setTextColor(res.getColor(usageColor)); final DataUsageGraph graph = (DataUsageGraph) v.findViewById(R.id.usage_graph); - graph.setLevels(info.maxLevel, info.limitLevel, info.warningLevel, info.usageLevel); + graph.setLevels(info.limitLevel, info.warningLevel, info.usageLevel); final TextView carrier = (TextView) v.findViewById(R.id.usage_carrier_text); carrier.setText(info.carrier); final TextView period = (TextView) v.findViewById(R.id.usage_period_text); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java index f2dfe05899e74..33d68bf37f09b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java @@ -20,6 +20,8 @@ import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.NetworkStatsHistory.FIELD_RX_BYTES; import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; import static android.telephony.TelephonyManager.SIM_STATE_READY; +import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; +import static android.text.format.DateUtils.FORMAT_SHOW_DATE; import android.content.Context; import android.net.ConnectivityManager; @@ -33,19 +35,23 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.telephony.TelephonyManager; import android.text.format.DateUtils; +import android.text.format.Time; import android.util.Log; import com.android.systemui.statusbar.policy.NetworkController.DataUsageInfo; -import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Locale; public class MobileDataController { private static final String TAG = "MobileDataController"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final SimpleDateFormat MMM_D = new SimpleDateFormat("MMM d"); + private static final long DEFAULT_WARNING_LEVEL = 2L * 1024 * 1024 * 1024; private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES; + private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50); + private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter( + PERIOD_BUILDER, Locale.getDefault()); private final Context mContext; private final TelephonyManager mTelephonyManager; @@ -87,6 +93,13 @@ public class MobileDataController { return null; } + private static Time addMonth(Time t, int months) { + final Time rt = new Time(t); + rt.set(t.monthDay, t.month + months, t.year); + rt.normalize(false); + return rt; + } + public DataUsageInfo getDataUsageInfo() { final String subscriberId = getActiveSubscriberId(mContext); if (subscriberId == null) { @@ -101,9 +114,28 @@ public class MobileDataController { try { final NetworkStatsHistory history = mSession.getHistoryForNetwork(template, FIELDS); final long now = System.currentTimeMillis(); - // period = last 4 wks for now - final long start = now - DateUtils.WEEK_IN_MILLIS * 4; - final long end = now; + final long start, end; + if (policy != null && policy.cycleDay > 0) { + // period = determined from cycleDay + if (DEBUG) Log.d(TAG, "Cycle day=" + policy.cycleDay + " tz=" + + policy.cycleTimezone); + final Time nowTime = new Time(policy.cycleTimezone); + nowTime.setToNow(); + final Time policyTime = new Time(nowTime); + policyTime.set(policy.cycleDay, policyTime.month, policyTime.year); + policyTime.normalize(false); + if (nowTime.after(policyTime)) { + start = policyTime.toMillis(false); + end = addMonth(policyTime, 1).toMillis(false); + } else { + start = addMonth(policyTime, -1).toMillis(false); + end = policyTime.toMillis(false); + } + } else { + // period = last 4 wks + end = now; + start = now - DateUtils.WEEK_IN_MILLIS * 4; + } final long callStart = System.currentTimeMillis(); final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null); final long callEnd = System.currentTimeMillis(); @@ -115,12 +147,13 @@ public class MobileDataController { } final long totalBytes = entry.rxBytes + entry.txBytes; final DataUsageInfo usage = new DataUsageInfo(); - usage.maxLevel = (long) (totalBytes / .4); usage.usageLevel = totalBytes; - usage.period = MMM_D.format(new Date(start)) + " - " + MMM_D.format(new Date(end)); + usage.period = formatDateRange(start, end); if (policy != null) { usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0; usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0; + } else { + usage.warningLevel = DEFAULT_WARNING_LEVEL; } return usage; } catch (RemoteException e) { @@ -178,6 +211,15 @@ public class MobileDataController { return actualSubscriberId; } + private String formatDateRange(long start, long end) { + final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH; + synchronized (PERIOD_BUILDER) { + PERIOD_BUILDER.setLength(0); + return DateUtils.formatDateRange(mContext, PERIOD_FORMATTER, start, end, flags, null) + .toString(); + } + } + public interface Callback { void onMobileDataEnabled(boolean enabled); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index d058bd0a51d92..6d8b4001bd0d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -61,7 +61,6 @@ public interface NetworkController { public static class DataUsageInfo { public String carrier; public String period; - public long maxLevel; public long limitLevel; public long warningLevel; public long usageLevel; From 891ec88577b3e47835416661f6d8b1466e182e99 Mon Sep 17 00:00:00 2001 From: Wenchao Tong Date: Wed, 18 Jun 2014 12:59:21 -0700 Subject: [PATCH 15/75] Changes in framework for WIFI_CREDENTIAL_CHANGE broadcast. * Only allow system app to send/receive WIFI_CREDENTIAL_CHANGE. * Define constants in WifiManager. Bug: 15703353 Change-Id: I62f582ac8cbfd8748c35d73daa2f733665cc0fa8 --- core/res/AndroidManifest.xml | 14 ++++++++++++ wifi/java/android/net/wifi/WifiManager.java | 25 ++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 4725cfbf39c6e..fc12a6ed5bd10 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -235,6 +235,7 @@ + @@ -805,6 +806,19 @@ android:description="@string/permdesc_changeWifiState" android:label="@string/permlab_changeWifiState" /> + + + + + + Date: Sun, 13 Jul 2014 17:43:31 -0700 Subject: [PATCH 16/75] Fixing some regressions - Removing some legacy code with task loading - Ensure that doze triggers are set correctly on configuration change - Adding WCAGv2 contrast checking for deciding which foreground color to use for labels - Bumping up the max thumbnail cache size slightly - Fixing case where visibility state was not correct if you toggle recents too quickly Change-Id: I5e954890d53948547842edce3c76c34d74e180a8 --- .../android/systemui/recents/Constants.java | 2 +- .../systemui/recents/RecentsActivity.java | 4 +- .../recents/misc/ReferenceCountedTrigger.java | 5 + .../systemui/recents/misc/Utilities.java | 39 +++-- .../recents/model/BitmapLruCache.java | 2 +- .../recents/model/DrawableLruCache.java | 2 +- .../recents/model/KeyStoreLruCache.java | 5 - .../recents/model/RecentsPackageMonitor.java | 2 +- .../recents/model/RecentsTaskLoader.java | 147 ++++++++---------- .../android/systemui/recents/model/Task.java | 6 +- .../systemui/recents/model/TaskStack.java | 2 +- .../systemui/recents/views/RecentsView.java | 16 -- .../systemui/recents/views/TaskBarView.java | 9 +- .../systemui/recents/views/TaskStackView.java | 89 ++++++----- .../views/TaskStackViewLayoutAlgorithm.java | 4 +- .../systemui/recents/views/TaskView.java | 48 +++--- 16 files changed, 174 insertions(+), 208 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java index 8a80b76edc14a..e7ac2e1ec5681 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java @@ -32,7 +32,7 @@ public class Constants { // Enables the filtering of tasks according to their grouping public static final boolean EnableTaskFiltering = false; // Enables clipping of tasks against each other - public static final boolean EnableTaskStackClipping = true; + public static final boolean EnableTaskStackClipping = false; // Enables tapping on the TaskBar to launch the task public static final boolean EnableTaskBarTouchEvents = true; // Enables app-info pane on long-pressing the icon diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 56de0be56042a..1e581c1daf4a9 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -458,8 +458,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView filter.addAction(ACTION_TOGGLE_RECENTS_ACTIVITY); filter.addAction(ACTION_START_ENTER_ANIMATION); registerReceiver(mServiceBroadcastReceiver, filter); - - mVisible = true; } @Override @@ -485,6 +483,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } }, 1); } + + mVisible = true; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java index 31825afa545ff..4c0ff481a2897 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java @@ -72,7 +72,12 @@ public class ReferenceCountedTrigger { /** Adds a runnable to the last-decrement runnables list. */ public void addLastDecrementRunnable(Runnable r) { + // To ensure that the last decrement always calls, we increment and decrement after setting + // the last decrement runnable + boolean ensureLastDecrement = (mCount == 0); + if (ensureLastDecrement) increment(); mLastDecRunnables.add(r); + if (ensureLastDecrement) decrement(); } /** Decrements the ref count */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java index bda195b548c00..607e155d92f6f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java @@ -16,16 +16,10 @@ package com.android.systemui.recents.misc; -import android.app.ActivityManager; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.ParcelFileDescriptor; import com.android.systemui.recents.RecentsConfiguration; -import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -73,22 +67,25 @@ public class Utilities { } } - /** Calculates the luminance-preserved greyscale of a given color. */ - public static int colorToGreyscale(int color) { - return Math.round(0.2126f * Color.red(color) + 0.7152f * Color.green(color) + - 0.0722f * Color.blue(color)); - } + /** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */ + public static float computeContrastBetweenColors(int bg, int fg) { + float bgR = Color.red(bg) / 255f; + float bgG = Color.green(bg) / 255f; + float bgB = Color.blue(bg) / 255f; + bgR = (bgR < 0.03928f) ? bgR / 12.92f : (float) Math.pow((bgR + 0.055f) / 1.055f, 2.4f); + bgG = (bgG < 0.03928f) ? bgG / 12.92f : (float) Math.pow((bgG + 0.055f) / 1.055f, 2.4f); + bgB = (bgB < 0.03928f) ? bgB / 12.92f : (float) Math.pow((bgB + 0.055f) / 1.055f, 2.4f); + float bgL = 0.2126f * bgR + 0.7152f * bgG + 0.0722f * bgB; + + float fgR = Color.red(fg) / 255f; + float fgG = Color.green(fg) / 255f; + float fgB = Color.blue(fg) / 255f; + fgR = (fgR < 0.03928f) ? fgR / 12.92f : (float) Math.pow((fgR + 0.055f) / 1.055f, 2.4f); + fgG = (fgG < 0.03928f) ? fgG / 12.92f : (float) Math.pow((fgG + 0.055f) / 1.055f, 2.4f); + fgB = (fgB < 0.03928f) ? fgB / 12.92f : (float) Math.pow((fgB + 0.055f) / 1.055f, 2.4f); + float fgL = 0.2126f * fgR + 0.7152f * fgG + 0.0722f * fgB; - /** Returns the ideal color to draw on top of a specified background color. */ - public static int getIdealColorForBackgroundColorGreyscale(int greyscale, int lightRes, - int darkRes) { - return (greyscale < 128) ? lightRes : darkRes; - } - /** Returns the ideal drawable to draw on top of a specified background color. */ - public static Drawable getIdealResourceForBackgroundColorGreyscale(int greyscale, - Drawable lightRes, - Drawable darkRes) { - return (greyscale < 128) ? lightRes : darkRes; + return Math.abs((fgL + 0.05f) / (bgL + 0.05f)); } /** Sets some private shadow properties. */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java index 1344729001c28..757c07fbb28db 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java @@ -29,6 +29,6 @@ class BitmapLruCache extends KeyStoreLruCache { @Override protected int computeSize(Bitmap b) { // The cache size will be measured in kilobytes rather than number of items - return b.getAllocationByteCount() / 1024; + return b.getAllocationByteCount(); } } \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java index 61d19da86d261..5b5035865d36f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java @@ -31,6 +31,6 @@ class DrawableLruCache extends KeyStoreLruCache { // The cache size will be measured in kilobytes rather than number of items // NOTE: this isn't actually correct, as the icon may be smaller int maxBytes = (d.getIntrinsicWidth() * d.getIntrinsicHeight() * 4); - return maxBytes / 1024; + return maxBytes; } } \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java index 3ccca9a2e9447..5f4fabeaa918b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java @@ -73,11 +73,6 @@ public class KeyStoreLruCache { return mCache.get(key); } - /** Gets the previous task key that matches the specified key. */ - final Task.TaskKey getKey(Task.TaskKey key) { - return mKeys.get(key); - } - /** Puts an entry in the cache for a specific key. */ final void put(Task.TaskKey key, V value) { mCache.put(key, value); diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java index 2d50659858f9f..2f1c1c43d200d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java @@ -45,7 +45,7 @@ public class RecentsPackageMonitor extends PackageMonitor { mSystemServicesProxy = new SystemServicesProxy(context); mCb = cb; try { - register(context, Looper.getMainLooper(), false); + register(context, Looper.getMainLooper(), true); } catch (IllegalStateException e) { e.printStackTrace(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java index 854ea1c8446bc..cbb8892dea94e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -27,38 +27,29 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.HandlerThread; import android.os.UserHandle; -import android.util.Pair; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.misc.Console; import com.android.systemui.recents.misc.SystemServicesProxy; -import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; /** A bitmap load queue */ class TaskResourceLoadQueue { ConcurrentLinkedQueue mQueue = new ConcurrentLinkedQueue(); - ConcurrentHashMap mForceLoadSet = - new ConcurrentHashMap(); - - static final Boolean sFalse = new Boolean(false); /** Adds a new task to the load queue */ - void addTask(Task t, boolean forceLoad) { + void addTask(Task t) { if (Console.Enabled) { Console.log(Constants.Log.App.TaskDataLoader, " [TaskResourceLoadQueue|addTask]"); } if (!mQueue.contains(t)) { mQueue.add(t); } - if (forceLoad) { - mForceLoadSet.put(t.key, new Boolean(true)); - } synchronized(this) { notifyAll(); } @@ -68,19 +59,11 @@ class TaskResourceLoadQueue { * Retrieves the next task from the load queue, as well as whether we want that task to be * force reloaded. */ - Pair nextTask() { + Task nextTask() { if (Console.Enabled) { Console.log(Constants.Log.App.TaskDataLoader, " [TaskResourceLoadQueue|nextTask]"); } - Task task = mQueue.poll(); - Boolean forceLoadTask = null; - if (task != null) { - forceLoadTask = mForceLoadSet.remove(task.key); - } - if (forceLoadTask == null) { - forceLoadTask = sFalse; - } - return new Pair(task, forceLoadTask); + return mQueue.poll(); } /** Removes a task from the load queue */ @@ -89,7 +72,6 @@ class TaskResourceLoadQueue { Console.log(Constants.Log.App.TaskDataLoader, " [TaskResourceLoadQueue|removeTask]"); } mQueue.remove(t); - mForceLoadSet.remove(t.key); } /** Clears all the tasks from the load queue */ @@ -98,7 +80,6 @@ class TaskResourceLoadQueue { Console.log(Constants.Log.App.TaskDataLoader, " [TaskResourceLoadQueue|clearTasks]"); } mQueue.clear(); - mForceLoadSet.clear(); } /** Returns whether the load queue is empty */ @@ -119,19 +100,20 @@ class TaskResourceLoader implements Runnable { DrawableLruCache mApplicationIconCache; BitmapLruCache mThumbnailCache; Bitmap mDefaultThumbnail; + BitmapDrawable mDefaultApplicationIcon; boolean mCancelled; boolean mWaitingOnLoadQueue; /** Constructor, creates a new loading thread that loads task resources in the background */ - public TaskResourceLoader(TaskResourceLoadQueue loadQueue, - DrawableLruCache applicationIconCache, - BitmapLruCache thumbnailCache, - Bitmap defaultThumbnail) { + public TaskResourceLoader(TaskResourceLoadQueue loadQueue, DrawableLruCache applicationIconCache, + BitmapLruCache thumbnailCache, Bitmap defaultThumbnail, + BitmapDrawable defaultApplicationIcon) { mLoadQueue = loadQueue; mApplicationIconCache = applicationIconCache; mThumbnailCache = thumbnailCache; mDefaultThumbnail = defaultThumbnail; + mDefaultApplicationIcon = defaultApplicationIcon; mMainThreadHandler = new Handler(); mLoadThread = new HandlerThread("Recents-TaskResourceLoader"); mLoadThread.setPriority(Thread.NORM_PRIORITY - 1); @@ -200,59 +182,51 @@ class TaskResourceLoader implements Runnable { SystemServicesProxy ssp = mSystemServicesProxy; // Load the next item from the queue - Pair nextTaskData = mLoadQueue.nextTask(); - final Task t = nextTaskData.first; - final boolean forceLoadTask = nextTaskData.second; + final Task t = mLoadQueue.nextTask(); if (t != null) { - Drawable loadIcon = mApplicationIconCache.getCheckLastActiveTime(t.key); - Bitmap loadThumbnail = mThumbnailCache.getCheckLastActiveTime(t.key); + Drawable cachedIcon = mApplicationIconCache.getCheckLastActiveTime(t.key); + Bitmap cachedThumbnail = mThumbnailCache.getCheckLastActiveTime(t.key); if (Console.Enabled) { Console.log(Constants.Log.App.TaskDataLoader, " [TaskResourceLoader|load]", - t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail + - " forceLoad: " + forceLoadTask); + t + " icon: " + cachedIcon + " thumbnail: " + cachedThumbnail); } - // Load the application icon - if (loadIcon == null || forceLoadTask) { + // Load the application icon if it is stale or we haven't cached one yet + if (cachedIcon == null) { + Drawable icon = null; ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(), t.userId); - Drawable icon = ssp.getActivityIcon(info, t.userId); - if (!mCancelled) { - if (icon != null) { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - " [TaskResourceLoader|loadIcon]", icon); - } - loadIcon = icon; - mApplicationIconCache.put(t.key, icon); + if (info != null) { + icon = ssp.getActivityIcon(info, t.userId); + if (Console.Enabled) { + Console.log(Constants.Log.App.TaskDataLoader, + " [TaskResourceLoader|loadedIcon]", icon); } } + // If we can't load the icon, then set the default application icon into the + // cache. This will remain until the task's last active time is updated. + cachedIcon = icon != null ? icon : mDefaultApplicationIcon; + mApplicationIconCache.put(t.key, cachedIcon); } - // Load the thumbnail - if (loadThumbnail == null || forceLoadTask) { + // Load the thumbnail if it is stale or we haven't cached one yet + if (cachedThumbnail == null) { Bitmap thumbnail = ssp.getTaskThumbnail(t.key.id); - if (!mCancelled) { - if (thumbnail != null) { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - " [TaskResourceLoader|loadThumbnail]", thumbnail); - } - thumbnail.setHasAlpha(false); - loadThumbnail = thumbnail; - } else { - loadThumbnail = mDefaultThumbnail; - Console.logError(mContext, - "Failed to load task top thumbnail for: " + - t.key.baseIntent.getComponent().getPackageName()); + if (thumbnail != null) { + thumbnail.setHasAlpha(false); + if (Console.Enabled) { + Console.log(Constants.Log.App.TaskDataLoader, + " [TaskResourceLoader|loadedThumbnail]", thumbnail); } - // We put the default thumbnail in the cache anyways - mThumbnailCache.put(t.key, loadThumbnail); } + // Even if we can't load the icon, we set the default thumbnail into the + // cache. This will remain until the task's last active time is updated. + cachedThumbnail = thumbnail != null ? thumbnail : mDefaultThumbnail; + mThumbnailCache.put(t.key, cachedThumbnail); } if (!mCancelled) { // Notify that the task data has changed - final Drawable newIcon = loadIcon; - final Bitmap newThumbnail = loadThumbnail; + final Drawable newIcon = cachedIcon; + final Bitmap newThumbnail = cachedThumbnail; mMainThreadHandler.post(new Runnable() { @Override public void run() { @@ -306,11 +280,11 @@ public class RecentsTaskLoader { /** Private Constructor */ private RecentsTaskLoader(Context context) { // Calculate the cache sizes, we just use a reasonable number here similar to those - // suggested in the Android docs, 1/8th for the thumbnail cache and 1/32 of the max memory + // suggested in the Android docs, 1/6th for the thumbnail cache and 1/30 of the max memory // for icons. - int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); - mMaxThumbnailCacheSize = maxMemory / 8; - mMaxIconCacheSize = mMaxThumbnailCacheSize / 4; + int maxMemory = (int) Runtime.getRuntime().maxMemory(); + mMaxThumbnailCacheSize = maxMemory / 6; + mMaxIconCacheSize = mMaxThumbnailCacheSize / 5; int iconCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 : mMaxIconCacheSize; int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 : @@ -340,7 +314,7 @@ public class RecentsTaskLoader { mApplicationIconCache = new DrawableLruCache(iconCacheSize); mThumbnailCache = new BitmapLruCache(thumbnailCacheSize); mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache, - mDefaultThumbnail); + mDefaultThumbnail, mDefaultApplicationIcon); if (Console.Enabled) { Console.log(Constants.Log.App.TaskDataLoader, @@ -394,7 +368,7 @@ public class RecentsTaskLoader { } RecentsConfiguration config = RecentsConfiguration.getInstance(); Resources res = context.getResources(); - ArrayList tasksToForceLoad = new ArrayList(); + LinkedHashSet tasksToLoad = new LinkedHashSet(); TaskStack stack = new TaskStack(); SpaceNode root = new SpaceNode(); root.setStack(stack); @@ -446,15 +420,16 @@ public class RecentsTaskLoader { if (isForemostTask) { // We force loading the application icon for the foremost task task.applicationIcon = ssp.getActivityIcon(info, task.userId); - if (task.applicationIcon != null) { - mApplicationIconCache.put(task.key, task.applicationIcon); - } else { + if (task.applicationIcon == null) { task.applicationIcon = mDefaultApplicationIcon; } + // Even if we can't load the icon we set the default application icon into + // the cache. This will remain until the task's last active time is updated. + mApplicationIconCache.put(task.key, task.applicationIcon); } else { - // Either the task has updated, or we haven't cached any information for the - // task, so reload it - tasksToForceLoad.add(task); + // Either the task has changed since the last active time, or it was not + // previously cached, so try and load the task anew. + tasksToLoad.add(task); } } @@ -473,11 +448,13 @@ public class RecentsTaskLoader { } else { task.thumbnail = mDefaultThumbnail; } + // Even if we can't load the thumbnail we set the default thumbnail into + // the cache. This will remain until the task's last active time is updated. mThumbnailCache.put(task.key, task.thumbnail); } else { - // Either the task has updated, or we haven't cached any information for the - // task, so reload it - tasksToForceLoad.add(task); + // Either the task has changed since the last active time, or it was not + // previously cached, so try and load the task anew. + tasksToLoad.add(task); } } } @@ -496,14 +473,14 @@ public class RecentsTaskLoader { } // Simulate the groupings that we describe - stack.createSimulatedAffiliatedGroupings(); + stack.createAffiliatedGroupings(); // Start the task loader mLoader.start(context); - // Add all the tasks that we are force/re-loading - for (Task t : tasksToForceLoad) { - mLoadQueue.addTask(t, true); + // Add all the tasks that we are reloading + for (Task t : tasksToLoad) { + mLoadQueue.addTask(t); } // Update the package monitor with the list of packages to listen for @@ -526,7 +503,7 @@ public class RecentsTaskLoader { stack.addTask(new Task(t.persistentId, true, t.baseIntent, t.affiliatedTaskId, null, null, 0, 0, t.firstActiveTime, t.lastActiveTime, (i == (taskCount - 1)))); } - stack.createSimulatedAffiliatedGroupings(); + stack.createAffiliatedGroupings(); return stack; } @@ -551,7 +528,7 @@ public class RecentsTaskLoader { requiresLoad = true; } if (requiresLoad) { - mLoadQueue.addTask(t, false); + mLoadQueue.addTask(t); } t.notifyTaskDataLoaded(thumbnail, applicationIcon); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java index 88e9f40ea8130..1670735873048 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java @@ -18,6 +18,7 @@ package com.android.systemui.recents.model; import android.content.Intent; import android.graphics.Bitmap; +import android.graphics.Color; import android.graphics.drawable.Drawable; import com.android.systemui.recents.misc.Utilities; @@ -84,7 +85,7 @@ public class Task { public Drawable activityIcon; public String activityLabel; public int colorPrimary; - public int colorPrimaryGreyscale; + public boolean useLightOnPrimaryColor; public Bitmap thumbnail; public boolean isActive; public boolean canLockToTask; @@ -104,7 +105,8 @@ public class Task { this.activityLabel = activityTitle; this.activityIcon = activityIcon; this.colorPrimary = colorPrimary; - this.colorPrimaryGreyscale = Utilities.colorToGreyscale(colorPrimary); + this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(colorPrimary, + Color.WHITE) > 3f; this.isActive = isActive; this.canLockToTask = canLockToTask; this.userId = userId; diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java index 7dd15a6c057da..e3bcff0749879 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java @@ -320,7 +320,7 @@ public class TaskStack { /** * Temporary: This method will simulate affiliation groups by */ - public void createSimulatedAffiliatedGroupings() { + public void createAffiliatedGroupings() { if (Constants.DebugFlags.App.EnableSimulatedTaskGroups) { HashMap taskMap = new HashMap(); // Sort all tasks by increasing firstActiveTime of the task diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 99b012e0c9bcb..73bbf86165c69 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -191,10 +191,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV /** Requests all task stacks to start their enter-recents animation */ public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) { - // Handle the case when there are no views by incrementing and decrementing after all - // animations are started. - ctx.postAnimationTrigger.increment(); - int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); @@ -203,18 +199,10 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV stackView.startEnterRecentsAnimation(ctx); } } - - // Handle the case when there are no views by incrementing and decrementing after all - // animations are started. - ctx.postAnimationTrigger.decrement(); } /** Requests all task stacks to start their exit-recents animation */ public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) { - // Handle the case when there are no views by incrementing and decrementing after all - // animations are started. - ctx.postAnimationTrigger.increment(); - int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); @@ -224,10 +212,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } } - // Handle the case when there are no views by incrementing and decrementing after all - // animations are started. - ctx.postAnimationTrigger.decrement(); - // Notify of the exit animation mCb.onExitToHomeAnimationTriggered(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java index db849626a07a3..deb9df3ed67eb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java @@ -162,11 +162,10 @@ class TaskBarView extends FrameLayout { } // Try and apply the system ui tint setBackgroundColor(t.colorPrimary); - mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColorGreyscale( - t.colorPrimaryGreyscale, mConfig.taskBarViewLightTextColor, - mConfig.taskBarViewDarkTextColor)); - mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColorGreyscale( - t.colorPrimaryGreyscale, mLightDismissDrawable, mDarkDismissDrawable)); + mActivityDescription.setTextColor(t.useLightOnPrimaryColor ? + mConfig.taskBarViewLightTextColor : mConfig.taskBarViewDarkTextColor); + mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ? + mLightDismissDrawable : mDarkDismissDrawable); } /** Unbinds the bar view from the task */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 599c59072862f..adc808a733fac 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -302,7 +302,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int[] visibleRange = mTmpVisibleRange; updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false); TaskViewTransform tmpTransform = new TaskViewTransform(); - TaskStack.GroupTaskIndex gti = new TaskStack.GroupTaskIndex(); // Return all the invisible children to the pool HashMap taskChildViewMap = getTaskChildViewMap(); @@ -355,6 +354,47 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } + /** Updates the clip for each of the task views. */ + void clipTaskViews() { + // Update the clip on each task child + if (Constants.DebugFlags.App.EnableTaskStackClipping) { + int childCount = getChildCount(); + for (int i = 0; i < childCount - 1; i++) { + TaskView tv = (TaskView) getChildAt(i); + TaskView nextTv = null; + TaskView tmpTv = null; + int clipBottom = 0; + if (tv.shouldClipViewInStack()) { + // Find the next view to clip against + int nextIndex = i; + while (nextIndex < getChildCount()) { + tmpTv = (TaskView) getChildAt(++nextIndex); + if (tmpTv != null && tmpTv.shouldClipViewInStack()) { + nextTv = tmpTv; + break; + } + } + + // Clip against the next view, this is just an approximation since we are + // stacked and we can make assumptions about the visibility of the this + // task relative to the ones in front of it. + if (nextTv != null) { + // XXX: Can hash the visible rects for this run + tv.getHitRect(mTmpRect); + nextTv.getHitRect(mTmpRect2); + clipBottom = (mTmpRect.bottom - mTmpRect2.top); + } + } + tv.setClipFromBottom(clipBottom); + } + } + if (getChildCount() > 0) { + // The front most task should never be clipped + TaskView tv = (TaskView) getChildAt(getChildCount() - 1); + tv.setClipFromBottom(0); + } + } + /** Sets the current stack scroll */ public void setStackScroll(int value) { mStackScroll = value; @@ -641,50 +681,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal Console.AnsiPurple); } synchronizeStackViewsWithModel(); + clipTaskViews(); super.dispatchDraw(canvas); } - @Override - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - if (Constants.DebugFlags.App.EnableTaskStackClipping) { - TaskView tv = (TaskView) child; - TaskView nextTv = null; - TaskView tmpTv = null; - if (tv.shouldClipViewInStack()) { - int curIndex = indexOfChild(tv); - - // Find the next view to clip against - while (nextTv == null && curIndex < getChildCount()) { - tmpTv = (TaskView) getChildAt(++curIndex); - if (tmpTv != null && tmpTv.shouldClipViewInStack()) { - nextTv = tmpTv; - } - } - - // Clip against the next view (if we aren't animating its alpha) - if (nextTv != null) { - Rect curRect = tv.getClippingRect(mTmpRect); - Rect nextRect = nextTv.getClippingRect(mTmpRect2); - // The hit rects are relative to the task view, which needs to be offset by - // the system bar height - curRect.offset(0, mConfig.systemInsets.top); - nextRect.offset(0, mConfig.systemInsets.top); - // Compute the clip region - Region clipRegion = new Region(); - clipRegion.op(curRect, Region.Op.UNION); - clipRegion.op(nextRect, Region.Op.DIFFERENCE); - // Clip the canvas - int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG); - canvas.clipRegion(clipRegion); - boolean invalidate = super.drawChild(canvas, child, drawingTime); - canvas.restoreToCount(saveCount); - return invalidate; - } - } - } - return super.drawChild(canvas, child, drawingTime); - } - /** Computes the stack and task rects */ public void computeRects(int width, int height, int insetLeft, int insetBottom) { // Compute the rects in the stack algorithm @@ -1155,6 +1155,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mStack.removeTask(task); } + @Override + public void onTaskViewClipStateChanged(TaskView tv) { + invalidate(mStackAlgorithm.mStackRect); + } + /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/ @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java index 908e0636245d0..9c48896ca9d65 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java @@ -135,9 +135,9 @@ public class TaskStackViewLayoutAlgorithm { // Set the y translation if (boundedT < 0f) { transformOut.translationY = (int) ((Math.max(-numPeekCards, boundedT) / - numPeekCards) * peekHeight - scaleYOffset - scaleBarYOffset); + numPeekCards) * peekHeight - scaleYOffset); } else { - transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset - scaleBarYOffset); + transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset); } // Set the z translation diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 5524e155b283c..ab148632698c5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -46,8 +46,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On interface TaskViewCallbacks { public void onTaskViewAppIconClicked(TaskView tv); public void onTaskViewAppInfoClicked(TaskView tv); - public void onTaskViewClicked(TaskView tv, Task t, boolean lockToTask); + public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask); public void onTaskViewDismissed(TaskView tv); + public void onTaskViewClipStateChanged(TaskView tv); } RecentsConfiguration mConfig; @@ -65,7 +66,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On boolean mIsFocused; boolean mIsStub; boolean mClipViewInStack; - Rect mTmpRect = new Rect(); + int mClipFromBottom; Paint mLayerPaint = new Paint(); TaskThumbnailView mThumbnailView; @@ -118,7 +119,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On setOutlineProvider(new ViewOutlineProvider() { @Override public boolean getOutline(View view, Outline outline) { - int height = getHeight() - mMaxFooterHeight + mFooterHeight; + // The current height is measured with the footer, so account for the footer height + // and the current clip (in the stack) + int height = getMeasuredHeight() - mClipFromBottom - mMaxFooterHeight + mFooterHeight; outline.setRoundRect(0, 0, getWidth(), height, mConfig.taskViewRoundedCornerRadiusPx); return true; @@ -483,15 +486,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On mBarView.setNoUserInteractionState(); } - /** Returns the rect we want to clip (it may not be the full rect) */ - Rect getClippingRect(Rect outRect) { - getHitRect(outRect); - // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster - outRect.right = outRect.left + mThumbnailView.getRight(); - outRect.bottom = outRect.top + mThumbnailView.getBottom(); - return outRect; - } - /** Enable the hw layers on this task view */ void enableHwLayers() { mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint); @@ -506,7 +500,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On mLockToAppButtonView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint); } - /** Sets the stubbed state of this task view. */ + /** Sets the stubbed state of this task view. void setStubState(boolean isStub) { if (!mIsStub && isStub) { // This is now a stub task view, so clip to the bar height, hide the thumbnail @@ -519,7 +513,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On mThumbnailView.setVisibility(View.VISIBLE); } mIsStub = isStub; - } + } */ /** * Returns whether this view should be clipped, or any views below should clip against this @@ -533,19 +527,26 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On void setClipViewInStack(boolean clip) { if (clip != mClipViewInStack) { mClipViewInStack = clip; - if (getParent() instanceof View) { - getHitRect(mTmpRect); - ((View) getParent()).invalidate(mTmpRect); - } + mCb.onTaskViewClipStateChanged(this); + } + } + + void setClipFromBottom(int clipFromBottom) { + clipFromBottom = Math.max(0, Math.min(getMeasuredHeight(), clipFromBottom)); + if (mClipFromBottom != clipFromBottom) { + mClipFromBottom = clipFromBottom; + invalidateOutline(); } } /** Sets the footer height. */ - public void setFooterHeight(int height) { - mFooterHeight = height; - invalidateOutline(); - invalidate(0, getMeasuredHeight() - mMaxFooterHeight, getMeasuredWidth(), - getMeasuredHeight()); + public void setFooterHeight(int footerHeight) { + if (footerHeight != mFooterHeight) { + mFooterHeight = footerHeight; + invalidateOutline(); + invalidate(0, getMeasuredHeight() - mMaxFooterHeight, getMeasuredWidth(), + getMeasuredHeight()); + } } /** Gets the footer height. */ @@ -677,6 +678,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On mTask = t; mTask.setCallbacks(this); if (getMeasuredWidth() == 0) { + // If we haven't yet measured, we should just set the footer height with any animation animateFooterVisibility(t.canLockToTask, 0, 0); } else { animateFooterVisibility(t.canLockToTask, mConfig.taskViewLockToAppLongAnimDuration, 0); From c0069550b161c4ce502cd397585c3acc0551720e Mon Sep 17 00:00:00 2001 From: Alan Viverette Date: Sun, 13 Jul 2014 19:00:01 -0700 Subject: [PATCH 17/75] Remove sub-position scroller BUG: 14477472 Change-Id: I019c58dedb383e9e906831c8e44bab8b88e92604 --- core/java/android/widget/AbsListView.java | 289 ---------------------- core/java/android/widget/GridView.java | 34 --- core/java/android/widget/ListView.java | 25 -- 3 files changed, 348 deletions(-) diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 9701c6fce6ba8..aa0b94f0208cc 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -36,7 +36,6 @@ import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; import android.util.LongSparseArray; -import android.util.MathUtils; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.StateSet; @@ -61,8 +60,6 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import android.view.inputmethod.BaseInputConnection; @@ -7269,290 +7266,4 @@ public abstract class AbsListView extends AdapterView implements Te } } } - - /** - * Abstract position scroller that handles sub-position scrolling but has no - * understanding of layout. - */ - abstract class AbsSubPositionScroller extends AbsPositionScroller { - private static final int DURATION_AUTO = -1; - - private static final int DURATION_AUTO_MIN = 100; - private static final int DURATION_AUTO_MAX = 500; - - private final SubScroller mSubScroller = new SubScroller(); - - /** - * The target offset in pixels between the top of the list and the top - * of the target position. - */ - private int mOffset; - - /** - * Scroll the minimum amount to get the target view entirely on-screen. - */ - private void scrollToPosition(final int targetPosition, final boolean useOffset, - final int offset, final int boundPosition, final int duration) { - stop(); - - if (mDataChanged) { - // Wait until we're back in a stable state to try this. - mPositionScrollAfterLayout = new Runnable() { - @Override - public void run() { - scrollToPosition( - targetPosition, useOffset, offset, boundPosition, duration); - } - }; - return; - } - - if (mAdapter == null) { - // Can't scroll anywhere without an adapter. - return; - } - - final int itemCount = getCount(); - final int clampedPosition = MathUtils.constrain(targetPosition, 0, itemCount - 1); - final int clampedBoundPosition = MathUtils.constrain(boundPosition, -1, itemCount - 1); - final int firstPosition = getFirstVisiblePosition(); - final int lastPosition = firstPosition + getChildCount(); - final int targetRow = getRowForPosition(clampedPosition); - final int firstRow = getRowForPosition(firstPosition); - final int lastRow = getRowForPosition(lastPosition); - if (useOffset || targetRow <= firstRow) { - // Offset so the target row is top-aligned. - mOffset = offset; - } else if (targetRow >= lastRow - 1) { - // Offset so the target row is bottom-aligned. - final int listHeight = getHeight() - getPaddingTop() - getPaddingBottom(); - mOffset = getHeightForPosition(clampedPosition) - listHeight; - } else { - // Don't scroll, target is entirely on-screen. - return; - } - - float endSubRow = targetRow; - if (clampedBoundPosition != INVALID_POSITION) { - final int boundRow = getRowForPosition(clampedBoundPosition); - if (boundRow >= firstRow && boundRow < lastRow && boundRow != targetRow) { - endSubRow = computeBoundSubRow(targetRow, boundRow); - } - } - - final View firstChild = getChildAt(0); - if (firstChild == null) { - return; - } - - final int firstChildHeight = firstChild.getHeight(); - final float startOffsetRatio; - if (firstChildHeight == 0) { - startOffsetRatio = 0; - } else { - startOffsetRatio = -firstChild.getTop() / (float) firstChildHeight; - } - - final float startSubRow = MathUtils.constrain( - firstRow + startOffsetRatio, 0, getCount()); - if (startSubRow == endSubRow && mOffset == 0) { - // Don't scroll, target is already in position. - return; - } - - final int durationMillis; - if (duration == DURATION_AUTO) { - final float subRowDelta = Math.abs(startSubRow - endSubRow); - durationMillis = (int) MathUtils.lerp( - DURATION_AUTO_MIN, DURATION_AUTO_MAX, subRowDelta / getCount()); - } else { - durationMillis = duration; - } - - mSubScroller.startScroll(startSubRow, endSubRow, durationMillis); - - postOnAnimation(mAnimationFrame); - } - - /** - * Given a target row and offset, computes the sub-row position that - * aligns with the top of the list. If the offset is negative, the - * resulting sub-row will be smaller than the target row. - */ - private float resolveOffset(int targetRow, int offset) { - // Compute the target sub-row position by finding the actual row - // indicated by the target and offset. - int remainingOffset = offset; - int targetHeight = getHeightForRow(targetRow); - if (offset < 0) { - // Subtract row heights until we find the right row. - while (targetRow > 0 && remainingOffset < 0) { - remainingOffset += targetHeight; - targetRow--; - targetHeight = getHeightForRow(targetRow); - } - } else if (offset > 0) { - // Add row heights until we find the right row. - while (targetRow < getCount() - 1 && remainingOffset > targetHeight) { - remainingOffset -= targetHeight; - targetRow++; - targetHeight = getHeightForRow(targetRow); - } - } - - final float targetOffsetRatio; - if (remainingOffset < 0 || targetHeight == 0) { - targetOffsetRatio = 0; - } else { - targetOffsetRatio = remainingOffset / (float) targetHeight; - } - - return targetRow + targetOffsetRatio; - } - - private float computeBoundSubRow(int targetRow, int boundRow) { - final float targetSubRow = resolveOffset(targetRow, mOffset); - mOffset = 0; - - // The target row is below the bound row, so the end position would - // push the bound position above the list. Abort! - if (targetSubRow >= boundRow) { - return boundRow; - } - - // Compute the closest possible sub-position that wouldn't push the - // bound position's view further below the list. - final int listHeight = getHeight() - getPaddingTop() - getPaddingBottom(); - final int boundHeight = getHeightForRow(boundRow); - final float boundSubRow = resolveOffset(boundRow, -listHeight + boundHeight); - - return Math.max(boundSubRow, targetSubRow); - } - - @Override - public void start(int position) { - scrollToPosition(position, false, 0, INVALID_POSITION, DURATION_AUTO); - } - - @Override - public void start(int position, int boundPosition) { - scrollToPosition(position, false, 0, boundPosition, DURATION_AUTO); - } - - @Override - public void startWithOffset(int position, int offset) { - scrollToPosition(position, true, offset, INVALID_POSITION, DURATION_AUTO); - } - - @Override - public void startWithOffset(int position, int offset, int duration) { - scrollToPosition(position, true, offset, INVALID_POSITION, duration); - } - - @Override - public void stop() { - removeCallbacks(mAnimationFrame); - } - - /** - * Returns the height of a row, which is computed as the maximum height of - * the items in the row. - * - * @param row the row index - * @return row height in pixels - */ - public abstract int getHeightForRow(int row); - - /** - * Returns the row for the specified item position. - * - * @param position the item position - * @return the row index - */ - public abstract int getRowForPosition(int position); - - /** - * Returns the first item position within the specified row. - * - * @param row the row - * @return the position of the first item in the row - */ - public abstract int getFirstPositionForRow(int row); - - private void onAnimationFrame() { - final boolean shouldPost = mSubScroller.computePosition(); - final float subRow = mSubScroller.getPosition(); - - final int row = (int) subRow; - final int position = getFirstPositionForRow(row); - if (position >= getCount()) { - // Invalid position, abort scrolling. - return; - } - - final int rowHeight = getHeightForRow(row); - final int offset = (int) (rowHeight * (subRow - row)); - final int addOffset = (int) (mOffset * mSubScroller.getInterpolatedValue()); - setSelectionFromTop(position, -offset - addOffset); - - if (shouldPost) { - postOnAnimation(mAnimationFrame); - } - } - - private Runnable mAnimationFrame = new Runnable() { - @Override - public void run() { - onAnimationFrame(); - } - }; - } - - /** - * Scroller capable of returning floating point positions. - */ - static class SubScroller { - private static final Interpolator INTERPOLATOR = new AccelerateDecelerateInterpolator(); - - private float mStartPosition; - private float mEndPosition; - private long mStartTime; - private long mDuration; - - private float mPosition; - private float mInterpolatedValue; - - public void startScroll(float startPosition, float endPosition, int duration) { - mStartPosition = startPosition; - mEndPosition = endPosition; - mDuration = duration; - - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - mPosition = startPosition; - mInterpolatedValue = 0; - } - - public boolean computePosition() { - final long elapsed = AnimationUtils.currentAnimationTimeMillis() - mStartTime; - final float value; - if (mDuration <= 0) { - value = 1; - } else { - value = MathUtils.constrain(elapsed / (float) mDuration, 0, 1); - } - - mInterpolatedValue = INTERPOLATOR.getInterpolation(value); - mPosition = (mEndPosition - mStartPosition) * mInterpolatedValue + mStartPosition; - - return elapsed < mDuration; - } - - public float getPosition() { - return mPosition; - } - - public float getInterpolatedValue() { - return mInterpolatedValue; - } - } } diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index 93810b3f6bf37..33cc66e55bc2b 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -1028,11 +1028,6 @@ public class GridView extends AbsListView { return didNotInitiallyFit; } - @Override - AbsPositionScroller createPositionScroller() { - return new GridViewPositionScroller(); - } - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Sets up mListPadding @@ -2392,33 +2387,4 @@ public class GridView extends AbsListView { column, 1, row, 1, isHeading, isSelected); info.setCollectionItemInfo(itemInfo); } - - /** - * Sub-position scroller that understands the layout of a GridView. - */ - class GridViewPositionScroller extends AbsSubPositionScroller { - @Override - public int getRowForPosition(int position) { - return position / mNumColumns; - } - - @Override - public int getFirstPositionForRow(int row) { - return row * mNumColumns; - } - - @Override - public int getHeightForRow(int row) { - final int firstRowPosition = row * mNumColumns; - final int lastRowPosition = Math.min(getCount(), firstRowPosition + mNumColumns); - int maxHeight = 0; - for (int i = firstRowPosition; i < lastRowPosition; i++) { - final int height = getHeightForPosition(i); - if (height > maxHeight) { - maxHeight = height; - } - } - return maxHeight; - } - } } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 1baeca880e9af..9db1e057ee7e8 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -3871,11 +3871,6 @@ public class ListView extends AbsListView { return false; } - @Override - AbsPositionScroller createPositionScroller() { - return new ListViewPositionScroller(); - } - @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); @@ -3905,24 +3900,4 @@ public class ListView extends AbsListView { 0, 1, position, 1, isHeading, isSelected); info.setCollectionItemInfo(itemInfo); } - - /** - * Sub-position scroller that understands the layout of a ListView. - */ - class ListViewPositionScroller extends AbsSubPositionScroller { - @Override - public int getRowForPosition(int position) { - return position; - } - - @Override - public int getFirstPositionForRow(int row) { - return row; - } - - @Override - public int getHeightForRow(int row) { - return getHeightForPosition(row); - } - } } From 1d93d2c217426c31cb89414279cc2f5e612ccc82 Mon Sep 17 00:00:00 2001 From: Alan Viverette Date: Sun, 13 Jul 2014 16:32:54 -0700 Subject: [PATCH 18/75] Add support for AVD reverse() to ASLD, clean up transition handling BUG: 16162242 Change-Id: I29336491d01d40e5369503ece858bcbe5aa99b19 --- .../drawable/AnimatedStateListDrawable.java | 203 +++++++++++++----- .../drawable/AnimatedVectorDrawable.java | 17 ++ 2 files changed, 163 insertions(+), 57 deletions(-) diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java index 848382004d49d..485b38a26d04e 100644 --- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java @@ -67,14 +67,14 @@ public class AnimatedStateListDrawable extends StateListDrawable { private AnimatedStateListState mState; - /** The currently running animation, if any. */ - private ObjectAnimator mAnim; + /** The currently running transition, if any. */ + private Transition mTransition; - /** Index to be set after the animation ends. */ - private int mAnimToIndex = -1; + /** Index to be set after the transition ends. */ + private int mTransitionToIndex = -1; - /** Index away from which we are animating. */ - private int mAnimFromIndex = -1; + /** Index away from which we are transitioning. */ + private int mTransitionFromIndex = -1; private boolean mMutated; @@ -84,20 +84,13 @@ public class AnimatedStateListDrawable extends StateListDrawable { @Override public boolean setVisible(boolean visible, boolean restart) { - // If we're relying on an Animatable transition, the super method - // will handle visibility changes. final boolean changed = super.setVisible(visible, restart); - if (mAnim != null) { + if (mTransition != null && (changed || restart)) { if (visible) { - if (restart) { - mAnim.cancel(); - mAnim.start(); - } else if (changed && mAnim.isPaused()) { - mAnim.resume(); - } - } else if (mAnim.isRunning()) { - mAnim.pause(); + mTransition.start(); + } else { + mTransition.stop(); } } @@ -164,30 +157,31 @@ public class AnimatedStateListDrawable extends StateListDrawable { } private boolean selectTransition(int toIndex) { - if (toIndex == mAnimToIndex) { + if (toIndex == mTransitionToIndex) { // Already animating to that keyframe. return true; } - if (mAnim != null) { - if (toIndex == mAnimToIndex) { + final Transition currentTransition = mTransition; + if (currentTransition != null) { + if (toIndex == mTransitionToIndex) { return true; - } else if (toIndex == mAnimFromIndex) { + } else if (toIndex == mTransitionFromIndex) { // Reverse the current animation. - mAnim.reverse(); - mAnimFromIndex = mAnimToIndex; - mAnimToIndex = toIndex; + currentTransition.reverse(); + mTransitionFromIndex = mTransitionToIndex; + mTransitionToIndex = toIndex; return true; } // Changing animation, end the current animation. - mAnim.cancel(); - mAnim = null; + currentTransition.stop(); + mTransition = null; } // Reset state. - mAnimFromIndex = -1; - mAnimToIndex = -1; + mTransitionFromIndex = -1; + mTransitionToIndex = -1; final AnimatedStateListState state = mState; final int fromIndex = getCurrentIndex(); @@ -205,49 +199,144 @@ public class AnimatedStateListDrawable extends StateListDrawable { return false; } + final Transition transition; final Drawable d = getCurrent(); if (d instanceof AnimationDrawable) { - // We can support reverse() here. - final boolean reversed = mState.isTransitionReversed(fromId, toId); - mAnim = getAnimationDrawableAnimator((AnimationDrawable) d, reversed); - mAnim.start(); + final boolean reversed = state.isTransitionReversed(fromId, toId); + transition = new AnimationDrawableTransition((AnimationDrawable) d, reversed); + } else if (d instanceof AnimatedVectorDrawable) { + final boolean reversed = state.isTransitionReversed(fromId, toId); + transition = new AnimatedVectorDrawableTransition((AnimatedVectorDrawable) d, reversed); } else if (d instanceof Animatable) { - // Let the transition animate itself. - ((Animatable) d).start(); + transition = new AnimatableTransition((Animatable) d); } else { // We don't know how to animate this transition. return false; } - mAnimFromIndex = fromIndex; - mAnimToIndex = toIndex; + transition.start(); + + mTransition = transition; + mTransitionFromIndex = fromIndex; + mTransitionToIndex = toIndex; return true; } - private ObjectAnimator getAnimationDrawableAnimator(@NonNull AnimationDrawable ad, - boolean reversed) { - final int frameCount = ad.getNumberOfFrames(); - final int fromFrame = reversed ? frameCount - 1 : 0; - final int toFrame = reversed ? 0 : frameCount - 1; - final FrameInterpolator interp = new FrameInterpolator(ad, reversed); - final ObjectAnimator anim = ObjectAnimator.ofInt(ad, "currentIndex", fromFrame, toFrame); - anim.setAutoCancel(true); - anim.setDuration(interp.getTotalDuration()); - anim.addListener(mAnimListener); - anim.setInterpolator(interp); + private static abstract class Transition { + public abstract void start(); + public abstract void stop(); - return anim; + public void reverse() { + // Not supported by default. + } + + public boolean canReverse() { + return false; + } } + private static class AnimatableTransition extends Transition { + private final Animatable mA; + + public AnimatableTransition(Animatable a) { + mA = a; + } + + @Override + public void start() { + mA.start(); + } + + @Override + public void stop() { + mA.stop(); + } + } + + + private static class AnimationDrawableTransition extends Transition { + private final ObjectAnimator mAnim; + + public AnimationDrawableTransition(AnimationDrawable ad, boolean reversed) { + final int frameCount = ad.getNumberOfFrames(); + final int fromFrame = reversed ? frameCount - 1 : 0; + final int toFrame = reversed ? 0 : frameCount - 1; + final FrameInterpolator interp = new FrameInterpolator(ad, reversed); + final ObjectAnimator anim = ObjectAnimator.ofInt(ad, "currentIndex", fromFrame, toFrame); + anim.setAutoCancel(true); + anim.setDuration(interp.getTotalDuration()); + anim.setInterpolator(interp); + + mAnim = anim; + } + + @Override + public boolean canReverse() { + return true; + } + + @Override + public void start() { + mAnim.start(); + } + + @Override + public void reverse() { + mAnim.reverse(); + } + + @Override + public void stop() { + mAnim.cancel(); + } + } + + private static class AnimatedVectorDrawableTransition extends Transition { + private final AnimatedVectorDrawable mAvd; + private final boolean mReversed; + + public AnimatedVectorDrawableTransition(AnimatedVectorDrawable avd, boolean reversed) { + mAvd = avd; + mReversed = reversed; + } + + @Override + public boolean canReverse() { + return true; + } + + @Override + public void start() { + if (mReversed) { + mAvd.reverse(); + } else { + mAvd.start(); + } + } + + @Override + public void reverse() { + mAvd.reverse(); + } + + @Override + public void stop() { + mAvd.stop(); + } + } + + @Override public void jumpToCurrentState() { - // If we're relying on an Animatable transition, the super method - // will handle jumping it to the current state. super.jumpToCurrentState(); - if (mAnim != null) { - mAnim.end(); - mAnim = null; + if (mTransition != null) { + mTransition.stop(); + mTransition = null; + + selectDrawable(mTransitionToIndex); + mTransitionToIndex = -1; + mTransitionFromIndex = -1; } } @@ -407,11 +496,11 @@ public class AnimatedStateListDrawable extends StateListDrawable { private final AnimatorListenerAdapter mAnimListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator anim) { - selectDrawable(mAnimToIndex); + selectDrawable(mTransitionToIndex); - mAnimToIndex = -1; - mAnimFromIndex = -1; - mAnim = null; + mTransitionToIndex = -1; + mTransitionFromIndex = -1; + mTransition = null; } }; diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 1cecef329867e..e3c172f31063c 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -16,6 +16,7 @@ package android.graphics.drawable; import android.animation.Animator; import android.animation.AnimatorInflater; +import android.animation.ValueAnimator; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; @@ -329,4 +330,20 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { animator.pause(); } } + + /** + * Reverses ongoing animations or starts pending animations in reverse. + *

+ * NOTE: Only works of all animations are ValueAnimators. + */ + void reverse() { + final ArrayList animators = mAnimatedVectorState.mAnimators; + final int size = animators.size(); + for (int i = 0; i < size; i++) { + final Animator animator = animators.get(i); + if (animator instanceof ValueAnimator) { + ((ValueAnimator) animator).reverse(); + } + } + } } From be34509baefa6f7e87011dd8ee05dd3011167005 Mon Sep 17 00:00:00 2001 From: Ihab Awad Date: Wed, 9 Jul 2014 12:30:52 -0700 Subject: [PATCH 19/75] Implement multi-SIM capabilities (1/6) - Split PhoneAccount into PhoneAccount & PhoneAccountMetadata - Move PhoneAccount methods from TelephonyManager to TelecommManager Change-Id: Ib440368d6bd0572b63c942360450fde5c27d84b9 --- api/current.txt | 27 +-- core/java/android/app/ContextImpl.java | 4 +- .../java/android/telecomm/PhoneAccount.java | 167 ++++++------------ .../telecomm/PhoneAccountMetadata.aidl | 22 +++ .../telecomm/PhoneAccountMetadata.java | 140 +++++++++++++++ .../telecomm/RemoteConnectionService.java | 5 +- .../android/telecomm/TelecommManager.java | 130 ++++++++++++-- .../internal/telecomm/ITelecommService.aidl | 25 ++- .../android/telephony/TelephonyManager.java | 47 ----- 9 files changed, 366 insertions(+), 201 deletions(-) create mode 100644 telecomm/java/android/telecomm/PhoneAccountMetadata.aidl create mode 100644 telecomm/java/android/telecomm/PhoneAccountMetadata.java diff --git a/api/current.txt b/api/current.txt index 8868c8565bf86..41aad5f59f4b2 100644 --- a/api/current.txt +++ b/api/current.txt @@ -27868,18 +27868,27 @@ package android.telecomm { method protected abstract void updateCall(android.telecomm.InCallCall); } - public final class PhoneAccount implements android.os.Parcelable { - ctor public PhoneAccount(android.content.ComponentName, java.lang.String, android.net.Uri, java.lang.String, java.lang.String, boolean, boolean); + public class PhoneAccount implements android.os.Parcelable { + ctor public PhoneAccount(android.content.ComponentName, java.lang.String, android.net.Uri, int); method public int describeContents(); + method public int getCapabilities(); method public android.content.ComponentName getComponentName(); method public android.net.Uri getHandle(); - method public android.graphics.drawable.Drawable getIcon(android.content.Context); - method public android.graphics.drawable.Drawable getIcon(android.content.Context, int); method public java.lang.String getId(); - method public java.lang.String getLabel(android.content.Context); - method public java.lang.String getShortDescription(android.content.Context); - method public boolean isEnabled(); - method public boolean isSystemDefault(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2 + field public static final int CAPABILITY_SIM_CALL_MANAGER = 1; // 0x1 + field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4 + field public static final android.os.Parcelable.Creator CREATOR; + } + + public class PhoneAccountMetadata implements android.os.Parcelable { + ctor public PhoneAccountMetadata(android.telecomm.PhoneAccount, int, java.lang.String, java.lang.String); + method public int describeContents(); + method public android.telecomm.PhoneAccount getAccount(); + method public android.graphics.drawable.Drawable getIcon(android.content.Context); + method public java.lang.String getLabel(); + method public java.lang.String getShortDescription(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } @@ -28420,7 +28429,6 @@ package android.telephony { } public class TelephonyManager { - method public java.util.List getAccounts(); method public java.util.List getAllCellInfo(); method public int getCallState(); method public android.telephony.CellLocation getCellLocation(); @@ -28469,7 +28477,6 @@ package android.telephony { field public static final int DATA_CONNECTING = 1; // 0x1 field public static final int DATA_DISCONNECTED = 0; // 0x0 field public static final int DATA_SUSPENDED = 3; // 0x3 - field public static final java.lang.String EXTRA_ACCOUNT = "account"; field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number"; field public static final java.lang.String EXTRA_STATE = "state"; field public static final java.lang.String EXTRA_STATE_IDLE; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index bbfb05e12eccd..395c1b9b12868 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -558,9 +558,7 @@ class ContextImpl extends Context { registerService(TELECOMM_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { - IBinder b = ServiceManager.getService(TELECOMM_SERVICE); - return new TelecommManager(ctx.getOuterContext(), - ITelecommService.Stub.asInterface(b)); + return new TelecommManager(ctx.getOuterContext()); }}); registerService(PHONE_SERVICE, new ServiceFetcher() { diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java index 4e440d8d18c5f..c1eec83e2a9a3 100644 --- a/telecomm/java/android/telecomm/PhoneAccount.java +++ b/telecomm/java/android/telecomm/PhoneAccount.java @@ -17,52 +17,64 @@ package android.telecomm; import android.content.ComponentName; -import android.content.Context; -import android.content.pm.PackageManager; -import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; -import android.telephony.Rlog; -import android.util.DisplayMetrics; -import android.util.Log; -import java.util.MissingResourceException; import java.util.Objects; /** * Represents a distinct account, line of service or call placement method that * the system can use to place phone calls. */ -public final class PhoneAccount implements Parcelable { +public class PhoneAccount implements Parcelable { - private static final int NO_DENSITY = -1; - private static final String LOG_TAG = "Account"; + /** + * Flag indicating that this {@code PhoneAccount} can act as a call manager for traditional + * SIM-based telephony calls. The {@link ConnectionService} associated with this phone-account + * will be allowed to manage SIM-based phone calls including using its own proprietary + * phone-call implementation (like VoIP calling) to make calls instead of the telephony stack. + * When a user opts to place a call using the SIM-based telephony stack, the connection-service + * associated with this phone-account will be attempted first if the user has explicitly + * selected it to be used as the default call-manager. + *

+ * See {@link #getCapabilities} + */ + public static final int CAPABILITY_SIM_CALL_MANAGER = 0x1; - private final ComponentName mComponentName; - private final String mId; - private final Uri mHandle; - private final String mLabel; - private final String mShortDescription; - private final boolean mIsEnabled; - private final boolean mIsSystemDefault; + /** + * Flag indicating that this {@code PhoneAccount} can make phone calls in place of traditional + * SIM-based telephony calls. This account will be treated as a distinct method for placing + * calls alongside the traditional SIM-based telephony stack. This flag is distinct from + * {@link #CAPABILITY_SIM_CALL_MANAGER} in that it is not allowed to manage calls from or use + * the built-in telephony stack to place its calls. + *

+ * See {@link #getCapabilities} + */ + public static final int CAPABILITY_CALL_PROVIDER = 0x2; + + /** + * Flag indicating that this {@code PhoneAccount} represents a built-in PSTN SIM subscription. + *

+ * Only the android framework can set this capability on a phone-account. + */ + public static final int CAPABILITY_SIM_SUBSCRIPTION = 0x4; + + private ComponentName mComponentName; + private String mId; + private Uri mHandle; + private int mCapabilities; public PhoneAccount( ComponentName componentName, String id, Uri handle, - String label, - String shortDescription, - boolean isEnabled, - boolean isSystemDefault) { + int capabilities) { mComponentName = componentName; mId = id; mHandle = handle; - mLabel = label; - mShortDescription = shortDescription; - mIsSystemDefault = isSystemDefault; - mIsEnabled = isEnabled; + mCapabilities = capabilities; } /** @@ -87,8 +99,8 @@ public final class PhoneAccount implements Parcelable { /** * The handle (e.g., a phone number) associated with this {@code PhoneAccount}. This represents - * the destination from which outgoing calls using this {@code PhoneAccount} will appear to come - * from, if applicable, and the destination to which incoming calls using this + * the destination from which outgoing calls using this {@code PhoneAccount} will appear to + * come, if applicable, and the destination to which incoming calls using this * {@code PhoneAccount} may be addressed. * * @return A handle expressed as a {@code Uri}, for example, a phone number. @@ -98,76 +110,23 @@ public final class PhoneAccount implements Parcelable { } /** - * A short string label describing this {@code PhoneAccount}. + * The capabilities of this {@code PhoneAccount}. * - * @param context The invoking {@code Context}, used for retrieving resources. - * - * TODO(ihab): If don't need context, remove param - * - * @return A label for this {@code PhoneAccount}. + * @return A bit field of flags describing this {@code PhoneAccount}'s capabilities. */ - public String getLabel(Context context) { - return mLabel; + public int getCapabilities() { + return mCapabilities; } - /** - * A short paragraph describing this {@code PhoneAccount}. - * - * @param context The invoking {@code Context}, used for retrieving resources. - * - * TODO(ihab): If don't need context, remove param - * - * @return A description for this {@code PhoneAccount}. - */ - public String getShortDescription(Context context) { - return mShortDescription; + @Override + public int hashCode() { + return Objects.hashCode(mComponentName) + Objects.hashCode(mId) + + Objects.hashCode(mHandle) + mCapabilities; } - // TODO(ihab): Representation of the icons // - // Refactor to pass a Bitmap (scale it at runtime), but if they don't pass one, fall - // back to the android:icon attr in the manifest ( first, second) - - /** - * An icon to represent this {@code PhoneAccount} in a user interface. - * - * @param context The invoking {@code Context}, used for retrieving resources. - * - * @return An icon for this {@code PhoneAccount}. - */ - public Drawable getIcon(Context context) { - return null; // TODO(ihab): See above - } - - /** - * An icon to represent this {@code PhoneAccount} in a user interface. - * - * @param context The invoking {@code Context}, used for retrieving resources. - * @param density A display density from {@link DisplayMetrics}. - * - * @return An icon for this {@code PhoneAccount}. - */ - public Drawable getIcon(Context context, int density) { - return null; // TODO(ihab): See above - } - - /** - * Whether this {@code PhoneAccount} is enabled for use. - * - * @return {@code true} if this {@code PhoneAccount} is enabled. - */ - public boolean isEnabled() { - return mIsEnabled; - } - - /** - * Whether this {@code PhoneAccount} is the system default. - * - * @return {@code true} if this {@code PhoneAccount} is the system default. - */ - public boolean isSystemDefault() { - return mIsSystemDefault; - } + // Parcelable implementation. + // @Override public int describeContents() { @@ -179,18 +138,16 @@ public final class PhoneAccount implements Parcelable { out.writeParcelable(mComponentName, flags); out.writeString(mId); out.writeString(mHandle != null ? mHandle.toString() : ""); - out.writeString(mLabel); - out.writeString(mShortDescription); - out.writeInt(mIsEnabled ? 1 : 0); - out.writeInt(mIsSystemDefault ? 1 : 0); + out.writeInt(mCapabilities); } - public static final Creator CREATOR - = new Creator() { + public static final Creator CREATOR = new Creator() { + @Override public PhoneAccount createFromParcel(Parcel in) { return new PhoneAccount(in); } + @Override public PhoneAccount[] newArray(int size) { return new PhoneAccount[size]; } @@ -201,22 +158,6 @@ public final class PhoneAccount implements Parcelable { mId = in.readString(); String uriString = in.readString(); mHandle = uriString.length() > 0 ? Uri.parse(uriString) : null; - mLabel = in.readString(); - mShortDescription = in.readString(); - mIsEnabled = in.readInt() == 1; - mIsSystemDefault = in.readInt() == 1; - } - - @Override - public boolean equals(Object other) { - return - other instanceof PhoneAccount && - Objects.equals(mComponentName, ((PhoneAccount) other).mComponentName) && - Objects.equals(mId, ((PhoneAccount) other).mId); - } - - @Override - public int hashCode() { - return Objects.hashCode(mComponentName) + Objects.hashCode(mId); + mCapabilities = in.readInt(); } } diff --git a/telecomm/java/android/telecomm/PhoneAccountMetadata.aidl b/telecomm/java/android/telecomm/PhoneAccountMetadata.aidl new file mode 100644 index 0000000000000..55b890007eb7e --- /dev/null +++ b/telecomm/java/android/telecomm/PhoneAccountMetadata.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +/** + * {@hide} + */ +parcelable PhoneAccountMetadata; diff --git a/telecomm/java/android/telecomm/PhoneAccountMetadata.java b/telecomm/java/android/telecomm/PhoneAccountMetadata.java new file mode 100644 index 0000000000000..20a4d47a714b3 --- /dev/null +++ b/telecomm/java/android/telecomm/PhoneAccountMetadata.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.IOException; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.util.MissingResourceException; + +/** + * Provides user interface description information for a {@code PhoneAccount}. + */ +public class PhoneAccountMetadata implements Parcelable { + private PhoneAccount mAccount; + private int mIconResId; + private String mLabel; + private String mShortDescription; + + public PhoneAccountMetadata( + PhoneAccount account, + int iconResId, + String label, + String shortDescription) { + mAccount = account; + mIconResId = iconResId; + mLabel = label; + mShortDescription = shortDescription; + } + + /** + * The {@code PhoneAccount} to which this metadata pertains. + * + * @return A {@code PhoneAccount}. + */ + public PhoneAccount getAccount() { + return mAccount; + } + + /** + * A short string label describing a {@code PhoneAccount}. + * + * @return A label for this {@code PhoneAccount}. + */ + public String getLabel() { + return mLabel; + } + + /** + * A short paragraph describing a {@code PhoneAccount}. + * + * @return A description for this {@code PhoneAccount}. + */ + public String getShortDescription() { + return mShortDescription; + } + + /** + * An icon to represent this {@code PhoneAccount} in a user interface. + * + * @return An icon for this {@code PhoneAccount}. + */ + public Drawable getIcon(Context context) { + return getIcon(context, mIconResId); + } + + private Drawable getIcon(Context context, int resId) { + Context packageContext; + try { + packageContext = context.createPackageContext( + mAccount.getComponentName().getPackageName(), 0); + } catch (PackageManager.NameNotFoundException e) { + Log.w(this, "Cannot find package %s", mAccount.getComponentName().getPackageName()); + return null; + } + try { + return packageContext.getResources().getDrawable(resId); + } catch (MissingResourceException e) { + Log.e(this, e, "Cannot find icon %d in package %s", + resId, mAccount.getComponentName().getPackageName()); + return null; + } + } + + // + // Parcelable implementation + // + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(mAccount, 0); + out.writeInt(mIconResId); + out.writeString(mLabel); + out.writeString(mShortDescription); + } + + public static final Creator CREATOR + = new Creator() { + @Override + public PhoneAccountMetadata createFromParcel(Parcel in) { + return new PhoneAccountMetadata(in); + } + + @Override + public PhoneAccountMetadata[] newArray(int size) { + return new PhoneAccountMetadata[size]; + } + }; + + private PhoneAccountMetadata(Parcel in) { + mAccount = in.readParcelable(getClass().getClassLoader()); + mIconResId = in.readInt(); + mLabel = in.readString(); + mShortDescription = in.readString(); + } +} diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java index a436af27a3145..430133cebfc62 100644 --- a/telecomm/java/android/telecomm/RemoteConnectionService.java +++ b/telecomm/java/android/telecomm/RemoteConnectionService.java @@ -266,10 +266,7 @@ final class RemoteConnectionService implements DeathRecipient { mComponentName, null /* id */, null /* handle */, - "" /* label */, - "" /* shortDescription */, - true /* isEnabled */, - false /* isSystemDefault */)); + 0 /* capabilities */)); return accounts; } diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java index 1bb18f234147a..fcd2eba7116f5 100644 --- a/telecomm/java/android/telecomm/TelecommManager.java +++ b/telecomm/java/android/telecomm/TelecommManager.java @@ -23,31 +23,30 @@ import android.util.Log; import com.android.internal.telecomm.ITelecommService; +import java.util.List; + /** * Provides access to Telecomm-related functionality. * TODO(santoscordon): Move this all into PhoneManager. * @hide */ public class TelecommManager { + + /** + * The extra used with an {@link android.content.Intent#ACTION_CALL} or + * {@link android.content.Intent#ACTION_DIAL} {@code Intent} to specify a {@link PhoneAccount} + * to use when making the call. + * + *

+ * Retrieve with + * {@link android.content.Intent#getParcelableExtra(String)}. + */ + public static final String EXTRA_PHONE_ACCOUNT = "account"; + private static final String TAG = "TelecommManager"; private static final String TELECOMM_SERVICE_NAME = "telecomm"; private final Context mContext; - private final ITelecommService mService; - - /** - * @hide - */ - public TelecommManager(Context context, ITelecommService service) { - Context appContext = context.getApplicationContext(); - if (appContext != null) { - mContext = appContext; - } else { - mContext = context; - } - - mService = service; - } /** * @hide @@ -56,6 +55,103 @@ public class TelecommManager { return (TelecommManager) context.getSystemService(Context.TELECOMM_SERVICE); } + /** + * @hide + */ + public TelecommManager(Context context) { + Context appContext = context.getApplicationContext(); + if (appContext != null) { + mContext = appContext; + } else { + mContext = context; + } + } + + /** + * Return a list of {@link PhoneAccount}s which can be used to make and receive phone calls. + * + * @see #EXTRA_PHONE_ACCOUNT + * @return A list of {@code PhoneAccount} objects. + */ + public List getEnabledPhoneAccounts() { + try { + if (isServiceConnected()) { + return getTelecommService().getEnabledPhoneAccounts(); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecommService#getEnabledPhoneAccounts", e); + } + return null; + } + + /** + * Return the metadata for a specified {@link PhoneAccount}. Metadata includes resources which + * can be used in a user interface. + * + * @param account The {@link PhoneAccount}. + * + * @return The metadata for the account. + */ + public PhoneAccountMetadata getPhoneAccountMetadata(PhoneAccount account) { + try { + if (isServiceConnected()) { + return getTelecommService().getPhoneAccountMetadata(account); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecommService#getPhoneAccountMetadata", e); + } + return null; + } + + /** + * Register a {@link PhoneAccount} for use by the system. + * + * @param account The {@link PhoneAccount}. + * @param metadata The metadata for the account. + */ + public void registerPhoneAccount(PhoneAccount account, PhoneAccountMetadata metadata) { + try { + if (isServiceConnected()) { + getTelecommService().registerPhoneAccount(account, metadata); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecommService#registerPhoneAccount", e); + } + } + + /** + * Remove a {@link PhoneAccount} registration from the system. + * + * @param account An Account. + */ + public void unregisterPhoneAccount(PhoneAccount account) { + try { + if (isServiceConnected()) { + getTelecommService().unregisterPhoneAccount(account); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecommService#unregisterPhoneAccount", e); + } + } + + /** + * Remove all Accounts for a given package from the system. + * + * @param packageName A package name that may have registered Accounts. + * + * @hide + */ + @SystemApi + public void clearAccounts(String packageName) { + try { + if (isServiceConnected()) { + getTelecommService().clearAccounts(packageName); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecommService#clearAccounts", e); + } + } + /** * @hide */ @@ -108,7 +204,7 @@ public class TelecommManager { /** * Ends an ongoing call. TODO(santoscordon): L-release - need to convert all invocations of - * ITelephony#endCall to use this method (clockwork & gearhead). + * ITelecommService#endCall to use this method (clockwork & gearhead). * * @hide */ @@ -127,7 +223,7 @@ public class TelecommManager { /** * If there is a ringing incoming call, this method accepts the call on behalf of the user. * TODO(santoscordon): L-release - need to convert all invocation of - * ITelephony#answerRingingCall to use this method (clockwork & gearhead). + * ITelecommService#answerRingingCall to use this method (clockwork & gearhead). * * @hide */ diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl index 30e4bdc234b0b..3334385b3f57e 100644 --- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl +++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl @@ -18,6 +18,7 @@ package com.android.internal.telecomm; import android.content.ComponentName; import android.telecomm.PhoneAccount; +import android.telecomm.PhoneAccountMetadata; /** * Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing @@ -33,22 +34,32 @@ interface ITelecommService { void showCallScreen(boolean showDialpad); /** - * Gets a list of accounts. + * @see TelecommManager#getEnabledPhoneAccounts */ - List getAccounts(); + List getEnabledPhoneAccounts(); /** - * Sets the enabled state of a given account. + * @see TelecommManager#getPhoneAccountMetadata */ - void setEnabled(in PhoneAccount account, boolean enabled); + PhoneAccountMetadata getPhoneAccountMetadata(in PhoneAccount account); /** - * Sets a given account as the system default. + * @see TelecommManager#registerPhoneAccount */ - void setSystemDefault(in PhoneAccount account); + void registerPhoneAccount(in PhoneAccount account, in PhoneAccountMetadata metadata); /** - * Returns the component name of the default phone application. + * @see TelecommManager#unregisterPhoneAccount + */ + void unregisterPhoneAccount(in PhoneAccount account); + + /** + * @see TelecommManager#clearAccounts + */ + void clearAccounts(String packageName); + + /** + * @see TelecommManager#getDefaultPhoneApp */ ComponentName getDefaultPhoneApp(); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 91ce73a326caf..c1eb843eb6356 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -297,17 +297,6 @@ public class TelephonyManager { */ public static final String EXTRA_INCOMING_NUMBER = "incoming_number"; - /** - * The lookup key used with an {@link android.content.Intent#ACTION_CALL} or - * {@link android.content.Intent#ACTION_DIAL} {@code Intent} for a {@link PhoneAccount} - * object indicating a preference when making a phone connection. - * - *

- * Retrieve with - * {@link android.content.Intent#getParcelableExtra(String)}. - */ - public static final String EXTRA_ACCOUNT = "account"; - /** * Broadcast intent action indicating that a precise call state * (cellular) on the device has changed. @@ -3206,42 +3195,6 @@ public class TelephonyManager { return false; } - /** - * Return a list of Accounts that can be used to indicate a preference when making - * a phone call. - * - * @see #EXTRA_ACCOUNT - * @return A list of {@code Accouint} objects. - */ - public List getAccounts() { - try { - return getTelecommService().getAccounts(); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#getAccounts", e); - } - return null; - } - - /** @hide */ - @SystemApi - public void setEnabled(PhoneAccount account, boolean enabled) { - try { - getTelecommService().setEnabled(account, enabled); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#setEnabled", e); - } - } - - /** @hide */ - @SystemApi - public void setSystemDefault(PhoneAccount account) { - try { - getTelecommService().setSystemDefault(account); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#setSystemDefault", e); - } - } - /** * Set whether Android should display a simplified Mobile Network Settings UI. * The setting won't be persisted during power cycle. From 24cda2a793ac41c3e6ccfbc17248edbb1afaf02e Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Mon, 30 Jun 2014 12:26:22 -0400 Subject: [PATCH 20/75] Do not assert that typeface is not NULL. There may not be a typeface. Change-Id: Ia4a4b8adbd3c382da98afe1fc5d23ab6b1bb79bb --- core/jni/android/graphics/Graphics.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 9177696957b4b..6c7962e026725 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -385,7 +385,6 @@ android::TypefaceImpl* GraphicsJNI::getNativeTypeface(JNIEnv* env, jobject paint SkASSERT(env->IsInstanceOf(paint, gPaint_class)); jlong typefaceHandle = env->GetLongField(paint, gPaint_nativeTypefaceID); android::TypefaceImpl* p = reinterpret_cast(typefaceHandle); - SkASSERT(p); return p; } From 2fee0f867b497074fb2243a9f51e06adfc0b9d3b Mon Sep 17 00:00:00 2001 From: PauloftheWest Date: Mon, 14 Jul 2014 07:50:47 -0700 Subject: [PATCH 21/75] Changed wording from "Mobile" to "Cellular" Bug: 15720445 Change-Id: Ifdf2667fb01ebd621b89facc0c6b4dbbe0d8c4bb --- core/res/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 5d57262e8c1e3..bab9d76e76481 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3622,13 +3622,13 @@ SIM card removed - The mobile network will be unavailable until you restart with a valid SIM card inserted. + The cellular network will be unavailable until you restart with a valid SIM card inserted. Done SIM card added - Restart your device to access the mobile network. + Restart your device to access the cellular network. Restart From 72e14ab8ab297ef8ad2aee32e89a6d88c170f1f9 Mon Sep 17 00:00:00 2001 From: Sailesh Nepal Date: Mon, 14 Jul 2014 08:19:46 -0700 Subject: [PATCH 22/75] Remove CallServiceProvider and CallServiceDescriptor This CL removes CallServiceProvider. Instead of using a provider we'll look up ConnectionServices using the package manager instead. This CL also removes CallServiceDescriptor. For incoming calls, connection services will now pass a PhoneAccount instead of a call descriptor. For outgoing calls we already use PhoneAccounts for everything. Change-Id: I2e40c5c64c0d242dc41b680943d7e9209142db5b --- Android.mk | 6 - api/current.txt | 33 +-- .../telecomm/CallServiceDescriptor.aidl | 19 -- .../telecomm/CallServiceDescriptor.java | 233 ------------------ .../telecomm/CallServiceLookupResponse.java | 51 ---- .../android/telecomm/CallServiceProvider.java | 109 -------- .../java/android/telecomm/InCallCall.java | 13 +- .../android/telecomm/TelecommConstants.java | 24 +- .../android/telecomm/TelecommManager.java | 12 - .../telecomm/ICallServiceLookupResponse.aidl | 32 --- .../telecomm/ICallServiceProvider.aidl | 32 --- .../telephony/IThirdPartyCallListener.aidl | 46 ---- .../telephony/IThirdPartyCallProvider.aidl | 47 ---- .../IThirdPartyCallSendDtmfCallback.aidl | 27 -- .../telephony/IThirdPartyCallService.aidl | 35 --- 15 files changed, 14 insertions(+), 705 deletions(-) delete mode 100644 telecomm/java/android/telecomm/CallServiceDescriptor.aidl delete mode 100644 telecomm/java/android/telecomm/CallServiceDescriptor.java delete mode 100644 telecomm/java/android/telecomm/CallServiceLookupResponse.java delete mode 100644 telecomm/java/android/telecomm/CallServiceProvider.java delete mode 100644 telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl delete mode 100644 telecomm/java/com/android/internal/telecomm/ICallServiceProvider.aidl delete mode 100644 telephony/java/com/android/internal/telephony/IThirdPartyCallListener.aidl delete mode 100644 telephony/java/com/android/internal/telephony/IThirdPartyCallProvider.aidl delete mode 100644 telephony/java/com/android/internal/telephony/IThirdPartyCallSendDtmfCallback.aidl delete mode 100644 telephony/java/com/android/internal/telephony/IThirdPartyCallService.aidl diff --git a/Android.mk b/Android.mk index 23c4753cfa22c..03676d37ee67e 100644 --- a/Android.mk +++ b/Android.mk @@ -337,8 +337,6 @@ LOCAL_SRC_FILES += \ media/java/android/media/tv/ITvInputServiceCallback.aidl \ media/java/android/media/tv/ITvInputSession.aidl \ media/java/android/media/tv/ITvInputSessionCallback.aidl \ - telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl \ - telecomm/java/com/android/internal/telecomm/ICallServiceProvider.aidl \ telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl \ telecomm/java/com/android/internal/telecomm/ICallVideoClient.aidl \ telecomm/java/com/android/internal/telecomm/IConnectionService.aidl \ @@ -359,10 +357,6 @@ LOCAL_SRC_FILES += \ telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \ telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \ telephony/java/com/android/internal/telephony/ITelephony.aidl \ - telephony/java/com/android/internal/telephony/IThirdPartyCallListener.aidl \ - telephony/java/com/android/internal/telephony/IThirdPartyCallProvider.aidl \ - telephony/java/com/android/internal/telephony/IThirdPartyCallSendDtmfCallback.aidl \ - telephony/java/com/android/internal/telephony/IThirdPartyCallService.aidl \ telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \ telephony/java/com/android/internal/telephony/ISms.aidl \ telephony/java/com/android/internal/telephony/IWapPushManager.aidl \ diff --git a/api/current.txt b/api/current.txt index 41aad5f59f4b2..490196be78e7a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -27642,35 +27642,6 @@ package android.telecomm { field public static final int UNKNOWN = 2; // 0x2 } - public final class CallServiceDescriptor implements android.os.Parcelable { - method public int describeContents(); - method public java.lang.String getConnectionServiceId(); - method public int getNetworkType(); - method public android.content.ComponentName getServiceComponent(); - method public static android.telecomm.CallServiceDescriptor.Builder newBuilder(android.content.Context); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator CREATOR; - field public static final int FLAG_MOBILE = 4; // 0x4 - field public static final int FLAG_PSTN = 2; // 0x2 - field public static final int FLAG_WIFI = 1; // 0x1 - } - - public static class CallServiceDescriptor.Builder { - method public android.telecomm.CallServiceDescriptor build(); - method public android.telecomm.CallServiceDescriptor.Builder setConnectionService(java.lang.Class); - method public android.telecomm.CallServiceDescriptor.Builder setNetworkType(int); - } - - public final class CallServiceLookupResponse { - method public void setCallServiceDescriptors(java.util.List); - } - - public abstract class CallServiceProvider extends android.app.Service { - ctor protected CallServiceProvider(); - method public abstract void lookupCallServices(android.telecomm.CallServiceLookupResponse); - method public android.os.IBinder onBind(android.content.Intent); - } - public final class CallState extends java.lang.Enum { method public static android.telecomm.CallState valueOf(java.lang.String); method public static final android.telecomm.CallState[] values(); @@ -27842,7 +27813,6 @@ package android.telecomm { method public java.util.List getCannedSmsResponses(); method public int getCapabilities(); method public long getConnectTimeMillis(); - method public android.telecomm.CallServiceDescriptor getCurrentCallServiceDescriptor(); method public int getDisconnectCauseCode(); method public java.lang.String getDisconnectCauseMsg(); method public android.telecomm.GatewayInfo getGatewayInfo(); @@ -27981,16 +27951,15 @@ package android.telecomm { public final class TelecommConstants { ctor public TelecommConstants(); - field public static final java.lang.String ACTION_CALL_SERVICE_PROVIDER; field public static final java.lang.String ACTION_CONNECTION_SERVICE; field public static final java.lang.String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL"; field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ',' field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';' field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecomm.extra.CALL_DISCONNECT_CAUSE"; field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecomm.extra.CALL_DISCONNECT_MESSAGE"; - field public static final java.lang.String EXTRA_CALL_SERVICE_DESCRIPTOR = "android.intent.extra.CALL_SERVICE_DESCRIPTOR"; field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecomm.extra.CONNECTION_SERVICE"; field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS"; + field public static final java.lang.String EXTRA_PHONE_ACCOUNT = "android.intent.extra.PHONE_ACCOUNT"; field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE"; field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.intent.extra.START_CALL_WITH_VIDEO_STATE"; } diff --git a/telecomm/java/android/telecomm/CallServiceDescriptor.aidl b/telecomm/java/android/telecomm/CallServiceDescriptor.aidl deleted file mode 100644 index f517c731dacfa..0000000000000 --- a/telecomm/java/android/telecomm/CallServiceDescriptor.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2014, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.telecomm; - -parcelable CallServiceDescriptor; diff --git a/telecomm/java/android/telecomm/CallServiceDescriptor.java b/telecomm/java/android/telecomm/CallServiceDescriptor.java deleted file mode 100644 index 5ae07d3bd9198..0000000000000 --- a/telecomm/java/android/telecomm/CallServiceDescriptor.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright 2014, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.telecomm; - -import android.content.ComponentName; -import android.content.Context; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -import java.util.Locale; -import java.util.UUID; - -/** - * An immutable object containing information about a given {@link ConnectionService}. Instances are - * created using the enclosed {@link Builder}. - */ -public final class CallServiceDescriptor implements Parcelable { - private static final String TAG = CallServiceDescriptor.class.getSimpleName(); - - /** - * A placeholder value indicating an invalid network type. - * @hide - */ - private static final int FLAG_INVALID = 0; - - /** - * Indicates that the device must be connected to a Wi-Fi network in order for the backing - * {@link ConnectionService} to be used. - */ - public static final int FLAG_WIFI = 0x01; - - /** - * Indicates that the device must be connected to a cellular PSTN network in order for the - * backing {@link ConnectionService} to be used. - */ - public static final int FLAG_PSTN = 0x02; - - /** - * Indicates that the device must be connected to a cellular data network in order for the - * backing {@link ConnectionService} to be used. - */ - public static final int FLAG_MOBILE = 0x04; - - /** - * Represents all of the defined FLAG_ constants so validity can be easily checked. - * @hide - */ - public static final int FLAG_ALL = FLAG_WIFI | FLAG_PSTN | FLAG_MOBILE; - - /** - * A unique ID used to identify a given instance. - */ - private final String mConnectionServiceId; - - /** - * The {@link ComponentName} of the {@link ConnectionService} implementation which this is - * describing. - */ - private final ComponentName mComponentName; - - /** - * The type of connection that the {@link ConnectionService} requires; will be one of the FLAG_* - * constants defined in this class. - */ - private final int mNetworkType; - - private CallServiceDescriptor( - String connectionServiceId, - ComponentName componentName, - int networkType) { - - mConnectionServiceId = connectionServiceId; - mComponentName = componentName; - mNetworkType = networkType; - } - - /** - * @return The ID used to identify this {@link ConnectionService}. - */ - public String getConnectionServiceId() { - return mConnectionServiceId; - } - - /** - * @return The {@link ComponentName} of the {@link ConnectionService}. - */ - public ComponentName getServiceComponent() { - return mComponentName; - } - - /** - * @return The network type required by the {@link ConnectionService} to place a call. - */ - public int getNetworkType() { - return mNetworkType; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (!(obj instanceof CallServiceDescriptor)) { - return false; - } - CallServiceDescriptor descriptor = (CallServiceDescriptor) obj; - return mConnectionServiceId.equals(descriptor.mConnectionServiceId) && - mComponentName.equals(descriptor.mComponentName) && - mNetworkType == descriptor.mNetworkType; - } - - @Override - public String toString() { - return String.format(Locale.US, "[%s, component: %s]", - CallServiceDescriptor.class.getSimpleName(), - mComponentName == null ? "null" : mComponentName.flattenToShortString()); - } - - /** - * @param context {@link Context} to use for the construction of the {@link Builder}. - * @return A new {@link Builder} instance. - */ - public static Builder newBuilder(Context context) { - return new Builder(context); - } - - /** - * Creates {@link CallServiceDescriptor} instances. Builders should be created with the - * {@link CallServiceDescriptor#newBuilder(Context)} method. - */ - public static class Builder { - /** The {@link Context} to use to verify {@link ComponentName} ownership. */ - private Context mContext; - - /** The {@link ComponentName} pointing to the backing {@link ConnectionService}. */ - private ComponentName mComponentName; - - /** The required network type that the {@link ConnectionService} needs. */ - private int mNetworkType = FLAG_INVALID; - - private Builder(Context context) { - mContext = context; - } - - /** - * Set which {@link ConnectionService} this {@link CallServiceDescriptor} is describing. - * - * @param serviceClass The {@link ConnectionService} class - * @return This {@link Builder} for method chaining. - */ - public Builder setConnectionService(Class serviceClass) { - mComponentName = new ComponentName(mContext, serviceClass); - return this; - } - - /** - * Which network type the backing {@link ConnectionService} requires. This must be one of - * the {@link CallServiceDescriptor}.TYPE_* fields. - * - * @param networkType Which network type the backing {@link ConnectionService} requires. - * @return This {@link Builder} for method chaining. - */ - public Builder setNetworkType(int networkType) { - mNetworkType = networkType; - return this; - } - - /** - * @return A constructed {@link CallServiceDescriptor} object. - */ - public CallServiceDescriptor build() { - // STOPSHIP: Verify validity of ComponentName (permissions, intents, etc) - - // Make sure that they passed in a valid network flag combination - if (mNetworkType == FLAG_INVALID || ((mNetworkType & FLAG_ALL) == 0)) { - - Log.wtf(TAG, "Invalid network type for " + mComponentName); - // Revert them back to TYPE_INVALID so it won't be considered. - mNetworkType = FLAG_INVALID; - } - - // TODO: Should we use a sha1 of the ComponentName? Would prevent duplicates. - return new CallServiceDescriptor( - UUID.randomUUID().toString(), mComponentName, mNetworkType); - } - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(mConnectionServiceId); - dest.writeParcelable(mComponentName, 0); - dest.writeInt(mNetworkType); - } - - public static final Creator CREATOR = - new Creator() { - @Override - public CallServiceDescriptor createFromParcel(Parcel source) { - String id = source.readString(); - ComponentName componentName = source.readParcelable( - CallServiceDescriptor.class.getClassLoader()); - int networkType = source.readInt(); - - return new CallServiceDescriptor(id, componentName, networkType); - } - - @Override - public CallServiceDescriptor[] newArray(int size) { - return new CallServiceDescriptor[size]; - } - }; -} diff --git a/telecomm/java/android/telecomm/CallServiceLookupResponse.java b/telecomm/java/android/telecomm/CallServiceLookupResponse.java deleted file mode 100644 index dd35a2439d8a4..0000000000000 --- a/telecomm/java/android/telecomm/CallServiceLookupResponse.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.telecomm; - -import android.os.RemoteException; - -import com.android.internal.telecomm.ICallServiceLookupResponse; - -import java.util.List; - -/** - * Used by {@link CallServiceProvider} to return a list of {@link CallServiceDescriptor}s. - */ -public final class CallServiceLookupResponse { - private final ICallServiceLookupResponse mResponse; - - /** - * {@hide} - */ - public CallServiceLookupResponse(ICallServiceLookupResponse response) { - mResponse = response; - } - - /** - * Passes the sorted list of preferred {@link CallServiceDescriptor}s back to Telecomm. Used - * in the context of attempting to place a pending outgoing call. - * - * @param callServiceDescriptors The set of call-service descriptors from - * {@link CallServiceProvider}. - */ - public void setCallServiceDescriptors(List callServiceDescriptors) { - try { - mResponse.setCallServiceDescriptors(callServiceDescriptors); - } catch (RemoteException e) { - } - } -} diff --git a/telecomm/java/android/telecomm/CallServiceProvider.java b/telecomm/java/android/telecomm/CallServiceProvider.java deleted file mode 100644 index c50334aeee512..0000000000000 --- a/telecomm/java/android/telecomm/CallServiceProvider.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.telecomm; - -import android.app.Service; -import android.content.Intent; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; - -import com.android.internal.telecomm.ICallServiceLookupResponse; -import com.android.internal.telecomm.ICallServiceProvider; - -/** - * Base implementation of a call service provider which extends {@link Service}. This class - * should be extended by an app that wants to supply phone calls to be handled and managed by - * the device's in-call interface. All method-calls from the framework to the call service provider - * are passed through to the main thread for before executing the overriden methods of - * CallServiceProvider. - * - * TODO(santoscordon): Improve paragraph above once the final design is in place. Needs more - * about how this can be used. - */ -public abstract class CallServiceProvider extends Service { - - /** - * Default Handler used to consolidate binder method calls onto a single thread. - */ - private final class CallServiceProviderMessageHandler extends Handler { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_LOOKUP_CALL_SERVICES: - CallServiceLookupResponse response = - new CallServiceLookupResponse((ICallServiceLookupResponse) msg.obj); - lookupCallServices(response); - break; - } - } - } - - /** - * Default ICallServiceProvider implementation provided to CallsManager via {@link #onBind}. - */ - private final class CallServiceProviderWrapper extends ICallServiceProvider.Stub { - /** {@inheritDoc} */ - @Override - public void lookupCallServices(ICallServiceLookupResponse callServiceLookupResponse) { - Message message = mMessageHandler.obtainMessage( - MSG_LOOKUP_CALL_SERVICES, callServiceLookupResponse); - message.sendToTarget(); - } - } - - // Only used internally by this class. - // Binder method calls on this service can occur on multiple threads. These messages are used - // in conjunction with {@link #mMessageHandler} to ensure that all callbacks are handled on a - // single thread. Keeping it on a single thread allows CallService implementations to avoid - // needing multi-threaded code in their own callback routines. - private static final int MSG_LOOKUP_CALL_SERVICES = 1; - - /** - * Message handler for consolidating binder callbacks onto a single thread. - * See {@link CallServiceProviderMessageHandler}. - */ - private final CallServiceProviderMessageHandler mMessageHandler; - - /** - * Default binder implementation of {@link ICallServiceProvider} interface. - */ - private final CallServiceProviderWrapper mBinder; - - /** - * Protected constructor called only by subclasses creates the binder interface and - * single-threaded message handler. - */ - protected CallServiceProvider() { - mMessageHandler = new CallServiceProviderMessageHandler(); - mBinder = new CallServiceProviderWrapper(); - } - - /** {@inheritDoc} */ - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - /** - * Initiates the process to retrieve the list of {@link CallServiceDescriptor}s implemented by - * this provider. - * - * @param response The response object through which the list of call services is sent. - */ - public abstract void lookupCallServices(CallServiceLookupResponse response); -} diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java index 7c35020b4c3eb..355c260566000 100644 --- a/telecomm/java/android/telecomm/InCallCall.java +++ b/telecomm/java/android/telecomm/InCallCall.java @@ -45,7 +45,6 @@ public final class InCallCall implements Parcelable { private final int mCallerDisplayNamePresentation; private final GatewayInfo mGatewayInfo; private final PhoneAccount mAccount; - private final CallServiceDescriptor mCurrentCallServiceDescriptor; private final ICallVideoProvider mCallVideoProvider; private RemoteCallVideoProvider mRemoteCallVideoProvider; private final String mParentCallId; @@ -67,7 +66,6 @@ public final class InCallCall implements Parcelable { int callerDisplayNamePresentation, GatewayInfo gatewayInfo, PhoneAccount account, - CallServiceDescriptor descriptor, ICallVideoProvider callVideoProvider, String parentCallId, List childCallIds, @@ -85,7 +83,6 @@ public final class InCallCall implements Parcelable { mCallerDisplayNamePresentation = callerDisplayNamePresentation; mGatewayInfo = gatewayInfo; mAccount = account; - mCurrentCallServiceDescriptor = descriptor; mCallVideoProvider = callVideoProvider; mParentCallId = parentCallId; mChildCallIds = childCallIds; @@ -165,11 +162,6 @@ public final class InCallCall implements Parcelable { return mAccount; } - /** The descriptor for the call service currently routing this call. */ - public CallServiceDescriptor getCurrentCallServiceDescriptor() { - return mCurrentCallServiceDescriptor; - } - /** * Returns an object for remotely communicating through the call video provider's binder. * @return The call video provider. @@ -232,7 +224,6 @@ public final class InCallCall implements Parcelable { int callerDisplayNamePresentation = source.readInt(); GatewayInfo gatewayInfo = source.readParcelable(classLoader); PhoneAccount account = source.readParcelable(classLoader); - CallServiceDescriptor descriptor = source.readParcelable(classLoader); ICallVideoProvider callVideoProvider = ICallVideoProvider.Stub.asInterface(source.readStrongBinder()); String parentCallId = source.readString(); @@ -242,8 +233,7 @@ public final class InCallCall implements Parcelable { return new InCallCall(id, state, disconnectCauseCode, disconnectCauseMsg, cannedSmsResponses, capabilities, connectTimeMillis, handle, handlePresentation, callerDisplayName, callerDisplayNamePresentation, gatewayInfo, - account, descriptor, callVideoProvider, parentCallId, childCallIds, - statusHints); + account, callVideoProvider, parentCallId, childCallIds, statusHints); } @Override @@ -274,7 +264,6 @@ public final class InCallCall implements Parcelable { destination.writeInt(mCallerDisplayNamePresentation); destination.writeParcelable(mGatewayInfo, 0); destination.writeParcelable(mAccount, 0); - destination.writeParcelable(mCurrentCallServiceDescriptor, 0); destination.writeStrongBinder( mCallVideoProvider != null ? mCallVideoProvider.asBinder() : null); destination.writeString(mParentCallId); diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java index b9fb40cb9587b..6334702b233af 100644 --- a/telecomm/java/android/telecomm/TelecommConstants.java +++ b/telecomm/java/android/telecomm/TelecommConstants.java @@ -16,6 +16,7 @@ package android.telecomm; +import android.content.ComponentName; import android.content.Intent; import android.os.Bundle; import android.telephony.TelephonyManager; @@ -31,20 +32,15 @@ public final class TelecommConstants { * to find and bind to the appropriate {@link android.telecomm.ConnectionService} which * Telecomm will ultimately use to control and get information about the call.

* - *

Input: get*Extra field {@link #EXTRA_CALL_SERVICE_DESCRIPTOR} contains the component name - * of the {@link android.telecomm.ConnectionService} that Telecomm should bind to. Telecomm - * will then ask the call service for more information about the call prior to showing any UI. + *

Input: get*Extra field {@link #EXTRA_PHONE_ACCOUNT} contains the component name of the + * {@link android.telecomm.ConnectionService} that Telecomm should bind to. Telecomm will then + * ask the connection service for more information about the call prior to showing any UI. * * TODO(santoscordon): Needs permissions. * TODO(santoscordon): Consider moving this into a simple method call on a system service. */ public static final String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL"; - /** - * The service action used to bind to {@link CallServiceProvider} implementations. - */ - public static final String ACTION_CALL_SERVICE_PROVIDER = CallServiceProvider.class.getName(); - /** * The service action used to bind to {@link ConnectionService} implementations. */ @@ -69,11 +65,15 @@ public final class TelecommConstants { "android.intent.extra.START_CALL_WITH_VIDEO_STATE"; /** - * Extra for {@link #ACTION_INCOMING_CALL} containing the {@link CallServiceDescriptor} that - * describes the call service to use for the incoming call. + * The extra used with an {@link android.content.Intent#ACTION_CALL}, + * {@link #ACTION_INCOMING_CALL}, {@link android.content.Intent#ACTION_DIAL} {@code Intent} to + * specify a {@link PhoneAccount} to use when making the call. + * + *

+ * Retrieve with + * {@link android.content.Intent#getParcelableExtra(String)}. */ - public static final String EXTRA_CALL_SERVICE_DESCRIPTOR = - "android.intent.extra.CALL_SERVICE_DESCRIPTOR"; + public static final String EXTRA_PHONE_ACCOUNT = "android.intent.extra.PHONE_ACCOUNT"; /** * Optional extra for {@link #ACTION_INCOMING_CALL} containing a {@link Bundle} which contains diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java index fcd2eba7116f5..9d1e8f568f548 100644 --- a/telecomm/java/android/telecomm/TelecommManager.java +++ b/telecomm/java/android/telecomm/TelecommManager.java @@ -31,18 +31,6 @@ import java.util.List; * @hide */ public class TelecommManager { - - /** - * The extra used with an {@link android.content.Intent#ACTION_CALL} or - * {@link android.content.Intent#ACTION_DIAL} {@code Intent} to specify a {@link PhoneAccount} - * to use when making the call. - * - *

- * Retrieve with - * {@link android.content.Intent#getParcelableExtra(String)}. - */ - public static final String EXTRA_PHONE_ACCOUNT = "account"; - private static final String TAG = "TelecommManager"; private static final String TELECOMM_SERVICE_NAME = "telecomm"; diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl deleted file mode 100644 index 10d73be7af05f..0000000000000 --- a/telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telecomm; - -import android.os.IBinder; -import android.telecomm.CallServiceDescriptor; -import java.util.List; - -/** - * Internal remote interface for call service lookup response. - * - * @see android.telecomm.CallServiceLookupResponse - * - * @hide - */ -oneway interface ICallServiceLookupResponse { - void setCallServiceDescriptors(in List callServiceDescriptors); -} diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceProvider.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceProvider.aidl deleted file mode 100644 index 96daeeda279e6..0000000000000 --- a/telecomm/java/com/android/internal/telecomm/ICallServiceProvider.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telecomm; - -import android.telecomm.CallServiceDescriptor; - -import com.android.internal.telecomm.ICallServiceLookupResponse; - -/** - * Internal remote interface for call service providers. - * - * @see android.telecomm.CallServiceProvider - * - * @hide - */ -oneway interface ICallServiceProvider { - void lookupCallServices(in ICallServiceLookupResponse response); -} diff --git a/telephony/java/com/android/internal/telephony/IThirdPartyCallListener.aidl b/telephony/java/com/android/internal/telephony/IThirdPartyCallListener.aidl deleted file mode 100644 index bcf2d81d9b636..0000000000000 --- a/telephony/java/com/android/internal/telephony/IThirdPartyCallListener.aidl +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony; - -import com.android.internal.telephony.IThirdPartyCallProvider; - -/** - * Interface provided to ThirdPartyCallService. The service can use this to notify the listener of - * changes to the call state. - */ -oneway interface IThirdPartyCallListener { - /** - * Called by the service when a call provider is available to perform the outgoing or incoming - * call. - */ - void onCallProviderAttached(IThirdPartyCallProvider callProvider); - - /** - * Notifies the listener that ringing has started for this call. - */ - void onRingingStarted(); - - /** - * Notifies the listener that the call has been successfully established. - */ - void onCallEstablished(); - - /** - * Notifies the listener that the call has ended. - */ - void onCallEnded(int reason); -} diff --git a/telephony/java/com/android/internal/telephony/IThirdPartyCallProvider.aidl b/telephony/java/com/android/internal/telephony/IThirdPartyCallProvider.aidl deleted file mode 100644 index 9d595b0b1b5a1..0000000000000 --- a/telephony/java/com/android/internal/telephony/IThirdPartyCallProvider.aidl +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony; - -import com.android.internal.telephony.IThirdPartyCallListener; -import com.android.internal.telephony.IThirdPartyCallSendDtmfCallback; - -/** - * Interface sent to ThirdPartyCallListener.onCallProviderAttached. This is used to control an - * outgoing or incoming call. - */ -oneway interface IThirdPartyCallProvider { - /** - * Mutes or unmutes the call. - */ - void mute(boolean shouldMute); - - /** - * Ends the current call. If this is an unanswered incoming call then the call is rejected (for - * example, a notification is sent to a server that the user declined the call). - */ - void hangup(); - - /** - * Accepts the incoming call. - */ - void incomingCallAccept(); - - /** - * Sends the given DTMF code. The code can be '0'-'9', 'A'-'D', '#', or '*'. - */ - void sendDtmf(char c, IThirdPartyCallSendDtmfCallback callback); -} diff --git a/telephony/java/com/android/internal/telephony/IThirdPartyCallSendDtmfCallback.aidl b/telephony/java/com/android/internal/telephony/IThirdPartyCallSendDtmfCallback.aidl deleted file mode 100644 index 3a02b06055d06..0000000000000 --- a/telephony/java/com/android/internal/telephony/IThirdPartyCallSendDtmfCallback.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony; - -/** - * Callback interface for when DTMF has been sent. - */ -oneway interface IThirdPartyCallSendDtmfCallback { - /** - * Called when the DTMF code has been sent. - */ - void onSendDtmfCompleted(); -} diff --git a/telephony/java/com/android/internal/telephony/IThirdPartyCallService.aidl b/telephony/java/com/android/internal/telephony/IThirdPartyCallService.aidl deleted file mode 100644 index 597567af02eae..0000000000000 --- a/telephony/java/com/android/internal/telephony/IThirdPartyCallService.aidl +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony; - -import com.android.internal.telephony.IThirdPartyCallListener; - -/** - * Interface provided by a service to start outgoing calls and attach to incoming calls. - */ -oneway interface IThirdPartyCallService { - /** - * Call to start a new outgoing call. - */ - void outgoingCallInitiate(IThirdPartyCallListener listener, String number); - - /** - * Call to attach to an incoming call. This is in response to a call to - * TelephonyManager.newIncomingThirdPartyCall. - */ - void incomingCallAttach(IThirdPartyCallListener listener, String callId); -} From 9e6d9d4c812d01ac07180c5d6ab64c36fb9b2aa7 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Mon, 14 Jul 2014 16:28:24 +0100 Subject: [PATCH 23/75] Add an internal API to get all asset locales. This will allow us to not copy paste this code verbatim into bundled apps. bug: 10090157 Change-Id: I008dc683ecbef2ad8b7a26968cb3cbda7e5a8388 --- .../android/internal/app/LocalePicker.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java index f5c498a8fbc91..229df8f23293d 100644 --- a/core/java/com/android/internal/app/LocalePicker.java +++ b/core/java/com/android/internal/app/LocalePicker.java @@ -36,7 +36,6 @@ import android.widget.ListView; import android.widget.TextView; import java.text.Collator; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -106,22 +105,21 @@ public class LocalePicker extends ListFragment { return constructAdapter(context, layoutId, fieldId, false /* disable pseudolocales */); } - public static ArrayAdapter constructAdapter(Context context, - final int layoutId, final int fieldId, final boolean isInDeveloperMode) { + public static List getAllAssetLocales(Context context, boolean isInDeveloperMode) { final Resources resources = context.getResources(); - String[] locales = Resources.getSystem().getAssets().getLocales(); + final String[] locales = Resources.getSystem().getAssets().getLocales(); List localeList = new ArrayList(locales.length); Collections.addAll(localeList, locales); if (isInDeveloperMode) { if (!localeList.contains("zz_ZZ")) { localeList.add("zz_ZZ"); } - /** - TODO: Enable when zz_ZY Pseudolocale is complete - * if (!localeList.contains("zz_ZY")) { - * localeList.add("zz_ZY"); - * } - */ + /** - TODO: Enable when zz_ZY Pseudolocale is complete + * if (!localeList.contains("zz_ZY")) { + * localeList.add("zz_ZY"); + * } + */ } Collections.sort(localeList); @@ -179,6 +177,13 @@ public class LocalePicker extends ListFragment { } Collections.sort(localeInfos); + return localeInfos; + } + + public static ArrayAdapter constructAdapter(Context context, + final int layoutId, final int fieldId, final boolean isInDeveloperMode) { + final List localeInfos = getAllAssetLocales(context, isInDeveloperMode); + final LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); return new ArrayAdapter(context, layoutId, fieldId, localeInfos) { From 0f7bbf8dcd13763ec88c1ad50a85c3256760b53f Mon Sep 17 00:00:00 2001 From: Ihab Awad Date: Wed, 9 Jul 2014 21:52:04 -0700 Subject: [PATCH 24/75] Introduce new In-Call Service interface Change-Id: I2dd8494f6e397c49180b19d1347c62edcae9b4e7 --- api/current.txt | 113 ++- telecomm/java/android/telecomm/Call.java | 710 ++++++++++++++++++ telecomm/java/android/telecomm/CallState.java | 16 - .../android/telecomm/CallVideoClient.java | 3 +- .../java/android/telecomm/InCallAdapter.java | 9 +- .../java/android/telecomm/InCallService.java | 113 +-- telecomm/java/android/telecomm/Phone.java | 255 +++++++ .../telecomm/RemoteCallVideoProvider.java | 81 +- 8 files changed, 1148 insertions(+), 152 deletions(-) create mode 100644 telecomm/java/android/telecomm/Call.java create mode 100644 telecomm/java/android/telecomm/Phone.java diff --git a/api/current.txt b/api/current.txt index 41aad5f59f4b2..202f1ec87a4a8 100644 --- a/api/current.txt +++ b/api/current.txt @@ -16268,8 +16268,8 @@ package android.media.tv { method public void onChannelRetuned(java.lang.String, android.net.Uri); method public void onError(java.lang.String, int); method public void onTrackInfoChanged(java.lang.String, java.util.List); - method public void onVideoSizeChanged(java.lang.String, int, int); method public void onVideoAvailable(java.lang.String); + method public void onVideoSizeChanged(java.lang.String, int, int); method public void onVideoUnavailable(java.lang.String, int); } @@ -27593,6 +27593,62 @@ package android.system { package android.telecomm { + public final class Call { + method public void addListener(android.telecomm.Call.Listener); + method public void answer(); + method public void conference(); + method public void disconnect(); + method public android.telecomm.RemoteCallVideoProvider getCallVideoProvider(); + method public java.util.List getCannedTextResponses(); + method public java.util.List getChildren(); + method public android.telecomm.Call.Details getDetails(); + method public android.telecomm.Call getParent(); + method public java.lang.String getRemainingPostDialSequence(); + method public int getState(); + method public void hold(); + method public void phoneAccountClicked(); + method public void playDtmfTone(char); + method public void postDialContinue(boolean); + method public void reject(boolean, java.lang.String); + method public void removeListener(android.telecomm.Call.Listener); + method public void splitFromConference(); + method public void stopDtmfTone(); + method public void swapWithBackgroundCall(); + method public void unhold(); + field public static final int STATE_ACTIVE = 4; // 0x4 + field public static final int STATE_DIALING = 1; // 0x1 + field public static final int STATE_DISCONNECTED = 7; // 0x7 + field public static final int STATE_HOLDING = 3; // 0x3 + field public static final int STATE_NEW = 0; // 0x0 + field public static final int STATE_RINGING = 2; // 0x2 + } + + public static class Call.Details { + method public android.telecomm.PhoneAccount getAccount(); + method public java.lang.String getCallerDisplayName(); + method public int getCallerDisplayNamePresentation(); + method public int getCapabilities(); + method public long getConnectTimeMillis(); + method public int getDisconnectCauseCode(); + method public java.lang.String getDisconnectCauseMsg(); + method public android.telecomm.GatewayInfo getGatewayInfo(); + method public android.net.Uri getHandle(); + method public int getHandlePresentation(); + } + + public static abstract class Call.Listener { + ctor public Call.Listener(); + method public void onCallDestroyed(android.telecomm.Call); + method public void onCallVideoProviderChanged(android.telecomm.Call, android.telecomm.RemoteCallVideoProvider); + method public void onCannedTextResponsesLoaded(android.telecomm.Call, java.util.List); + method public void onChildrenChanged(android.telecomm.Call, java.util.List); + method public void onDetailsChanged(android.telecomm.Call, android.telecomm.Call.Details); + method public void onParentChanged(android.telecomm.Call, android.telecomm.Call); + method public void onPostDial(android.telecomm.Call, java.lang.String); + method public void onPostDialWait(android.telecomm.Call, java.lang.String); + method public void onStateChanged(android.telecomm.Call, int); + } + public final class CallAudioState implements android.os.Parcelable { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); @@ -27679,8 +27735,6 @@ package android.telecomm { enum_constant public static final android.telecomm.CallState DISCONNECTED; enum_constant public static final android.telecomm.CallState NEW; enum_constant public static final android.telecomm.CallState ON_HOLD; - enum_constant public static final android.telecomm.CallState POST_DIAL; - enum_constant public static final android.telecomm.CallState POST_DIAL_WAIT; enum_constant public static final android.telecomm.CallState RINGING; } @@ -27855,17 +27909,29 @@ package android.telecomm { field public static final android.os.Parcelable.Creator CREATOR; } - public abstract class InCallService extends android.app.Service { + public abstract class InCallService { ctor protected InCallService(); - method protected abstract void addCall(android.telecomm.InCallCall); - method protected abstract void bringToForeground(boolean); - method protected final android.telecomm.InCallAdapter getAdapter(); - method protected void onAdapterAttached(android.telecomm.InCallAdapter); - method protected abstract void onAudioStateChanged(android.telecomm.CallAudioState); - method public final android.os.IBinder onBind(android.content.Intent); - method protected abstract void setPostDial(java.lang.String, java.lang.String); - method protected abstract void setPostDialWait(java.lang.String, java.lang.String); - method protected abstract void updateCall(android.telecomm.InCallCall); + method public final android.os.IBinder getBinder(); + method public android.telecomm.Phone getPhone(); + method public void onPhoneCreated(android.telecomm.Phone); + method public void onPhoneDestroyed(android.telecomm.Phone); + } + + public final class Phone { + method public final void addListener(android.telecomm.Phone.Listener); + method public final android.telecomm.CallAudioState getAudioState(); + method public final java.util.List getCalls(); + method public final void removeListener(android.telecomm.Phone.Listener); + method public final void setAudioRoute(int); + method public final void setMuted(boolean); + } + + public static abstract class Phone.Listener { + ctor public Phone.Listener(); + method public void onAudioStateChanged(android.telecomm.Phone, android.telecomm.CallAudioState); + method public void onBringToForeground(android.telecomm.Phone, boolean); + method public void onCallAdded(android.telecomm.Phone, android.telecomm.Call); + method public void onCallRemoved(android.telecomm.Phone, android.telecomm.Call); } public class PhoneAccount implements android.os.Parcelable { @@ -27903,18 +27969,17 @@ package android.telecomm { method public void updatePeerDimensions(int, int) throws android.os.RemoteException; } - public class RemoteCallVideoProvider implements android.os.IBinder.DeathRecipient { - method public void binderDied(); - method public void requestCallDataUsage() throws android.os.RemoteException; - method public void requestCameraCapabilities() throws android.os.RemoteException; - method public void sendSessionModifyRequest(android.telecomm.VideoCallProfile) throws android.os.RemoteException; - method public void sendSessionModifyResponse(android.telecomm.VideoCallProfile) throws android.os.RemoteException; - method public void setCallVideoClient(android.telecomm.CallVideoClient) throws android.os.RemoteException; + public class RemoteCallVideoProvider { + method public void requestCallDataUsage(); + method public void requestCameraCapabilities(); + method public void sendSessionModifyRequest(android.telecomm.VideoCallProfile); + method public void sendSessionModifyResponse(android.telecomm.VideoCallProfile); + method public void setCallVideoClient(android.telecomm.CallVideoClient); method public void setCamera(java.lang.String) throws android.os.RemoteException; - method public void setDeviceOrientation(int) throws android.os.RemoteException; - method public void setDisplaySurface(android.view.Surface) throws android.os.RemoteException; - method public void setPauseImage(java.lang.String) throws android.os.RemoteException; - method public void setPreviewSurface(android.view.Surface) throws android.os.RemoteException; + method public void setDeviceOrientation(int); + method public void setDisplaySurface(android.view.Surface); + method public void setPauseImage(java.lang.String); + method public void setPreviewSurface(android.view.Surface); method public void setZoom(float) throws android.os.RemoteException; } diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java new file mode 100644 index 0000000000000..ba7e253fc824f --- /dev/null +++ b/telecomm/java/android/telecomm/Call.java @@ -0,0 +1,710 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +import android.net.Uri; +import android.os.RemoteException; +import android.telephony.DisconnectCause; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Represents an ongoing phone call that the in-call app should present to the user. + */ +public final class Call { + /** + * The state of a {@code Call} when newly created. + */ + public static final int STATE_NEW = 0; + + /** + * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected. + */ + public static final int STATE_DIALING = 1; + + /** + * The state of an incoming {@code Call} when ringing locally, but not yet connected. + */ + public static final int STATE_RINGING = 2; + + /** + * The state of a {@code Call} when in a holding state. + */ + public static final int STATE_HOLDING = 3; + + /** + * The state of a {@code Call} when actively supporting conversation. + */ + public static final int STATE_ACTIVE = 4; + + /** + * The state of a {@code Call} when no further voice or other communication is being + * transmitted, the remote side has been or will inevitably be informed that the {@code Call} + * is no longer active, and the local data transport has or inevitably will release resources + * associated with this {@code Call}. + */ + public static final int STATE_DISCONNECTED = 7; + + public static class Details { + private final Uri mHandle; + private final int mHandlePresentation; + private final String mCallerDisplayName; + private final int mCallerDisplayNamePresentation; + private final PhoneAccount mAccount; + private final int mCapabilities; + private final int mDisconnectCauseCode; + private final String mDisconnectCauseMsg; + private final long mConnectTimeMillis; + private final GatewayInfo mGatewayInfo; + + /** + * @return The handle (e.g., phone number) to which the {@code Call} is currently + * connected. + */ + public Uri getHandle() { + return mHandle; + } + + /** + * @return The presentation requirements for the handle. See + * {@link android.telecomm.CallPropertyPresentation} for valid values. + */ + public int getHandlePresentation() { + return mHandlePresentation; + } + + /** + * @return The display name for the caller. + */ + public String getCallerDisplayName() { + return mCallerDisplayName; + } + + /** + * @return The presentation requirements for the caller display name. See + * {@link android.telecomm.CallPropertyPresentation} for valid values. + */ + public int getCallerDisplayNamePresentation() { + return mCallerDisplayNamePresentation; + } + + /** + * @return The {@code PhoneAccount} whereby the {@code Call} is currently being routed. + */ + public PhoneAccount getAccount() { + return mAccount; + } + + /** + * @return A bitmask of the capabilities of the {@code Call}, as defined in + * {@link CallCapabilities}. + */ + public int getCapabilities() { + return mCapabilities; + } + + /** + * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed + * as a code chosen from among those declared in {@link DisconnectCause}. + */ + public int getDisconnectCauseCode() { + return mDisconnectCauseCode; + } + + /** + * @return For a {@link #STATE_DISCONNECTED} {@code Call}, an optional reason for + * disconnection expressed as a free text message. + */ + public String getDisconnectCauseMsg() { + return mDisconnectCauseMsg; + } + + /** + * @return The time the {@code Call} has been connected. This information is updated + * periodically, but user interfaces should not rely on this to display any "call time + * clock". + */ + public long getConnectTimeMillis() { + return mConnectTimeMillis; + } + + /** + * @return Information about any calling gateway the {@code Call} may be using. + */ + public GatewayInfo getGatewayInfo() { + return mGatewayInfo; + } + + @Override + public boolean equals(Object o) { + if (o instanceof Details) { + Details d = (Details) o; + return + Objects.equals(mHandle, d.mHandle) && + Objects.equals(mHandlePresentation, d.mHandlePresentation) && + Objects.equals(mCallerDisplayName, d.mCallerDisplayName) && + Objects.equals(mCallerDisplayNamePresentation, + d.mCallerDisplayNamePresentation) && + Objects.equals(mAccount, d.mAccount) && + Objects.equals(mCapabilities, d.mCapabilities) && + Objects.equals(mDisconnectCauseCode, d.mDisconnectCauseCode) && + Objects.equals(mDisconnectCauseMsg, d.mDisconnectCauseMsg) && + Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) && + Objects.equals(mGatewayInfo, d.mGatewayInfo); + } + return false; + } + + @Override + public int hashCode() { + return + Objects.hashCode(mHandle) + + Objects.hashCode(mHandlePresentation) + + Objects.hashCode(mCallerDisplayName) + + Objects.hashCode(mCallerDisplayNamePresentation) + + Objects.hashCode(mAccount) + + Objects.hashCode(mCapabilities) + + Objects.hashCode(mDisconnectCauseCode) + + Objects.hashCode(mDisconnectCauseMsg) + + Objects.hashCode(mConnectTimeMillis) + + Objects.hashCode(mGatewayInfo); + } + + /** {@hide} */ + public Details( + Uri handle, + int handlePresentation, + String callerDisplayName, + int callerDisplayNamePresentation, + PhoneAccount account, + int capabilities, + int disconnectCauseCode, + String disconnectCauseMsg, + long connectTimeMillis, + GatewayInfo gatewayInfo) { + mHandle = handle; + mHandlePresentation = handlePresentation; + mCallerDisplayName = callerDisplayName; + mCallerDisplayNamePresentation = callerDisplayNamePresentation; + mAccount = account; + mCapabilities = capabilities; + mDisconnectCauseCode = disconnectCauseCode; + mDisconnectCauseMsg = disconnectCauseMsg; + mConnectTimeMillis = connectTimeMillis; + mGatewayInfo = gatewayInfo; + } + } + + public static abstract class Listener { + /** + * Invoked when the state of this {@code Call} has changed. See {@link #getState()}. + * + * TODO(ihab): Provide previous state also? + * + * @param call The {@code Call} invoking this method. + * @param state The new state of the {@code Call}. + */ + public void onStateChanged(Call call, int state) {} + + /** + * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}. + * + * @param call The {@code Call} invoking this method. + * @param parent The new parent of the {@code Call}. + */ + public void onParentChanged(Call call, Call parent) {} + + /** + * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}. + * + * @param call The {@code Call} invoking this method. + * @param children The new children of the {@code Call}. + */ + public void onChildrenChanged(Call call, List children) {} + + /** + * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}. + * + * @param call The {@code Call} invoking this method. + * @param details A {@code Details} object describing the {@code Call}. + */ + public void onDetailsChanged(Call call, Details details) {} + + /** + * Invoked when the text messages that can be used as responses to the incoming + * {@code Call} are loaded from the relevant database. + * See {@link #getCannedTextResponses()}. + * + * @param call The {@code Call} invoking this method. + * @param cannedTextResponses The text messages useable as responses. + */ + public void onCannedTextResponsesLoaded(Call call, List cannedTextResponses) {} + + /** + * Invoked when the outgoing {@code Call} has finished dialing but is sending DTMF signals + * that were embedded into the outgoing number. + * + * @param call The {@code Call} invoking this method. + * @param remainingPostDialSequence The post-dial characters that remain to be sent. + */ + public void onPostDial(Call call, String remainingPostDialSequence) {} + + /** + * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause + * character. This causes the post-dial signals to stop pending user confirmation. An + * implementation should present this choice to the user and invoke + * {@link #postDialContinue(boolean)} when the user makes the choice. + * + * @param call The {@code Call} invoking this method. + * @param remainingPostDialSequence The post-dial characters that remain to be sent. + */ + public void onPostDialWait(Call call, String remainingPostDialSequence) {} + + /** + * Invoked when the {@code RemoteCallVideoProvider} of the {@code Call} has changed. + * + * @param call The {@code Call} invoking this method. + * @param callVideoProvider The {@code RemoteCallVideoProvider} associated with the + * {@code Call}. + */ + + public void onCallVideoProviderChanged(Call call, + RemoteCallVideoProvider callVideoProvider) {} + + /** + * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning + * up their UI for the {@code Call} in response to state transitions. Specifically, + * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of + * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather, + * clients should wait for this method to be invoked. + * + * @param call The {@code Call} being destroyed. + */ + public void onCallDestroyed(Call call) {} + } + + private final Phone mPhone; + private final String mTelecommCallId; + private final InCallAdapter mInCallAdapter; + private Call mParent = null; + private int mState; + private final List mChildren = new ArrayList<>(); + private final List mUnmodifiableChildren = Collections.unmodifiableList(mChildren); + private List mCannedTextResponses = null; + private String mRemainingPostDialSequence; + private RemoteCallVideoProvider mCallVideoProvider; + private Details mDetails; + private final List mListeners = new ArrayList<>(); + + /** + * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any. + * + * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence + * remaining or this {@code Call} is not in a post-dial state. + */ + public String getRemainingPostDialSequence() { + return mRemainingPostDialSequence; + } + + /** + * Instructs this {@link #STATE_RINGING} {@code Call} to answer. + */ + public void answer() { + mInCallAdapter.answerCall(mTelecommCallId); + } + + /** + * Instructs this {@link #STATE_RINGING} {@code Call} to reject. + * + * @param rejectWithMessage Whether to reject with a text message. + * @param textMessage An optional text message with which to respond. + */ + public void reject(boolean rejectWithMessage, String textMessage) { + mInCallAdapter.rejectCall(mTelecommCallId, rejectWithMessage, textMessage); + } + + /** + * Instructs this {@code Call} to disconnect. + */ + public void disconnect() { + mInCallAdapter.disconnectCall(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to go on hold. + */ + public void hold() { + mInCallAdapter.holdCall(mTelecommCallId); + } + + /** + * Instructs this {@link #STATE_HOLDING} call to release from hold. + */ + public void unhold() { + mInCallAdapter.unholdCall(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone. + * + * Any other currently playing DTMF tone in the specified call is immediately stopped. + * + * @param digit A character representing the DTMF digit for which to play the tone. This + * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}. + */ + public void playDtmfTone(char digit) { + mInCallAdapter.playDtmfTone(mTelecommCallId, digit); + } + + /** + * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone + * currently playing. + * + * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is + * currently playing, this method will do nothing. + */ + public void stopDtmfTone() { + mInCallAdapter.stopDtmfTone(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to continue playing a post-dial DTMF string. + * + * A post-dial DTMF string is a string of digits entered after a phone number, when dialed, + * that are immediately sent as DTMF tones to the recipient as soon as the connection is made. + * While these tones are playing, this {@code Call} will notify listeners via + * {@link Listener#onPostDial(Call, String)}. + * + * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_PAUSE} symbol, this + * {@code Call} will temporarily pause playing the tones for a pre-defined period of time. + * + * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_WAIT} symbol, this + * {@code Call} will pause playing the tones and notify listeners via + * {@link Listener#onPostDialWait(Call, String)}. At this point, the in-call app + * should display to the user an indication of this state and an affordance to continue + * the postdial sequence. When the user decides to continue the postdial sequence, the in-call + * app should invoke the {@link #postDialContinue(boolean)} method. + * + * @param proceed Whether or not to continue with the post-dial sequence. + */ + public void postDialContinue(boolean proceed) { + mInCallAdapter.postDialContinue(mTelecommCallId, proceed); + } + + /** + * Notifies this {@code Call} that the phone account user interface element was touched. + * + * TODO(ihab): Figure out if and how we can generalize this + */ + public void phoneAccountClicked() { + mInCallAdapter.phoneAccountClicked(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to enter a conference. + */ + public void conference() { + mInCallAdapter.conference(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to split from any conference call with which it may be + * connected. + */ + public void splitFromConference() { + mInCallAdapter.splitFromConference(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to swap itself with an existing background call, if one + * such call exists. + */ + public void swapWithBackgroundCall() { + mInCallAdapter.swapWithBackgroundCall(mTelecommCallId); + } + + /** + * Obtains the parent of this {@code Call} in a conference, if any. + * + * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a + * child of any conference {@code Call}s. + */ + public Call getParent() { + return mParent; + } + + /** + * Obtains the children of this conference {@code Call}, if any. + * + * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty + * {@code List} otherwise. + */ + public List getChildren() { + return mUnmodifiableChildren; + } + + /** + * Obtains the state of this {@code Call}. + * + * @return A state value, chosen from the {@code STATE_*} constants. + */ + public int getState() { + return mState; + } + + /** + * Obtains a list of canned, pre-configured message responses to present to the user as + * ways of rejecting this {@code Call} using via a text message. + * + * @see #reject(boolean, String) + * + * @return A list of canned text message responses. + */ + public List getCannedTextResponses() { + return mCannedTextResponses; + } + + /** + * Obtains an object that can be used to display video from this {@code Call}. + * + * @return An {@code ICallVideoProvider}. + */ + public RemoteCallVideoProvider getCallVideoProvider() { + return mCallVideoProvider; + } + + /** + * Obtains an object containing call details. + * + * @return A {@link Details} object. Depending on the state of the {@code Call}, the + * result may be {@code null}. + */ + public Details getDetails() { + return mDetails; + } + + /** + * Adds a listener to this {@code Call}. + * + * @param listener A {@code Listener}. + */ + public void addListener(Listener listener) { + mListeners.add(listener); + } + + /** + * Removes a listener from this {@code Call}. + * + * @param listener A {@code Listener}. + */ + public void removeListener(Listener listener) { + mListeners.remove(listener); + } + + /** {@hide} */ + Call(Phone phone, String telecommCallId, InCallAdapter inCallAdapter) { + mPhone = phone; + mTelecommCallId = telecommCallId; + mInCallAdapter = inCallAdapter; + mState = STATE_NEW; + } + + /** {@hide} */ + final String internalGetCallId() { + return mTelecommCallId; + } + + /** {@hide} */ + final void internalUpdate(InCallCall inCallCall) { + // First, we update the internal state as far as possible before firing any updates. + + Details details = new Details( + inCallCall.getHandle(), + inCallCall.getHandlePresentation(), + inCallCall.getCallerDisplayName(), + inCallCall.getCallerDisplayNamePresentation(), + inCallCall.getAccount(), + inCallCall.getCapabilities(), + inCallCall.getDisconnectCauseCode(), + inCallCall.getDisconnectCauseMsg(), + inCallCall.getConnectTimeMillis(), + inCallCall.getGatewayInfo()); + boolean detailsChanged = !Objects.equals(mDetails, details); + if (detailsChanged) { + mDetails = details; + } + + boolean cannedTextResponsesChanged = false; + if (mCannedTextResponses == null && inCallCall.getCannedSmsResponses() != null + && !inCallCall.getCannedSmsResponses().isEmpty()) { + mCannedTextResponses = Collections.unmodifiableList(inCallCall.getCannedSmsResponses()); + } + + boolean callVideoProviderChanged = false; + try { + callVideoProviderChanged = + !Objects.equals(mCallVideoProvider, inCallCall.getCallVideoProvider()); + if (callVideoProviderChanged) { + mCallVideoProvider = inCallCall.getCallVideoProvider(); + } + } catch (RemoteException e) { + } + + int state = stateFromInCallCallState(inCallCall.getState()); + boolean stateChanged = mState != state; + if (stateChanged) { + mState = state; + } + + if (inCallCall.getParentCallId() != null) { + mParent = mPhone.internalGetCallByTelecommId(inCallCall.getParentCallId()); + } + + mChildren.clear(); + if (inCallCall.getChildCallIds() != null) { + for (int i = 0; i < inCallCall.getChildCallIds().size(); i++) { + mChildren.add(mPhone.internalGetCallByTelecommId( + inCallCall.getChildCallIds().get(i))); + } + } + + // Now we fire updates, ensuring that any client who listens to any of these notifications + // gets the most up-to-date state. + + if (stateChanged) { + fireStateChanged(mState); + } + if (detailsChanged) { + fireDetailsChanged(mDetails); + } + if (cannedTextResponsesChanged) { + fireCannedTextResponsesLoaded(mCannedTextResponses); + } + if (callVideoProviderChanged) { + fireCallVideoProviderChanged(mCallVideoProvider); + } + + // If we have transitioned to DISCONNECTED, that means we need to notify clients and + // remove ourselves from the Phone. Note that we do this after completing all state updates + // so a client can cleanly transition all their UI to the state appropriate for a + // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list. + if (mState == STATE_DISCONNECTED) { + fireCallDestroyed(); + mPhone.internalRemoveCall(this); + } + } + + /** {@hide} */ + final void internalSetPostDial(String remaining) { + mRemainingPostDialSequence = remaining; + firePostDial(mRemainingPostDialSequence); + } + + /** {@hide} */ + final void internalSetPostDialWait(String remaining) { + mRemainingPostDialSequence = remaining; + firePostDialWait(mRemainingPostDialSequence); + } + + private void fireStateChanged(int newState) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onStateChanged(this, newState); + } + } + + private void fireParentChanged(Call newParent) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onParentChanged(this, newParent); + } + } + + private void fireChildrenChanged(List children) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onChildrenChanged(this, children); + } + } + + private void fireDetailsChanged(Details details) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onDetailsChanged(this, details); + } + } + + private void fireCannedTextResponsesLoaded(List cannedTextResponses) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCannedTextResponsesLoaded(this, cannedTextResponses); + } + } + + private void fireCallVideoProviderChanged(RemoteCallVideoProvider callVideoProvider) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCallVideoProviderChanged(this, callVideoProvider); + } + } + + private void firePostDial(String remainingPostDialSequence) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onPostDial(this, remainingPostDialSequence); + } + } + + private void firePostDialWait(String remainingPostDialSequence) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onPostDialWait(this, remainingPostDialSequence); + } + } + + private void fireCallDestroyed() { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCallDestroyed(this); + } + } + + private int stateFromInCallCallState(CallState inCallCallState) { + switch (inCallCallState) { + case NEW: + return STATE_NEW; + case DIALING: + return STATE_DIALING; + case RINGING: + return STATE_RINGING; + case ACTIVE: + return STATE_ACTIVE; + case ON_HOLD: + return STATE_HOLDING; + case DISCONNECTED: + return STATE_DISCONNECTED; + case ABORTED: + return STATE_DISCONNECTED; + default: + Log.wtf(this, "Unrecognized CallState %s", inCallCallState); + return STATE_NEW; + } + } +} diff --git a/telecomm/java/android/telecomm/CallState.java b/telecomm/java/android/telecomm/CallState.java index 152c2023e7fd0..a464da5631056 100644 --- a/telecomm/java/android/telecomm/CallState.java +++ b/telecomm/java/android/telecomm/CallState.java @@ -47,22 +47,6 @@ public enum CallState { */ RINGING, - /** - * Indicates that the call is active but in a "post-dial" state where Telecomm is now sending - * some dual-tone multi-frequency signaling (DTMF) tones appended to the dialed number. Normal - * transitions are to {@link #POST_DIAL_WAIT} when the post-dial string requires user - * confirmation to proceed, {@link #ACTIVE} when the post-dial tones are completed, or - * {@link #DISCONNECTED}. - */ - POST_DIAL, - - /** - * Indicates that the call was in the {@link #POST_DIAL} state but is now waiting for user - * confirmation before the remaining digits can be sent. Normal transitions are to - * {@link #POST_DIAL} when the user asks Telecomm to proceed with the post-dial sequence. - */ - POST_DIAL_WAIT, - /** * Indicates that a call is currently connected to another party and a communication channel is * open between them. The normal transition to this state is by the user answering a diff --git a/telecomm/java/android/telecomm/CallVideoClient.java b/telecomm/java/android/telecomm/CallVideoClient.java index 76b28fad2d078..fb970dc14a35f 100644 --- a/telecomm/java/android/telecomm/CallVideoClient.java +++ b/telecomm/java/android/telecomm/CallVideoClient.java @@ -241,6 +241,7 @@ public abstract class CallVideoClient { * * @param callCameraCapabilities The changed camera capabilities. */ - public abstract void onHandleCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities); + public abstract void onHandleCameraCapabilitiesChange( + CallCameraCapabilities callCameraCapabilities); } diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java index d8293a5cdbe45..66cf1df98b1c6 100644 --- a/telecomm/java/android/telecomm/InCallAdapter.java +++ b/telecomm/java/android/telecomm/InCallAdapter.java @@ -24,7 +24,7 @@ import com.android.internal.telecomm.IInCallAdapter; * Receives commands from {@link InCallService} implementations which should be executed by * Telecomm. When Telecomm binds to a {@link InCallService}, an instance of this class is given to * the in-call service through which it can manipulate live (active, dialing, ringing) calls. When - * the in-call service is notified of new calls ({@link InCallService#addCall}), it can use the + * the in-call service is notified of new calls, it can use the * given call IDs to execute commands such as {@link #answerCall} for incoming calls or * {@link #disconnectCall} for active calls the user would like to end. Some commands are only * appropriate for calls in certain states; please consult each method for such limitations. @@ -167,16 +167,15 @@ public final class InCallAdapter { * A post-dial DTMF string is a string of digits entered after a phone number, when dialed, * that are immediately sent as DTMF tones to the recipient as soon as the connection is made. * While these tones are playing, Telecomm will notify the {@link InCallService} that the call - * is in the {@link InCallService#setPostDial(String,String)} state. + * is in the post dial state. * * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_PAUSE} symbol, Telecomm * will temporarily pause playing the tones for a pre-defined period of time. * * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_WAIT} symbol, Telecomm * will pause playing the tones and notify the {@link InCallService} that the call is in the - * {@link InCallService#setPostDialWait(String,String)} state. When the user decides to continue - * the postdial sequence, the {@link InCallService} should invoke the - * {@link #postDialContinue(String,boolean)} method. + * post dial wait state. When the user decides to continue the postdial sequence, the + * {@link InCallService} should invoke the {@link #postDialContinue(String,boolean)} method. * * @param callId The unique ID of the call for which postdial string playing should continue. * @param proceed Whether or not to continue with the post-dial sequence. diff --git a/telecomm/java/android/telecomm/InCallService.java b/telecomm/java/android/telecomm/InCallService.java index 31291fba7ec73..028b6e49e34e4 100644 --- a/telecomm/java/android/telecomm/InCallService.java +++ b/telecomm/java/android/telecomm/InCallService.java @@ -16,8 +16,6 @@ package android.telecomm; -import android.app.Service; -import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -31,11 +29,10 @@ import com.android.internal.telecomm.IInCallService; * This service is implemented by any app that wishes to provide the user-interface for managing * phone calls. Telecomm binds to this service while there exists a live (active or incoming) * call, and uses it to notify the in-call app of any live and and recently disconnected calls. - * TODO(santoscordon): Needs more/better description of lifecycle once the interface is better - * defined. + * * TODO(santoscordon): What happens if two or more apps on a given device implement this interface? */ -public abstract class InCallService extends Service { +public abstract class InCallService { private static final int MSG_SET_IN_CALL_ADAPTER = 1; private static final int MSG_ADD_CALL = 2; private static final int MSG_UPDATE_CALL = 3; @@ -50,21 +47,21 @@ public abstract class InCallService extends Service { public void handleMessage(Message msg) { switch (msg.what) { case MSG_SET_IN_CALL_ADAPTER: - mAdapter = new InCallAdapter((IInCallAdapter) msg.obj); - onAdapterAttached(mAdapter); + mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj)); + onPhoneCreated(mPhone); break; case MSG_ADD_CALL: - addCall((InCallCall) msg.obj); + mPhone.internalAddCall((InCallCall) msg.obj); break; case MSG_UPDATE_CALL: - updateCall((InCallCall) msg.obj); + mPhone.internalUpdateCall((InCallCall) msg.obj); break; - case MSG_SET_POST_DIAL: { + case MSG_SET_POST_DIAL: { SomeArgs args = (SomeArgs) msg.obj; try { String callId = (String) args.arg1; String remaining = (String) args.arg2; - setPostDial(callId, remaining); + mPhone.internalSetPostDial(callId, remaining); } finally { args.recycle(); } @@ -75,17 +72,17 @@ public abstract class InCallService extends Service { try { String callId = (String) args.arg1; String remaining = (String) args.arg2; - setPostDialWait(callId, remaining); + mPhone.internalSetPostDialWait(callId, remaining); } finally { args.recycle(); } break; } case MSG_ON_AUDIO_STATE_CHANGED: - onAudioStateChanged((CallAudioState) msg.obj); + mPhone.internalAudioStateChanged((CallAudioState) msg.obj); break; case MSG_BRING_TO_FOREGROUND: - bringToForeground(msg.arg1 == 1); + mPhone.internalBringToForeground(msg.arg1 == 1); break; default: break; @@ -142,85 +139,41 @@ public abstract class InCallService extends Service { } } - private final InCallServiceBinder mBinder; + private Phone mPhone; - private InCallAdapter mAdapter; + protected InCallService() {} - protected InCallService() { - mBinder = new InCallServiceBinder(); - } - - @Override - public final IBinder onBind(Intent intent) { - return mBinder; + public final IBinder getBinder() { + return new InCallServiceBinder(); } /** - * @return The attached {@link InCallAdapter} if attached, or null otherwise. + * Obtain the {@code Phone} associated with this {@code InCallService}. + * + * @return The {@code Phone} object associated with this {@code InCallService}, or {@code null} + * if the {@code InCallService} is not in a state where it has an associated {@code Phone}. */ - protected final InCallAdapter getAdapter() { - return mAdapter; + public Phone getPhone() { + return mPhone; } /** - * Lifecycle callback which is called when this {@link InCallService} has been attached - * to a {@link InCallAdapter}, indicating {@link #getAdapter()} is now safe to use. + * Invoked when the {@code Phone} has been created. This is a signal to the in-call experience + * to start displaying in-call information to the user. Each instance of {@code InCallService} + * will have only one {@code Phone}, and this method will be called exactly once in the + * lifetime of the {@code InCallService}. * - * @param adapter The adapter now attached to this in-call service. + * @param phone The {@code Phone} object associated with this {@code InCallService}. */ - protected void onAdapterAttached(InCallAdapter adapter) { - } + public void onPhoneCreated(Phone phone) { } /** - * Indicates to the in-call app that a new call has been created and an appropriate - * user-interface should be built and shown to notify the user. + * Invoked when a {@code Phone} has been destroyed. This is a signal to the in-call experience + * to stop displaying in-call information to the user. This method will be called exactly once + * in the lifetime of the {@code InCallService}, and it will always be called after a previous + * call to {@link #onPhoneCreated(Phone)}. * - * @param call Information about the new call. + * @param phone The {@code Phone} object associated with this {@code InCallService}. */ - protected abstract void addCall(InCallCall call); - - /** - * Call when information about a call has changed. - * - * @param call Information about the new call. - */ - protected abstract void updateCall(InCallCall call); - - /** - * Indicates to the in-call app that the specified call is active but in a "post-dial" state - * where Telecomm is now sending some dual-tone multi-frequency signaling (DTMF) tones appended - * to the dialed number. Normal transitions are to {@link #setPostDialWait(String,String)} when - * the post-dial string requires user confirmation to proceed, and {@link CallState#ACTIVE} when - * the post-dial tones are completed. - * - * @param callId The identifier of the call changing state. - * @param remaining The remaining postdial string to be dialed. - */ - protected abstract void setPostDial(String callId, String remaining); - - /** - * Indicates to the in-call app that the specified call was in the - * {@link #setPostDial(String,String)} state but is now waiting for user confirmation before the - * remaining digits can be sent. Normal transitions are to {@link #setPostDial(String,String)} - * when the user asks Telecomm to proceed with the post-dial sequence and the in-call app - * informs Telecomm of this by invoking {@link InCallAdapter#postDialContinue(String,boolean)}. - * - * @param callId The identifier of the call changing state. - * @param remaining The remaining postdial string to be dialed. - */ - protected abstract void setPostDialWait(String callId, String remaining); - - /** - * Called when the audio state changes. - * - * @param audioState The new {@link CallAudioState}. - */ - protected abstract void onAudioStateChanged(CallAudioState audioState); - - /** - * Brings the in-call screen to the foreground. - * - * @param showDialpad If true, put up the dialpad when the screen is shown. - */ - protected abstract void bringToForeground(boolean showDialpad); + public void onPhoneDestroyed(Phone phone) { } } diff --git a/telecomm/java/android/telecomm/Phone.java b/telecomm/java/android/telecomm/Phone.java new file mode 100644 index 0000000000000..9c97b45891b45 --- /dev/null +++ b/telecomm/java/android/telecomm/Phone.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +import android.util.ArrayMap; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * A unified virtual device providing a means of voice (and other) communication on a device. + */ +public final class Phone { + + public abstract static class Listener { + /** + * Called when the audio state changes. + * + * @param phone The {@code Phone} calling this method. + * @param audioState The new {@link CallAudioState}. + */ + public void onAudioStateChanged(Phone phone, CallAudioState audioState) { } + + /** + * Called to bring the in-call screen to the foreground. The in-call experience should + * respond immediately by coming to the foreground to inform the user of the state of + * ongoing {@code Call}s. + * + * @param phone The {@code Phone} calling this method. + * @param showDialpad If true, put up the dialpad when the screen is shown. + */ + public void onBringToForeground(Phone phone, boolean showDialpad) { } + + /** + * Called when a {@code Call} has been added to this in-call session. The in-call user + * experience should add necessary state listeners to the specified {@code Call} and + * immediately start to show the user information about the existence + * and nature of this {@code Call}. Subsequent invocations of {@link #getCalls()} will + * include this {@code Call}. + * + * @param phone The {@code Phone} calling this method. + * @param call A newly added {@code Call}. + */ + public void onCallAdded(Phone phone, Call call) { } + + /** + * Called when a {@code Call} has been removed from this in-call session. The in-call user + * experience should remove any state listeners from the specified {@code Call} and + * immediately stop displaying any information about this {@code Call}. + * Subsequent invocations of {@link #getCalls()} will no longer include this {@code Call}. + * + * @param phone The {@code Phone} calling this method. + * @param call A newly removed {@code Call}. + */ + public void onCallRemoved(Phone phone, Call call) { } + } + + // A Map allows us to track each Call by its Telecomm-specified call ID + private final Map mCallByTelecommCallId = new ArrayMap<>(); + + // A List allows us to keep the Calls in a stable iteration order so that casually developed + // user interface components do not incur any spurious jank + private final List mCalls = new ArrayList<>(); + + // An unmodifiable view of the above List can be safely shared with subclass implementations + private final List mUnmodifiableCalls = Collections.unmodifiableList(mCalls); + + private final InCallAdapter mInCallAdapter; + + private CallAudioState mAudioState; + + private final List mListeners = new ArrayList<>(); + + /** {@hide} */ + Phone(InCallAdapter adapter) { + mInCallAdapter = adapter; + } + + /** {@hide} */ + final void internalAddCall(InCallCall inCallCall) { + Call call = new Call(this, inCallCall.getId(), mInCallAdapter); + mCallByTelecommCallId.put(inCallCall.getId(), call); + mCalls.add(call); + checkCallTree(inCallCall); + call.internalUpdate(inCallCall); + fireCallAdded(call); + } + + /** {@hide} */ + final void internalRemoveCall(Call call) { + mCallByTelecommCallId.remove(call.internalGetCallId()); + mCalls.remove(call); + fireCallRemoved(call); + } + + /** {@hide} */ + final void internalUpdateCall(InCallCall inCallCall) { + Call call = mCallByTelecommCallId.get(inCallCall.getId()); + if (call != null) { + checkCallTree(inCallCall); + call.internalUpdate(inCallCall); + } + } + + /** {@hide} */ + final void internalSetPostDial(String callId, String remaining) { + Call call = mCallByTelecommCallId.get(callId); + if (call != null) { + call.internalSetPostDial(remaining); + } + } + + /** {@hide} */ + final void internalSetPostDialWait(String callId, String remaining) { + Call call = mCallByTelecommCallId.get(callId); + if (call != null) { + call.internalSetPostDialWait(remaining); + } + } + + /** {@hide} */ + final void internalAudioStateChanged(CallAudioState callAudioState) { + if (!Objects.equals(mAudioState, callAudioState)) { + mAudioState = callAudioState; + fireAudioStateChanged(callAudioState); + } + } + + /** {@hide} */ + final Call internalGetCallByTelecommId(String telecommId) { + return mCallByTelecommCallId.get(telecommId); + } + + /** {@hide} */ + final void internalBringToForeground(boolean showDialpad) { + fireBringToForeground(showDialpad); + } + + /** + * Adds a listener to this {@code Phone}. + * + * @param listener A {@code Listener} object. + */ + public final void addListener(Listener listener) { + mListeners.add(listener); + } + + /** + * Removes a listener from this {@code Phone}. + * + * @param listener A {@code Listener} object. + */ + public final void removeListener(Listener listener) { + mListeners.remove(listener); + } + + /** + * Obtains the current list of {@code Call}s to be displayed by this in-call experience. + * + * @return A list of the relevant {@code Call}s. + */ + public final List getCalls() { + return mUnmodifiableCalls; + } + + /** + * Sets the microphone mute state. When this request is honored, there will be change to + * the {@link #getAudioState()}. + * + * @param state {@code true} if the microphone should be muted; {@code false} otherwise. + */ + public final void setMuted(boolean state) { + mInCallAdapter.mute(state); + } + + /** + * Sets the audio route (speaker, bluetooth, etc...). When this request is honored, there will + * be change to the {@link #getAudioState()}. + * + * @param route The audio route to use. + */ + public final void setAudioRoute(int route) { + mInCallAdapter.setAudioRoute(route); + } + + /** + * Obtains the current phone call audio state of the {@code Phone}. + * + * @return An object encapsulating the audio state. + */ + public final CallAudioState getAudioState() { + return mAudioState; + } + + private void fireCallAdded(Call call) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCallAdded(this, call); + } + } + + private void fireCallRemoved(Call call) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCallRemoved(this, call); + } + } + + private void fireAudioStateChanged(CallAudioState audioState) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onAudioStateChanged(this, audioState); + } + } + + private void fireBringToForeground(boolean showDialpad) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onBringToForeground(this, showDialpad); + } + } + + private void checkCallTree(InCallCall inCallCall) { + if (inCallCall.getParentCallId() != null && + !mCallByTelecommCallId.containsKey(inCallCall.getParentCallId())) { + Log.wtf(this, "InCallCall %s has nonexistent parent %s", + inCallCall.getId(), inCallCall.getParentCallId()); + } + if (inCallCall.getChildCallIds() != null) { + for (int i = 0; i < inCallCall.getChildCallIds().size(); i++) { + if (!mCallByTelecommCallId.containsKey(inCallCall.getChildCallIds().get(i))) { + Log.wtf(this, "InCallCall %s has nonexistent child %s", + inCallCall.getId(), inCallCall.getChildCallIds().get(i)); + } + } + } + } +} diff --git a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java index 856d321dc4084..a49076a2fd0c8 100644 --- a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java +++ b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java @@ -20,63 +20,92 @@ import android.os.IBinder; import android.os.RemoteException; import android.view.Surface; -import com.android.internal.telecomm.ICallVideoClient; import com.android.internal.telecomm.ICallVideoProvider; -public class RemoteCallVideoProvider implements IBinder.DeathRecipient { +public class RemoteCallVideoProvider { private final ICallVideoProvider mCallVideoProvider; + private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + mCallVideoProvider.asBinder().unlinkToDeath(this, 0); + } + }; + + /** {@hide} */ RemoteCallVideoProvider(ICallVideoProvider callVideoProvider) throws RemoteException { mCallVideoProvider = callVideoProvider; - mCallVideoProvider.asBinder().linkToDeath(this, 0); + mCallVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0); } - @Override - public void binderDied() { - mCallVideoProvider.asBinder().unlinkToDeath(this, 0); - } - - public void setCallVideoClient(CallVideoClient callVideoClient) throws RemoteException { - mCallVideoProvider.setCallVideoClient(callVideoClient.getBinder()); + public void setCallVideoClient(CallVideoClient callVideoClient) { + try { + mCallVideoProvider.setCallVideoClient(callVideoClient.getBinder()); + } catch (RemoteException e) { + } } public void setCamera(String cameraId) throws RemoteException { mCallVideoProvider.setCamera(cameraId); } - public void setPreviewSurface(Surface surface) throws RemoteException { - mCallVideoProvider.setPreviewSurface(surface); + public void setPreviewSurface(Surface surface) { + try { + mCallVideoProvider.setPreviewSurface(surface); + } catch (RemoteException e) { + } } - public void setDisplaySurface(Surface surface) throws RemoteException { - mCallVideoProvider.setDisplaySurface(surface); + public void setDisplaySurface(Surface surface) { + try { + mCallVideoProvider.setDisplaySurface(surface); + } catch (RemoteException e) { + } } - public void setDeviceOrientation(int rotation) throws RemoteException { - mCallVideoProvider.setDeviceOrientation(rotation); + public void setDeviceOrientation(int rotation) { + try { + mCallVideoProvider.setDeviceOrientation(rotation); + } catch (RemoteException e) { + } } public void setZoom(float value) throws RemoteException { mCallVideoProvider.setZoom(value); } - public void sendSessionModifyRequest(VideoCallProfile requestProfile) throws RemoteException { - mCallVideoProvider.sendSessionModifyRequest(requestProfile); + public void sendSessionModifyRequest(VideoCallProfile requestProfile) { + try { + mCallVideoProvider.sendSessionModifyRequest(requestProfile); + } catch (RemoteException e) { + } } - public void sendSessionModifyResponse(VideoCallProfile responseProfile) throws RemoteException { - mCallVideoProvider.sendSessionModifyResponse(responseProfile); + public void sendSessionModifyResponse(VideoCallProfile responseProfile) { + try { + mCallVideoProvider.sendSessionModifyResponse(responseProfile); + } catch (RemoteException e) { + } } - public void requestCameraCapabilities() throws RemoteException { - mCallVideoProvider.requestCameraCapabilities(); + public void requestCameraCapabilities() { + try { + mCallVideoProvider.requestCameraCapabilities(); + } catch (RemoteException e) { + } } - public void requestCallDataUsage() throws RemoteException { - mCallVideoProvider.requestCallDataUsage(); + public void requestCallDataUsage() { + try { + mCallVideoProvider.requestCallDataUsage(); + } catch (RemoteException e) { + } } - public void setPauseImage(String uri) throws RemoteException { - mCallVideoProvider.setPauseImage(uri); + public void setPauseImage(String uri) { + try { + mCallVideoProvider.setPauseImage(uri); + } catch (RemoteException e) { + } } } \ No newline at end of file From 5e00dfdad55d1031dca3b4396a26783e5fd0971f Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Mon, 14 Jul 2014 22:42:23 +0000 Subject: [PATCH 25/75] Revert "Block focus with a touchscreen for ActionBars and Toolbars" This reverts commit a2933761d17bc5106357a1edd9bd3ff978160133. Change-Id: Idedd430574925a0e86311a3d9fee51a74731c2f8 --- core/res/res/layout/screen_action_bar.xml | 2 -- core/res/res/layout/screen_toolbar.xml | 1 - core/res/res/values/styles.xml | 1 - 3 files changed, 4 deletions(-) diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml index b3a34789883b8..5acb588eb35e6 100644 --- a/core/res/res/layout/screen_action_bar.xml +++ b/core/res/res/layout/screen_action_bar.xml @@ -35,7 +35,6 @@ This is an optimized layout for a screen with the Action Bar enabled. android:layout_alignParentTop="true" style="?attr/actionBarStyle" android:transitionName="android:action_bar" - android:touchscreenBlocksFocus="true" android:gravity="top"> diff --git a/core/res/res/layout/screen_toolbar.xml b/core/res/res/layout/screen_toolbar.xml index 039e89f5cbbf3..56815f86e0608 100644 --- a/core/res/res/layout/screen_toolbar.xml +++ b/core/res/res/layout/screen_toolbar.xml @@ -35,7 +35,6 @@ This is an optimized layout for a screen with a toolbar enabled. android:layout_alignParentTop="true" style="?attr/actionBarStyle" android:transitionName="android:action_bar" - android:touchscreenBlocksFocus="true" android:gravity="top"> @style/Widget.Toolbar.Button.Navigation ?attr/homeAsUpIndicator 16dp - true