diff options
149 files changed, 2589 insertions, 406 deletions
diff --git a/api/11.xml b/api/11.xml index 6ace7ec97913..34a7a5c41ec2 100644 --- a/api/11.xml +++ b/api/11.xml @@ -171131,50 +171131,6 @@ <exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException"> </exception> </method> -<field name="MAX_CONSTANT" - type="int" - transient="false" - volatile="false" - value="8" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="MAX_INPUT" - type="int" - transient="false" - volatile="false" - value="8" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="MAX_OUTPUT" - type="int" - transient="false" - volatile="false" - value="8" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="MAX_TEXTURE" - type="int" - transient="false" - volatile="false" - value="8" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> </class> <class name="Program.BaseProgramBuilder" extends="java.lang.Object" diff --git a/api/current.xml b/api/current.xml index 22f5c7da6a23..86c47f589e53 100644 --- a/api/current.xml +++ b/api/current.xml @@ -38983,6 +38983,17 @@ visibility="public" > </method> +<method name="clearViews" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +</method> <method name="createView" return="android.appwidget.AppWidgetHostView" abstract="false" @@ -171159,50 +171170,6 @@ <exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException"> </exception> </method> -<field name="MAX_CONSTANT" - type="int" - transient="false" - volatile="false" - value="8" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="MAX_INPUT" - type="int" - transient="false" - volatile="false" - value="8" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="MAX_OUTPUT" - type="int" - transient="false" - volatile="false" - value="8" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="MAX_TEXTURE" - type="int" - transient="false" - volatile="false" - value="8" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> </class> <class name="Program.BaseProgramBuilder" extends="java.lang.Object" @@ -260264,7 +260231,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="arg0" type="T"> +<parameter name="t" type="T"> </parameter> </method> </interface> diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java index 7f11871b0f74..3212cba76bbb 100644 --- a/core/java/android/animation/ObjectAnimator.java +++ b/core/java/android/animation/ObjectAnimator.java @@ -18,6 +18,7 @@ package android.animation; import android.util.Log; +import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.util.ArrayList; @@ -28,6 +29,8 @@ import java.util.ArrayList; * are then determined internally and the animation will call these functions as necessary to * animate the property. * + * <p class="note"><b>Note:</b> Instances of this class hold only a {@link WeakReference} + * to the target object.</p> * @see #setPropertyName(String) * */ @@ -35,7 +38,7 @@ public final class ObjectAnimator extends ValueAnimator { private static final boolean DBG = false; // The target object on which the property exists, set in the constructor - private Object mTarget; + private WeakReference<Object> mTargetRef; private String mPropertyName; @@ -102,6 +105,9 @@ public final class ObjectAnimator extends ValueAnimator { * @return Method the method associated with mPropertyName. */ private Method getPropertyFunction(String prefix, Class valueType) { + final Object target = mTargetRef == null ? null : mTargetRef.get(); + if (target == null) return null; + // TODO: faster implementation... Method returnVal = null; String firstLetter = mPropertyName.substring(0, 1); @@ -114,7 +120,7 @@ public final class ObjectAnimator extends ValueAnimator { args[0] = valueType; } try { - returnVal = mTarget.getClass().getMethod(setterName, args); + returnVal = target.getClass().getMethod(setterName, args); } catch (NoSuchMethodException e) { Log.e("ObjectAnimator", "Couldn't find setter/getter for property " + mPropertyName + ": " + e); @@ -134,13 +140,14 @@ public final class ObjectAnimator extends ValueAnimator { * A constructor that takes a single property name and set of values. This constructor is * used in the simple case of animating a single property. * - * @param target The object whose property is to be animated. This object should - * have a public method on it called <code>setName()</code>, where <code>name</code> is - * the value of the <code>propertyName</code> parameter. + * @param target The object whose property is to be animated. It will be weakly referenced + * from the newly-created ObjectAnimator. This object should have a public method on it called + * <code>setName()</code>, where <code>name</code> is the value of the <code>propertyName</code> + * parameter. * @param propertyName The name of the property being animated. */ private ObjectAnimator(Object target, String propertyName) { - mTarget = target; + mTargetRef = new WeakReference<Object>(target); setPropertyName(propertyName); } @@ -152,9 +159,10 @@ public final class ObjectAnimator extends ValueAnimator { * from the target object and property being animated). Therefore, there should typically * be two or more values. * - * @param target The object whose property is to be animated. This object should - * have a public method on it called <code>setName()</code>, where <code>name</code> is - * the value of the <code>propertyName</code> parameter. + * @param target The object whose property is to be animated. It will be weakly referenced + * from the newly-created ObjectAnimator. This object should have a public method on it called + * <code>setName()</code>, where <code>name</code> is the value of the <code>propertyName</code> + * parameter. * @param propertyName The name of the property being animated. * @param values A set of values that the animation will animate between over time. * @return A ValueAnimator object that is set up to animate between the given values. @@ -173,9 +181,10 @@ public final class ObjectAnimator extends ValueAnimator { * from the target object and property being animated). Therefore, there should typically * be two or more values. * - * @param target The object whose property is to be animated. This object should - * have a public method on it called <code>setName()</code>, where <code>name</code> is - * the value of the <code>propertyName</code> parameter. + * @param target The object whose property is to be animated. It will be weakly referenced + * from the newly-created ObjectAnimator. This object should have a public method on it called + * <code>setName()</code>, where <code>name</code> is the value of the <code>propertyName</code> + * parameter. * @param propertyName The name of the property being animated. * @param values A set of values that the animation will animate between over time. * @return A ValueAnimator object that is set up to animate between the given values. @@ -192,10 +201,10 @@ public final class ObjectAnimator extends ValueAnimator { * PropertyValuesHolder allows you to associate a set of animation values with a property * name. * - * @param target The object whose property is to be animated. This object should - * have public methods on it called <code>setName()</code>, where <code>name</code> is - * the name of the property passed in as the <code>propertyName</code> parameter for - * each of the PropertyValuesHolder objects. + * @param target The object whose property is to be animated. It will be weakly referenced + * from the newly-created ObjectAnimator. This object should have public methods on it called + * <code>setName()</code>, where <code>name</code> is the name of the property passed in as the + * <code>propertyName</code> parameter for each of the PropertyValuesHolder objects. * @param propertyName The name of the property being animated. * @param evaluator A TypeEvaluator that will be called on each animation frame to * provide the ncessry interpolation between the Object values to derive the animated @@ -218,10 +227,10 @@ public final class ObjectAnimator extends ValueAnimator { * PropertyValuesHolder allows you to associate a set of animation values with a property * name. * - * @param target The object whose property is to be animated. This object should - * have public methods on it called <code>setName()</code>, where <code>name</code> is - * the name of the property passed in as the <code>propertyName</code> parameter for - * each of the PropertyValuesHolder objects. + * @param target The object whose property is to be animated. It will be weakly referenced + * from the newly-created ObjectAnimator. This object should have public methods on it called + * <code>setName()</code>, where <code>name</code> is the name of the property passed in as the + * <code>propertyName</code> parameter for each of the PropertyValuesHolder objects. * @param values A set of PropertyValuesHolder objects whose values will be animated * between over time. * @return A ValueAnimator object that is set up to animate between the given values. @@ -229,7 +238,7 @@ public final class ObjectAnimator extends ValueAnimator { public static ObjectAnimator ofPropertyValuesHolder(Object target, PropertyValuesHolder... values) { ObjectAnimator anim = new ObjectAnimator(); - anim.mTarget = target; + anim.mTargetRef = new WeakReference<Object>(target); anim.setValues(values); return anim; } @@ -270,7 +279,8 @@ public final class ObjectAnimator extends ValueAnimator { @Override public void start() { if (DBG) { - Log.d("ObjectAnimator", "Anim target, duration" + mTarget + ", " + getDuration()); + final Object target = mTargetRef == null ? null : mTargetRef.get(); + Log.d("ObjectAnimator", "Anim target, duration" + target + ", " + getDuration()); for (int i = 0; i < mValues.length; ++i) { PropertyValuesHolder pvh = mValues[i]; ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes; @@ -297,11 +307,14 @@ public final class ObjectAnimator extends ValueAnimator { @Override void initAnimation() { if (!mInitialized) { + final Object target = mTargetRef == null ? null : mTargetRef.get(); + if (target == null) return; + // mValueType may change due to setter/getter setup; do this before calling super.init(), // which uses mValueType to set up the default type evaluator. int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { - mValues[i].setupSetterAndGetter(mTarget); + mValues[i].setupSetterAndGetter(target); } super.initAnimation(); } @@ -326,22 +339,26 @@ public final class ObjectAnimator extends ValueAnimator { /** * The target object whose property will be animated by this animation * - * @return The object being animated + * @return The object being animated, or null if the object has been garbage collected. */ public Object getTarget() { - return mTarget; + return mTargetRef == null ? null : mTargetRef.get(); } /** - * Sets the target object whose property will be animated by this animation + * Sets the target object whose property will be animated by this animation. The target + * will be weakly referenced from this object. * * @param target The object being animated */ @Override public void setTarget(Object target) { - if (mTarget != target) { - mTarget = target; - if (mTarget != null && target != null && mTarget.getClass() == target.getClass()) { + final Object currentTarget = mTargetRef == null ? null : mTargetRef.get(); + + if (currentTarget != target) { + mTargetRef = new WeakReference<Object>(target); + if (currentTarget != null && target != null + && currentTarget.getClass() == target.getClass()) { return; } // New target type should cause re-initialization prior to starting @@ -351,19 +368,25 @@ public final class ObjectAnimator extends ValueAnimator { @Override public void setupStartValues() { + final Object target = mTargetRef == null ? null : mTargetRef.get(); + if (target == null) return; + initAnimation(); int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { - mValues[i].setupStartValue(mTarget); + mValues[i].setupStartValue(target); } } @Override public void setupEndValues() { + final Object target = mTargetRef == null ? null : mTargetRef.get(); + if (target == null) return; + initAnimation(); int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { - mValues[i].setupEndValue(mTarget); + mValues[i].setupEndValue(target); } } @@ -382,9 +405,13 @@ public final class ObjectAnimator extends ValueAnimator { @Override void animateValue(float fraction) { super.animateValue(fraction); + + final Object target = mTargetRef == null ? null : mTargetRef.get(); + if (target == null) return; + int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { - mValues[i].setAnimatedValue(mTarget); + mValues[i].setAnimatedValue(target); } } diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java index 7c2d3a091d40..57a26959bc8c 100644 --- a/core/java/android/app/IntentService.java +++ b/core/java/android/app/IntentService.java @@ -113,6 +113,12 @@ public abstract class IntentService extends Service { mServiceHandler.sendMessage(msg); } + /** + * You should not override this method for your IntentService. Instead, + * override {@link #onHandleIntent}, which the system calls when the IntentService + * receives a start request. + * @see android.app.Service#onStartCommand + */ @Override public int onStartCommand(Intent intent, int flags, int startId) { onStart(intent, startId); @@ -124,6 +130,11 @@ public abstract class IntentService extends Service { mServiceLooper.quit(); } + /** + * Unless you provide binding for your service, you don't need to implement this + * method, because the default implementation returns null. + * @see android.app.Service#onBind + */ @Override public IBinder onBind(Intent intent) { return null; @@ -135,6 +146,8 @@ public abstract class IntentService extends Service { * worker thread that runs independently from other application logic. * So, if this code takes a long time, it will hold up other requests to * the same IntentService, but it will not hold up anything else. + * When all requests have been handled, the IntentService stops itself, + * so you should not call {@link #stopSelf}. * * @param intent The value passed to {@link * android.content.Context#startService(Intent)}. diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java index 7730942e467a..983548403004 100644 --- a/core/java/android/appwidget/AppWidgetHost.java +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -214,6 +214,10 @@ public class AppWidgetHost { } } + /** + * Create the AppWidgetHostView for the given widget. + * The AppWidgetHost retains a pointer to the newly-created View. + */ public final AppWidgetHostView createView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget) { AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget); @@ -272,6 +276,13 @@ public class AppWidgetHost { v.viewDataChanged(viewId); } } + + /** + * Clear the list of Views that have been created by this AppWidgetHost. + */ + protected void clearViews() { + mViews.clear(); + } } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index e8bf29d3719e..b40a22648be1 100755 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1476,7 +1476,6 @@ public class Resources { } } } - cache.clear(); } /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index c4bd1dee7c9e..f05ef8c588cd 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7580,6 +7580,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mHardwareLayer.destroy(); mHardwareLayer = null; } + + if (mAttachInfo != null) { + mAttachInfo.mHandler.removeMessages(AttachInfo.INVALIDATE_MSG, this); + mAttachInfo.mHandler.removeMessages(AttachInfo.INVALIDATE_RECT_MSG, this); + } + + mCurrentAnimation = null; } /** diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java index 2a76e33f94fe..6095a64d634a 100755 --- a/core/java/android/view/WindowOrientationListener.java +++ b/core/java/android/view/WindowOrientationListener.java @@ -234,6 +234,14 @@ public abstract class WindowOrientationListener { // high time constant. private static final float MAX_DEVIATION_FROM_GRAVITY = 1.5f; + // Minimum acceleration considered, in m/s^2. Below this threshold sensor noise will have + // significant impact on the calculations and in case of the vector (0, 0, 0) there is no + // defined rotation or tilt at all. Low or zero readings can happen when space travelling + // or free falling, but more commonly when shaking or getting bad readings from the sensor. + // The accelerometer is turned off when not used and polling it too soon after it is + // turned on may result in (0, 0, 0). + private static final float MIN_ABS_ACCELERATION = 1.5f; + // Actual sampling period corresponding to SensorManager.SENSOR_DELAY_NORMAL. There's no // way to get this information from SensorManager. // Note the actual period is generally 3-30ms larger than this depending on the device, but @@ -347,6 +355,9 @@ public abstract class WindowOrientationListener { float deviation = Math.abs(magnitude - SensorManager.STANDARD_GRAVITY); handleAccelerationDistrust(deviation); + if (magnitude < MIN_ABS_ACCELERATION) { + return; // Ignore tilt and orientation when (0, 0, 0) or low reading + } // only filter tilt when we're accelerating float alpha = 1; diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java index ec8e93c88947..c27082f928f9 100644 --- a/core/java/android/widget/AdapterViewAnimator.java +++ b/core/java/android/widget/AdapterViewAnimator.java @@ -787,6 +787,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> // We do the former in case mAdapter is null, and hence setDisplayedChild won't // set mWhichChild mWhichChild = ss.whichChild; + setDisplayedChild(mWhichChild); } @@ -986,4 +987,10 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> public void willBeAdvancedByHost() { } + + @Override + protected void onDetachedFromWindow() { + mAdapter = null; + super.onDetachedFromWindow(); + } } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 5b143fe2dd91..ef4e4e0a18a9 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -1004,9 +1004,11 @@ public class ProgressBar extends View { @Override protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); if (mIndeterminate) { stopAnimation(); } + // This should come after stopAnimation(), otherwise an invalidate message remains in the + // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation + super.onDetachedFromWindow(); } } diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index a7bff62935a9..df1f4bf12dce 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -152,6 +152,7 @@ public class RemoteViewsAdapter extends BaseAdapter { if (callback != null) { callback.onRemoteAdapterDisconnected(); } + adapter.mCache.reset(); } public IRemoteViewsFactory getRemoteViewsFactory() { @@ -657,11 +658,9 @@ public class RemoteViewsAdapter extends BaseAdapter { try { remoteViews = factory.getViewAt(position); itemId = factory.getItemId(position); - } catch (Exception e) { - // Print the error - Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + - e.getMessage()); - e.printStackTrace(); + } catch (Throwable t) { + Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + t.getMessage()); + t.printStackTrace(); // Return early to prevent additional work in re-centering the view cache, and // swapping from the loading view diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java index 0476d28fffdf..264af71eb438 100644 --- a/core/java/android/widget/StackView.java +++ b/core/java/android/widget/StackView.java @@ -194,7 +194,21 @@ public class StackView extends AdapterViewAnimator { /** * Animate the views between different relative indexes within the {@link AdapterViewAnimator} */ - void animateViewForTransition(int fromIndex, int toIndex, View view) { + void animateViewForTransition(int fromIndex, int toIndex, final View view) { + ObjectAnimator alphaOa = null; + ObjectAnimator oldAlphaOa = null; + + // If there is currently an alpha animation on this view, we need + // to know about it, and may need to cancel it so as not to interfere with + // a new alpha animation. + Object tag = view.getTag(com.android.internal.R.id.viewAlphaAnimation); + if (tag instanceof WeakReference<?>) { + Object obj = ((WeakReference<?>) tag).get(); + if (obj instanceof ObjectAnimator) { + oldAlphaOa = (ObjectAnimator) obj; + } + } + if (fromIndex == -1 && toIndex == NUM_ACTIVE_VIEWS -1) { // Fade item in if (view.getAlpha() == 1) { @@ -206,9 +220,12 @@ public class StackView extends AdapterViewAnimator { view.setTranslationY(0); view.setVisibility(VISIBLE); - ObjectAnimator fadeIn = ObjectAnimator.ofFloat(view, "alpha", view.getAlpha(), 1.0f); - fadeIn.setDuration(FADE_IN_ANIMATION_DURATION); - fadeIn.start(); + alphaOa = ObjectAnimator.ofFloat(view, "alpha", view.getAlpha(), 1.0f); + alphaOa.setDuration(FADE_IN_ANIMATION_DURATION); + if (oldAlphaOa != null) oldAlphaOa.cancel(); + alphaOa.start(); + view.setTagInternal(com.android.internal.R.id.viewAlphaAnimation, + new WeakReference<ObjectAnimator>(alphaOa)); } else if (fromIndex == 0 && toIndex == 1) { // Slide item in view.setVisibility(VISIBLE); @@ -216,39 +233,45 @@ public class StackView extends AdapterViewAnimator { int duration = Math.round(mStackSlider.getDurationForNeutralPosition(mYVelocity)); StackSlider animationSlider = new StackSlider(mStackSlider); + animationSlider.setView(view); PropertyValuesHolder slideInY = PropertyValuesHolder.ofFloat("YProgress", 0.0f); PropertyValuesHolder slideInX = PropertyValuesHolder.ofFloat("XProgress", 0.0f); - ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(animationSlider, + ObjectAnimator slideIn = ObjectAnimator.ofPropertyValuesHolder(animationSlider, slideInX, slideInY); - pa.setDuration(duration); - pa.setInterpolator(new LinearInterpolator()); - pa.start(); + slideIn.setDuration(duration); + slideIn.setInterpolator(new LinearInterpolator()); + slideIn.start(); } else if (fromIndex == 1 && toIndex == 0) { // Slide item out int duration = Math.round(mStackSlider.getDurationForOffscreenPosition(mYVelocity)); StackSlider animationSlider = new StackSlider(mStackSlider); + animationSlider.setView(view); PropertyValuesHolder slideOutY = PropertyValuesHolder.ofFloat("YProgress", 1.0f); PropertyValuesHolder slideOutX = PropertyValuesHolder.ofFloat("XProgress", 0.0f); - ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(animationSlider, + ObjectAnimator slideOut = ObjectAnimator.ofPropertyValuesHolder(animationSlider, slideOutX, slideOutY); - pa.setDuration(duration); - pa.setInterpolator(new LinearInterpolator()); - pa.start(); - } else if (fromIndex == -1 && toIndex == 0) { + slideOut.setDuration(duration); + slideOut.setInterpolator(new LinearInterpolator()); + slideOut.start(); + } else if (toIndex == 0) { // Make sure this view that is "waiting in the wings" is invisible view.setAlpha(0.0f); view.setVisibility(INVISIBLE); - LayoutParams lp = (LayoutParams) view.getLayoutParams(); - lp.setVerticalOffset(-mSlideAmount); + } else if (fromIndex == 0 && toIndex > 1) { + view.setVisibility(VISIBLE); + view.setAlpha(1.0f); } else if (fromIndex == -1) { view.setAlpha(1.0f); view.setVisibility(VISIBLE); } else if (toIndex == -1) { // Fade item out - ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, "alpha", view.getAlpha(), 0.0f); - fadeOut.setDuration(STACK_RELAYOUT_DURATION); - fadeOut.start(); + alphaOa = ObjectAnimator.ofFloat(view, "alpha", view.getAlpha(), 0.0f); + alphaOa.setDuration(STACK_RELAYOUT_DURATION); + if (oldAlphaOa != null) oldAlphaOa.cancel(); + alphaOa.start(); + view.setTagInternal(com.android.internal.R.id.viewAlphaAnimation, + new WeakReference<ObjectAnimator>(alphaOa)); } // Implement the faked perspective @@ -279,6 +302,16 @@ public class StackView extends AdapterViewAnimator { (getMeasuredWidth() * (1 - PERSPECTIVE_SHIFT_FACTOR_X) / 2.0f); final float transX = perspectiveTranslationX + scaleShiftCorrectionX; + // If this view is currently being animated for a certain position, we need to cancel + // this animation so as not to interfere with the new transformation. + Object tag = view.getTag(com.android.internal.R.id.viewAnimation); + if (tag instanceof WeakReference<?>) { + Object obj = ((WeakReference<?>) tag).get(); + if (obj instanceof ObjectAnimator) { + ((ObjectAnimator) obj).cancel(); + } + } + if (animate) { PropertyValuesHolder translationX = PropertyValuesHolder.ofFloat("translationX", transX); PropertyValuesHolder translationY = PropertyValuesHolder.ofFloat("translationY", transY); @@ -292,14 +325,6 @@ public class StackView extends AdapterViewAnimator { new WeakReference<ObjectAnimator>(oa)); oa.start(); } else { - Object tag = view.getTag(com.android.internal.R.id.viewAnimation); - if (tag instanceof WeakReference<?>) { - Object obj = ((WeakReference<?>) tag).get(); - if (obj instanceof ObjectAnimator) { - ((ObjectAnimator) obj).cancel(); - } - } - view.setTranslationX(transX); view.setTranslationY(transY); view.setScaleX(scale); diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java index d5f69bab9fcd..3325df617de9 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java @@ -66,7 +66,7 @@ public class ActionMenuItemView extends LinearLayout mItemData = itemData; setIcon(itemData.getIcon()); - setTitle(itemData.getTitle()); // Title only takes effect if there is no icon + setTitle(itemData.getTitleForItemView(this)); // Title only takes effect if there is no icon setId(itemData.getItemId()); setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE); @@ -91,7 +91,7 @@ public class ActionMenuItemView extends LinearLayout } public boolean prefersCondensedTitle() { - return false; + return true; } public void setCheckable(boolean checkable) { @@ -106,10 +106,11 @@ public class ActionMenuItemView extends LinearLayout mImageButton.setImageDrawable(icon); if (icon != null) { mImageButton.setVisibility(VISIBLE); - mTextButton.setVisibility(GONE); } else { mImageButton.setVisibility(GONE); } + + mTextButton.setVisibility(icon == null || mItemData.showsTextAsAction() ? VISIBLE : GONE); } public boolean hasText() { diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java index 463902fe53e9..8e491e9fdd62 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuView.java @@ -20,9 +20,9 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; +import android.text.TextUtils; import android.util.AttributeSet; import android.view.Gravity; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; @@ -186,10 +186,14 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo boolean needsDivider = false; for (int i = 0; i < itemCount; i++) { + final MenuItemImpl itemData = itemsToShow.get(i); + boolean hasDivider = false; + if (needsDivider) { addView(makeDividerView(), makeDividerLayoutParams()); + hasDivider = true; } - final MenuItemImpl itemData = itemsToShow.get(i); + View actionView = itemData.getActionView(); if (actionView != null) { @@ -199,14 +203,22 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo } addView(actionView, makeActionViewLayoutParams(actionView)); } else { - needsDivider = addItemView(i == 0 || !needsDivider, - (ActionMenuItemView) itemData.getItemView( - MenuBuilder.TYPE_ACTION_BUTTON, this)); + ActionMenuItemView view = (ActionMenuItemView) itemData.getItemView( + MenuBuilder.TYPE_ACTION_BUTTON, this); + view.setItemInvoker(this); + if (i > 0 && !hasDivider && view.hasText() && itemData.getIcon() == null) { + addView(makeDividerView(), makeDividerLayoutParams()); + } + addView(view); + needsDivider = view.hasText(); } } if (reserveOverflow) { if (mMenu.getNonActionItems(true).size() > 0) { + if (itemCount > 0) { + addView(makeDividerView(), makeDividerLayoutParams()); + } OverflowMenuButton button = new OverflowMenuButton(mContext); addView(button); mOverflowButton = button; diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java index e15587520625..02584b618cf2 100644 --- a/core/java/com/android/internal/view/menu/ListMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java @@ -44,6 +44,7 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView private Drawable mBackground; private int mTextAppearance; private Context mTextAppearanceContext; + private boolean mPreserveIconSpacing; private int mMenuType; @@ -57,6 +58,8 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView mBackground = a.getDrawable(com.android.internal.R.styleable.MenuView_itemBackground); mTextAppearance = a.getResourceId(com.android.internal.R.styleable. MenuView_itemTextAppearance, -1); + mPreserveIconSpacing = a.getBoolean( + com.android.internal.R.styleable.MenuView_preserveIconSpacing, false); mTextAppearanceContext = context; a.recycle(); @@ -184,8 +187,8 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView } public void setIcon(Drawable icon) { - - if (!mItemData.shouldShowIcon(mMenuType)) { + final boolean showIcon = mItemData.shouldShowIcon(mMenuType); + if (!showIcon && !mPreserveIconSpacing) { return; } @@ -197,8 +200,8 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView insertIconView(); } - if (icon != null) { - mIconView.setImageDrawable(icon); + if (icon != null || mPreserveIconSpacing) { + mIconView.setImageDrawable(showIcon ? icon : null); if (mIconView.getVisibility() != VISIBLE) { mIconView.setVisibility(VISIBLE); diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java index e1aa385fa051..6fbae9446f30 100644 --- a/core/java/com/android/internal/view/menu/MenuItemImpl.java +++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java @@ -644,7 +644,6 @@ public final class MenuItemImpl implements MenuItem { public boolean shouldShowIcon(int menuType) { return menuType == MenuBuilder.TYPE_ICON || menuType == MenuBuilder.TYPE_ACTION_BUTTON || - menuType == MenuBuilder.TYPE_POPUP || mMenu.getOptionalIconsVisible(); } diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 1d9276a39daf..297dde78254b 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -437,7 +437,7 @@ public class ActionBarView extends ViewGroup { mListNavLayout = new LinearLayout(mContext, null, com.android.internal.R.attr.actionBarTabBarStyle); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( - LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); params.gravity = Gravity.CENTER; mListNavLayout.addView(mSpinner, params); } diff --git a/core/res/res/drawable-hdpi/stat_notify_chat.png b/core/res/res/drawable-hdpi/stat_notify_chat.png Binary files differindex 95b2700f337c..71ea8de6b682 100644 --- a/core/res/res/drawable-hdpi/stat_notify_chat.png +++ b/core/res/res/drawable-hdpi/stat_notify_chat.png diff --git a/core/res/res/drawable-hdpi/stat_notify_email_generic.png b/core/res/res/drawable-hdpi/stat_notify_email_generic.png Binary files differindex f222872886a9..bc5fcab3c365 100644 --- a/core/res/res/drawable-hdpi/stat_notify_email_generic.png +++ b/core/res/res/drawable-hdpi/stat_notify_email_generic.png diff --git a/core/res/res/drawable-hdpi/stat_notify_gmail.png b/core/res/res/drawable-hdpi/stat_notify_gmail.png Binary files differindex 2d5686ddeef3..ea8beaee100a 100644 --- a/core/res/res/drawable-hdpi/stat_notify_gmail.png +++ b/core/res/res/drawable-hdpi/stat_notify_gmail.png diff --git a/core/res/res/drawable-mdpi/stat_notify_chat.png b/core/res/res/drawable-mdpi/stat_notify_chat.png Binary files differindex f199fcea6ef9..306d9c561b6b 100644 --- a/core/res/res/drawable-mdpi/stat_notify_chat.png +++ b/core/res/res/drawable-mdpi/stat_notify_chat.png diff --git a/core/res/res/drawable-mdpi/stat_notify_email_generic.png b/core/res/res/drawable-mdpi/stat_notify_email_generic.png Binary files differindex 536e8ec8818e..1620ad537eaf 100644 --- a/core/res/res/drawable-mdpi/stat_notify_email_generic.png +++ b/core/res/res/drawable-mdpi/stat_notify_email_generic.png diff --git a/core/res/res/drawable-mdpi/stat_notify_gmail.png b/core/res/res/drawable-mdpi/stat_notify_gmail.png Binary files differindex 89fe3cd6daed..4860c3459ff6 100644 --- a/core/res/res/drawable-mdpi/stat_notify_gmail.png +++ b/core/res/res/drawable-mdpi/stat_notify_gmail.png diff --git a/core/res/res/drawable-xlarge-hdpi/stat_notify_chat.png b/core/res/res/drawable-xlarge-hdpi/stat_notify_chat.png Binary files differnew file mode 100644 index 000000000000..e936faceda63 --- /dev/null +++ b/core/res/res/drawable-xlarge-hdpi/stat_notify_chat.png diff --git a/core/res/res/drawable-xlarge-hdpi/stat_notify_disk_full.png b/core/res/res/drawable-xlarge-hdpi/stat_notify_disk_full.png Binary files differnew file mode 100644 index 000000000000..eb626df39878 --- /dev/null +++ b/core/res/res/drawable-xlarge-hdpi/stat_notify_disk_full.png diff --git a/core/res/res/drawable-xlarge-hdpi/stat_notify_email_generic.png b/core/res/res/drawable-xlarge-hdpi/stat_notify_email_generic.png Binary files differnew file mode 100644 index 000000000000..d6bc7d30cddb --- /dev/null +++ b/core/res/res/drawable-xlarge-hdpi/stat_notify_email_generic.png diff --git a/core/res/res/drawable-xlarge-hdpi/stat_notify_error.png b/core/res/res/drawable-xlarge-hdpi/stat_notify_error.png Binary files differnew file mode 100644 index 000000000000..8c8f25dbc171 --- /dev/null +++ b/core/res/res/drawable-xlarge-hdpi/stat_notify_error.png diff --git a/core/res/res/drawable-xlarge-hdpi/stat_notify_gmail.png b/core/res/res/drawable-xlarge-hdpi/stat_notify_gmail.png Binary files differnew file mode 100644 index 000000000000..661cc2ff47b8 --- /dev/null +++ b/core/res/res/drawable-xlarge-hdpi/stat_notify_gmail.png diff --git a/core/res/res/drawable-xlarge-mdpi/stat_notify_chat.png b/core/res/res/drawable-xlarge-mdpi/stat_notify_chat.png Binary files differnew file mode 100644 index 000000000000..b2d71862c8cc --- /dev/null +++ b/core/res/res/drawable-xlarge-mdpi/stat_notify_chat.png diff --git a/core/res/res/drawable-xlarge-mdpi/stat_notify_disk_full.png b/core/res/res/drawable-xlarge-mdpi/stat_notify_disk_full.png Binary files differnew file mode 100644 index 000000000000..36ab1ff76f47 --- /dev/null +++ b/core/res/res/drawable-xlarge-mdpi/stat_notify_disk_full.png diff --git a/core/res/res/drawable-xlarge-mdpi/stat_notify_email_generic.png b/core/res/res/drawable-xlarge-mdpi/stat_notify_email_generic.png Binary files differnew file mode 100644 index 000000000000..a14b3c7d0e7c --- /dev/null +++ b/core/res/res/drawable-xlarge-mdpi/stat_notify_email_generic.png diff --git a/core/res/res/drawable-xlarge-mdpi/stat_notify_error.png b/core/res/res/drawable-xlarge-mdpi/stat_notify_error.png Binary files differnew file mode 100644 index 000000000000..81a66c112876 --- /dev/null +++ b/core/res/res/drawable-xlarge-mdpi/stat_notify_error.png diff --git a/core/res/res/drawable-xlarge-mdpi/stat_notify_gmail.png b/core/res/res/drawable-xlarge-mdpi/stat_notify_gmail.png Binary files differnew file mode 100644 index 000000000000..a286ac611169 --- /dev/null +++ b/core/res/res/drawable-xlarge-mdpi/stat_notify_gmail.png diff --git a/core/res/res/layout/action_menu_item_layout.xml b/core/res/res/layout/action_menu_item_layout.xml index e502b1e6c03a..e0018945d7b4 100644 --- a/core/res/res/layout/action_menu_item_layout.xml +++ b/core/res/res/layout/action_menu_item_layout.xml @@ -20,10 +20,11 @@ android:layout_gravity="center" android:addStatesFromChildren="true" android:background="?attr/selectableItemBackground" - android:minWidth="64dip" - android:minHeight="?attr/actionBarSize" + android:gravity="center" android:paddingLeft="12dip" - android:paddingRight="12dip"> + android:paddingRight="12dip" + android:minWidth="64dip" + android:minHeight="?attr/actionBarSize"> <ImageButton android:id="@+id/imageButton" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/core/res/res/layout/list_menu_item_icon.xml b/core/res/res/layout/list_menu_item_icon.xml index 2be9fab3fc94..6ff14dd8b197 100644 --- a/core/res/res/layout/list_menu_item_icon.xml +++ b/core/res/res/layout/list_menu_item_icon.xml @@ -19,6 +19,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginLeft="2dip" + android:layout_marginRight="8dip" android:duplicateParentState="true" /> diff --git a/core/res/res/layout/popup_menu_item_layout.xml b/core/res/res/layout/popup_menu_item_layout.xml index 2bbb4d06baf1..d22f74a055b7 100644 --- a/core/res/res/layout/popup_menu_item_layout.xml +++ b/core/res/res/layout/popup_menu_item_layout.xml @@ -16,7 +16,8 @@ <com.android.internal.view.menu.ListMenuItemView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="?android:attr/listPreferredItemHeight" + android:layout_height="48dip" + android:minWidth="196dip" android:paddingLeft="16dip" android:paddingRight="16dip"> @@ -28,8 +29,6 @@ android:layout_weight="1" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginLeft="6dip" - android:layout_marginRight="6dip" android:duplicateParentState="true"> <TextView diff --git a/core/res/res/layout/volume_adjust.xml b/core/res/res/layout/volume_adjust.xml index c44ed0bf91df..18da85f93d9a 100644 --- a/core/res/res/layout/volume_adjust.xml +++ b/core/res/res/layout/volume_adjust.xml @@ -14,24 +14,32 @@ limitations under the License. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="@android:drawable/toast_frame" - android:orientation="vertical" - android:gravity="center_horizontal"> + android:background="@android:drawable/dialog_full_holo_dark" + android:gravity="left"> + + <LinearLayout + android:layout_width="416dip" + android:layout_height="wrap_content" + android:paddingLeft="16dip" + android:paddingTop="16dip" + android:paddingRight="16dip" + android:paddingBottom="8dip" + android:orientation="vertical"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="14dip" - android:gravity="center_vertical"> + android:layout_marginBottom="8dip" + android:gravity="left"> <ImageView android:id="@+id/other_stream_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginRight="6dip" /> + android:layout_marginRight="16dip" /> <TextView android:layout_width="wrap_content" @@ -56,13 +64,9 @@ <ProgressBar style="?android:attr/progressBarStyleHorizontal" android:id="@+id/level" - android:layout_width="200dip" - android:layout_height="wrap_content" - android:layout_marginTop="14dip" - android:layout_marginBottom="14dip" - android:layout_marginLeft="25dip" - android:layout_marginRight="25dip" /> - -</LinearLayout> + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </LinearLayout> +</FrameLayout> diff --git a/core/res/res/values-es-rUS-xlarge/strings.xml b/core/res/res/values-es-rUS-xlarge/strings.xml index 4743ac5daf1c..b1409baaf16c 100644 --- a/core/res/res/values-es-rUS-xlarge/strings.xml +++ b/core/res/res/values-es-rUS-xlarge/strings.xml @@ -303,6 +303,10 @@ <!-- XL --> <string name="status_bar_notification_info_overflow" msgid="1081154808901480710">"100+"</string> <!-- XL --> + <string name="permlab_accessMtp" msgid="2385215229145694622">"implementar protocolo MTP"</string> + <!-- XL --> + <string name="permdesc_accessMtp" msgid="4707854877711083465">"Permite acceso al driver kernel MTP para implementar el protocolo MTP USB."</string> + <!-- XL --> <string name="permlab_mediaStorageWrite" product="default" msgid="5585262071354704256">"modificar/eliminar los contenidos del almacenamientos de medios internos"</string> <!-- XL --> <string name="permdesc_mediaStorageWrite" product="default" msgid="2372999661142345443">"Permite que una aplicación modifique los contenidos del almacenamiento interno de medios."</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index a29f4172c408..02855b5405ca 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2349,6 +2349,8 @@ <attr name="windowAnimationStyle" /> <!-- Default disabled icon alpha for each menu item that shows an icon. --> <attr name="itemIconDisabledAlpha" format="float" /> + <!-- Whether space should be reserved in layout when an icon is missing. --> + <attr name="preserveIconSpacing" format="boolean" /> </declare-styleable> <declare-styleable name="IconMenuView"> <!-- Defines the height of each row. --> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index 93ccd4f9b8cd..837e04f536e8 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -74,4 +74,5 @@ <item type="id" name="rowTypeId" /> <item type="id" name="up" /> <item type="id" name="viewAnimation" /> + <item type="id" name="viewAlphaAnimation" /> </resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index cf9c00b2547d..1183915003d3 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1194,7 +1194,7 @@ </style> <style name="TextAppearance.Holo.Widget.PopupMenu.Large"> - <item name="android:textSize">22sp</item> + <item name="android:textSize">18sp</item> </style> <style name="TextAppearance.Holo.Widget.PopupMenu.Small"> @@ -1578,7 +1578,6 @@ </style> <style name="Widget.Holo.Spinner.DropDown.ActionBar"> - <item name="android:background">@android:drawable/spinner_cab_background_holo_dark</item> </style> <style name="Widget.Holo.CompoundButton.Star" parent="Widget.CompoundButton.Star"> @@ -1651,6 +1650,8 @@ </style> <style name="Widget.Holo.ActionButton" parent="Widget.ActionButton"> + <item name="android:minWidth">64dip</item> + <item name="android:gravity">center</item> <item name="android:paddingLeft">16dip</item> <item name="android:paddingRight">16dip</item> <item name="android:minHeight">56dip</item> @@ -1677,9 +1678,6 @@ </style> <style name="Widget.Holo.ActionBarView_TabBar" parent="Widget.ActionBarView_TabBar"> - <item name="divider">?android:attr/dividerVertical</item> - <item name="showDividers">beginning|end</item> - <item name="dividerPadding">8dip</item> </style> <style name="Widget.Holo.ActionBarView_TabText" parent="Widget.ActionBarView_TabText"> @@ -1920,7 +1918,6 @@ </style> <style name="Widget.Holo.Light.Spinner.DropDown.ActionBar"> - <item name="android:background">@android:drawable/spinner_cab_background_holo_light</item> </style> <style name="Widget.Holo.Light.CompoundButton.Star" parent="Widget.CompoundButton.Star"> diff --git a/docs/html/guide/topics/advanced/aidl.jd b/docs/html/guide/topics/advanced/aidl.jd new file mode 100644 index 000000000000..fef46eca464f --- /dev/null +++ b/docs/html/guide/topics/advanced/aidl.jd @@ -0,0 +1,387 @@ +page.title=Android Interface Definition Language (AIDL) +@jd:body + + + +<p>AIDL (Android Interface Definition Language) is similar to other IDLs you might have +worked with. It allows you to define the programming interface that both +the client and service agree upon in order to communicate with each other and allows for +interprocess communication (IPC). On Android, one process can not normally access the +memory of another process. So to talk, they need to decompose their objects into primitives that the +operating system can understand, and "marshall" the object across that boundary for you. The code to +do that marshalling is tedious to write, so Android handles it for you with AIDL.</p> + +<p class="note"><strong>Note:</strong> Using AIDL is necessary only if you allow clients from +different applications to access your service for IPC and want to handle multithreading in your +service. If you do not need to perform IPC across +different applications, you should create your interface <a href="Binder">implementing a +Binder</a> or, if you want to perform IPC, but do not need to handle multithreading, then you +should implement your interface <a href="#Messenger">using a Messenger</a>.</p> + +<p>Before you begin designing your AIDL interface, be aware that calls on to an AIDL interface are +direct function calls. You can not generally make assumptions about the thread in which the call +will happen. What happens is different depending on whether the call is from a thread in the +local process, or coming from a remote process. Specifically:</p> + +<ul> +<li>Calls from the local process are executed in the same thread that is making the call. If this is +your main UI thread, that thread will continue executing into the aidl interface. If it is another +thread, that is one that will be executing your code. Thus if only local threads are accessing the +interface, you can completely control which threads are executing in it (but if that is the case, +why are you defining an aidl interface at all?).</li> + +<li>Calls from a remote process are dispatched from a thread pool the platform maintains inside of +your own process. You must be prepared for incoming calls from unknown threads, with multiple calls +happening at the same time. In other words, an implementation of an aidl interface must be +completely thread-safe.</li> + +<li>The "oneway" keyword modifies the behavior of remote calls. When used, a remote call will not +block until its call completes; it simply sends the transaction data and immediately returns. The +implementation of the interface will eventually receive this as a regular call from the {@link +android.os.Binder} thread pool as a normal remote call. If "oneway" is used with a local call, +there is no impact and the call is still synchronous.</li> +</ul> + + +<h2 id="Defining">Defining an AIDL Interface</h2> + +<p>You must define your AIDL interface in an {@code .aidl} file using the Java +programming language syntax, then save it in the source code (in the {@code src/} directory) of both +the application hosting the service and any other application that will bind to the service.</p> + +<p>When you build the projects containing the {@code .aidl} file, the Android SDK tools generate an +{@link android.os.IBinder} class based on your AIDL interface (and saves the file in the {@code +gen/} directory). This class defines the APIs you can call to perform RPC as an interface—you +must implement the interface in your service.</p> + +<p>To create a bounded service using AIDL, follow these steps:</p> +<ol> + <li><a href="#CreateAidl">Create the .aidl file</a> + <p>This file defines the programming interface with method signatures.</p> + </li> + <li><a href="#ImplementTheInterface">Implement the interface</a> + <p>The Android SDK tools generate an interface in the Java programming language, based on your +{@code .aidl} file. This interface has an inner abstract class named {@code Stub} that extends +{@link android.os.Binder} and implements methods from your AIDL interface. You must extend the +{@code Stub} class and implement the methods.</p> + </li> + <li><a href="#ExposeTheInterface">Expose the interface to clients</a> + <p>Implement a {@link android.app.Service Service} and override {@link +android.app.Service#onBind onBind()} to return your implementation of the {@code Stub} +class.</p> + </li> +</ol> + +<p class="caution"><strong>Caution:</strong> Any changes that you make to your AIDL interface after +your first release must remain backward compatible in order to avoid breaking other applications +that use your service. That is, because your {@code .aidl} file must be copied to other applications +in order for them to access your service's interface, you must maintain support for the original +interface.</p> + + +<h3 id="CreateAidl">1. Create the .aidl file</h3> + +<p>AIDL uses a simple syntax that lets you declare an interface with one or more methods that can +take parameters and return values. The parameters and return values can be of any type, even other +AIDL-generated interfaces.</p> + +<p>The syntax for the {@code .aidl} file uses the Java programming language. The file defines a +single interface and requires only the interface declaration and method signatures.</p> + +<p>By default, AIDL supports the following data types:</p> + +<ul> + <li>All primitive types in the Java programming language ({@code int}, {@code long}, {@code +char}, {@code boolean}, etc.)</li> + <li>{@link java.lang.String}</li> + <li>{@link java.lang.CharSequence}</li> + <li>{@link java.util.List} + <p>All elements in the {@link java.util.List} must be one of the supported data types in this +list or one of the other AIDL-generated interfaces or parcelables you've declared. A {@link +java.util.List} may optionally be used as a "generic" class (e.g. <code>List<String></code>). +The actual concrete class that the other side will receive will always be an {@link +java.util.ArrayList}, although the method will be generated to use the {@link +java.util.List} interface.</p> + </li> + <li>{@link java.util.Map} + <p>All elements in the {@link java.util.Map} must be one of the supported data types in this +list or one of the other AIDL-generated interfaces or parcelables you've declared. Generic maps, +(such as those of the form +{@code Map<String,Integer>} are not supported. The actual concrete class that the other side +will receive will always be a {@link java.util.HashMap}, although the method will be generated to +use the {@link java.util.Map} interface.</p> + </li> +</ul> + +<p>You must include an {@code import} statement for each additional type not listed above, even if +they are defined in the same package as your interface.</p> + +<p>When defining methods for your service interface, be aware that:</p> +<ul> + <li>Methods can take zero or more parameters, and return a value or void.</li> + <li>All non-primitive parameters require a directional tag indicating which way the data will go. +Either <code>in</code>, <code>out</code>, or <code>inout</code> (see the example below). + <p>Primitives are <code>in</code> by default, and cannot be otherwise.</p> + <p class="caution"><strong>Caution:</strong> You should limit the direction to what is truly +needed, because marshalling parameters is expensive.</p></li> + <li>All code comments included in the {@code .aidl} file are included in the generated {@link +android.os.IBinder} interface (except for comments before the import and package +statements).</li> +</ul> + +<p>Here is an example {@code .aidl} file:</p> + +<pre> +// IRemoteService.aidl +package com.example.android; + +// Declare any non-default types here with import statements + +/** Example service interface */ +interface IRemoteService { + /** Request the process ID of this service, to do evil things with it. */ + int getPid(); + + /** Demonstrates some basic types that you can use as parameters + * and return values in AIDL. + */ + void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, + double aDouble, String aString); +} +</pre> + +<p>Simply save your {@code .aidl} file in your project's {@code src/} directory and when you +build your application, the SDK tools will generate the binder class file in your project's +{@code gen/} directory. The generated file name matches the {@code .aidl} file name, but with a +{@code .java} extension (for example, {@code IRemoteService.aidl} results in {@code +IRemoteService.java}).</p> + +<p>If you use Eclipse, the incremental build generates the binder class almost immediately. If you +do not use Eclipse, then the Ant tool generates the binder class next time you build your +application—you should build your project with <code>ant debug</code> (or <code>ant +release</code>) as soon as you're finished writing the {@code .aidl} file, so that your code can +link against the generated class.</p> + + +<h3 id="ImplementTheInterface">2. Implement the interface</h3> + +<p>When you build your application, the Android SDK tools generate a {@code .java} interface file +named after your {@code .aidl} file. The generated interface includes a subclass named {@code Stub} +that is an abstract implementation of its parent interface (for example, {@code +YourInterface.Stub}) and declares all the methods from the {@code .aidl} file.</p> + +<p class="note"><strong>Note:</strong> {@code Stub} also +defines a few helper methods, most notably {@code asInterface()}, which takes an {@link +android.os.IBinder} (usually the one passed to a client's {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} callback method) and +returns an instance of the stub interface. See the section <a href="#calling">Calling an IPC +Method</a> for more details on how to make this cast.</p></p> + +<p>To implement the interface generated from the {@code .aidl}, extend the generated {@link +android.os.Binder} interface (for example, {@code YourInterface.Stub}) and implement the methods +inherited from the {@code .aidl} file.</p> + +<p>Here is an example implementation of an interface called {@code IRemoteService} (defined by the +{@code IRemoteService.aidl} example, above) using an anonymous instance:</p> + +<pre> +private final IRemoteService.Stub mBinder = new IRemoteService.Stub() { + public int getPid(){ + return Process.myPid(); + } + public void basicTypes(int anInt, long aLong, boolean aBoolean, + float aFloat, double aDouble, String aString) { + // Does nothing + } +}; +</pre> + +<p>Now the {@code mBinder} is an instance of the {@code Stub} class (a {@link android.os.Binder}), +which defines the RPC interface for the service. In the next step, this instance is exposed to +clients so they can interact with the service.</p> + +<p>There are a few rules you should be aware of when implementing your AIDL interface: </p> +<ul> + <li>Incoming calls are not guaranteed to be executed on the main thread, so you need to think +about multithreading from the start and properly build your service to be thread-safe.</li> + <li>By default, RPC calls are synchronous. If you know that the service takes more than a few +milliseconds to complete a request, you should not call it from the activity's main thread, because +it might hang the application (Android might display an "Application is Not Responding" +dialog)—you should usually call them from a separate thread in the client. </li> + <li>No exceptions that you throw are sent back to the caller.</li> + <li>Only methods are supported; you cannot expose static fields in AIDL.</li> +</ul> + + +<h3 id="ExposeTheInterface">3. Expose the interface to clients</h3> + +<p>Once you've implemented the interface for your service, you need to expose it to +clients so they can bind to it. To expose the interface +for your service, extend {@link android.app.Service Service} and implement {@link +android.app.Service#onBind onBind()} to return an instance of your class that implements +the generated {@code Stub} (as discussed in the previous section). Here's an example +service that exposes the {@code IRemoteService} example interface to clients. </p> + +<pre> +public class RemoteService extends Service { + @Override + public void onCreate() { + super.onCreate(); + } + + @Override + public IBinder onBind(Intent intent) { + // Return the interface + return mBinder; + } + + private final IRemoteService.Stub mBinder = new IRemoteService.Stub() { + public int getPid(){ + return Process.myPid(); + } + public void basicTypes(int anInt, long aLong, boolean aBoolean, + float aFloat, double aDouble, String aString) { + // Does nothing + } + }; +} +</pre> + +<p>Now, when a client (such as an activity) calls {@link android.content.Context#bindService +bindService()} to connect to this service, the client's {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} callback receives the +{@code mBinder} instance returned by the service's {@link android.app.Service#onBind onBind()} +method.</p> + +<p>The client must also have access to the interface class, so if the client and service are in +separate applications, then the client's application must have a copy of the {@code .aidl} file +in its {@code src/} directory (which generates the {@code android.os.Binder} +interface—providing the client access to the AIDL methods).</p> + +<p>When the client receives the {@link android.os.IBinder} in the {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} callback, it must call +<code><em>YourServiceInterface</em>.Stub.asInterface(service)</code> to cast the returned +parameter to <code><em>YourServiceInterface</em></code> type. For example:</p> + +<pre> +IRemoteService mIRemoteService; +private ServiceConnection mConnection = new ServiceConnection() { + // Called when the connection with the service is established + public void onServiceConnected(ComponentName className, IBinder service) { + // Following the example above for an AIDL interface, + // this gets an instance of the IRemoteInterface, which we can use to call on the service + mIRemoteService = IRemoteService.Stub.asInterface(service); + } + + // Called when the connection with the service disconnects unexpectedly + public void onServiceDisconnected(ComponentName className) { + Log.e(TAG, "onServiceDisconnected"); + } +}; +</pre> + +<p>For more sample code, see the <a +href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code +RemoteService.java}</a> class in <a +href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a>.</p> + + + + + + + + +<h2 id="PassingObjects">Passing Objects over IPC</h2> + +<p>If you have a class that you would like to send from one process to another through +an IPC interface, you can do that. However, you must ensure that the code for your class is +available to the other side of the IPC and the class must support the {@link +android.os.Parcelable} interface, in order for the objects to be decomposed into primitives and +marshalled across processes by the Android system.</p> + +<p>There are five parts to making a class support the {@link android.os.Parcelable} protocol:</b> +<ol> +<li>Make your class implement the {@link android.os.Parcelable} interface.</li> +<li>Implement {@link android.os.Parcelable#writeToParcel writeToParcel}, which takes the +current state of the object and writes it to a {@link android.os.Parcel}.</li> +<li>Add a static field called <code>CREATOR</code> to your class which is an object implementing +the {@link android.os.Parcelable.Creator Parcelable.Creator} interface.</li> +<li>Finally, create an {@code .aidl} file that declares your parcelable class (as shown for the +{@code Rect.aidl} file, below). + <p>If you are using a custom build process, do <em>not</em> add the {@code .aidl} file to your +build. Similar to a header file in the C language, this {@code .aidl} file isn't compiled.</p></li> +</ol> + +<p>AIDL will use these methods and fields in the code it generates to marshall and unmarshall +your objects.</p> + +<p>For example, here is a {@code Rect.aidl} file to create a {@code Rect} class that's +parcelable:</p> + +<pre> +package android.graphics; + +// Declare Rect so AIDL can find it and knows that it implements +// the parcelable protocol. +parcelable Rect; +</pre> + +<p>And here is an example of how the {@link android.graphics.Rect} class implements the +{@link android.os.Parcelable} protocol.</p> + +<pre> +import android.os.Parcel; +import android.os.Parcelable; + +public final class Rect implements Parcelable { + public int left; + public int top; + public int right; + public int bottom; + + public static final Parcelable.Creator<Rect> CREATOR = new +Parcelable.Creator<Rect>() { + public Rect createFromParcel(Parcel in) { + return new Rect(in); + } + + public Rect[] newArray(int size) { + return new Rect[size]; + } + }; + + public Rect() { + } + + private Rect(Parcel in) { + readFromParcel(in); + } + + public void writeToParcel(Parcel out) { + out.writeInt(left); + out.writeInt(top); + out.writeInt(right); + out.writeInt(bottom); + } + + public void readFromParcel(Parcel in) { + left = in.readInt(); + top = in.readInt(); + right = in.readInt(); + bottom = in.readInt(); + } +} +</pre> + +<p>The marshalling in the {@code Rect} class is pretty simple. Take a look at the other +methods on {@link android.os.Parcel} to see the other kinds of values you can write +to a Parcel.</p> + +<p class="warning"><strong>Warning:</strong> Don't forget the security implications of receiving +data from other processes. In this case, the {@code Rect} will read four numbers from the {@link +android.os.Parcel}, but it is up to you to ensure that these are within the acceptable range of +values for whatever the caller is trying to do. See <a +href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> for more +information about how to keep your application secure from malware.</p> + diff --git a/docs/html/guide/topics/fundamentals/bound-services.jd b/docs/html/guide/topics/fundamentals/bound-services.jd new file mode 100644 index 000000000000..e5d626c0799c --- /dev/null +++ b/docs/html/guide/topics/fundamentals/bound-services.jd @@ -0,0 +1,675 @@ +page.title=Bound Services +parent.title=Services +parent.link=services.html +@jd:body + + +<div id="qv-wrapper"> +<ol id="qv"> +<h2>Quickview</h2> +<ul> + <li>A bound service allows other components to bind to it, in order to interact with it and +perform interprocess communication</li> + <li>A bound service is destroyed once all clients unbind, unless the service was also started</li> +</ul> +<h2>In this document</h2> +<ol> + <li><a href="#Basics">The Basics</a></li> + <li><a href="#Creating">Creating a Bound Service</a> + <ol> + <li><a href="#Binder">Extending the Binder class</a></li> + <li><a href="#Messenger">Using a Messenger</a></li> + </ol> + </li> + <li><a href="#Binding">Binding to a Service</a></li> + <li><a href="#Lifecycle">Managing the Lifecycle of a Bound Service</a></li> +</ol> + +<h2>Key classes</h2> +<ol> + <li>{@link android.app.Service}</li> + <li>{@link android.content.ServiceConnection}</li> + <li>{@link android.os.IBinder}</li> +</ol> + +<h2>Samples</h2> +<ol> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code + RemoteService}</a></li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code + LocalService}</a></li> +</ol> + +<h2>See also</h2> +<ol> + <li><a href="{@docRoot}guide/topics/fundamentals/services.html">Services</a></li> +</ol> +</div> + + +<p>A bound service is the server in a client-server interface. A bound service allows components +(such as activities) to bind to the service, send requests, receive responses, and even perform +interprocess communication (IPC). A bound service typically lives only while it serves another +application component and does not run in the background indefinitely.</p> + +<p>This document shows you how to create a bound service, including how to bind +to the service from other application components. However, you should also refer to the <a +href="{@docRoot}guide/topics/fundamentals/services.html">Services</a> document for additional +information about services in general, such as how to deliver notifications from a service, set +the service to run in the foreground, and more.</p> + + +<h2 id="Basics">The Basics</h2> + +<p>A bound service is an implementation of the {@link android.app.Service} class that allows +other applications to bind to it and interact with it. To provide binding for a +service, you must implement the {@link android.app.Service#onBind onBind()} callback method. This +method returns an {@link android.os.IBinder} object that defines the programming interface that +clients can use to interact with the service.</p> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>Binding to a Started Service</h3> + +<p>As discussed in the <a href="{@docRoot}guide/topics/fundamentals/services.html">Services</a> +document, you can create a service that is both started and bound. That is, the service can be +started by calling {@link android.content.Context#startService startService()}, which allows the +service to run indefinitely, and also allow a client to bind to the service by calling {@link +android.content.Context#bindService bindService()}. + <p>If you do allow your service to be started and bound, then when the service has been +started, the system does <em>not</em> destroy the service when all clients unbind. Instead, you must +explicitly stop the service, by calling {@link android.app.Service#stopSelf stopSelf()} or {@link +android.content.Context#stopService stopService()}.</p> + +<p>Although you should usually implement either {@link android.app.Service#onBind onBind()} +<em>or</em> {@link android.app.Service#onStartCommand onStartCommand()}, it's sometimes necessary to +implement both. For example, a music player might find it useful to allow its service to run +indefinitely and also provide binding. This way, an activity can start the service to play some +music and the music continues to play even if the user leaves the application. Then, when the user +returns to the application, the activity can bind to the service to regain control of playback.</p> + +<p>Be sure to read the section about <a href="#Lifecycle">Managing the Lifecycle of a Bound +Service</a>, for more information about the service lifecycle when adding binding to a +started service.</p> +</div> +</div> + +<p>A client can bind to the service by calling {@link android.content.Context#bindService +bindService()}. When it does, it must provide an implementation of {@link +android.content.ServiceConnection}, which monitors the connection with the service. The {@link +android.content.Context#bindService bindService()} method returns immediately without a value, but +when the Android system creates the connection between the +client and service, it calls {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} on the {@link +android.content.ServiceConnection}, to deliver the {@link android.os.IBinder} that +the client can use to communicate with the service.</p> + +<p>Multiple clients can connect to the service at once. However, the system calls your service's +{@link android.app.Service#onBind onBind()} method to retrieve the {@link android.os.IBinder} only +when the first client binds. The system then delivers the same {@link android.os.IBinder} to any +additional clients that bind, without calling {@link android.app.Service#onBind onBind()} again.</p> + +<p>When the last client unbinds from the service, the system destroys the service (unless the +service was also started by {@link android.content.Context#startService startService()}).</p> + +<p>When you implement your bound service, the most important part is defining the interface +that your {@link android.app.Service#onBind onBind()} callback method returns. There are a few +different ways you can define your service's {@link android.os.IBinder} interface and the following +section discusses each technique.</p> + + + +<h2 id="Creating">Creating a Bound Service</h2> + +<p>When creating a service that provides binding, you must provide an {@link android.os.IBinder} +that provides the programming interface that clients can use to interact with the service. There +are three ways you can define the interface:</p> + +<dl> + <dt><a href="#Binder">Extending the Binder class</a></dt> + <dd>If your service is private to your own application and runs in the same process as the client +(which is common), you should create your interface by extending the {@link android.os.Binder} class +and returning an instance of it from +{@link android.app.Service#onBind onBind()}. The client receives the {@link android.os.Binder} and +can use it to directly access public methods available in either the {@link android.os.Binder} +implementation or even the {@link android.app.Service}. + <p>This is the preferred technique when your service is merely a background worker for your own +application. The only reason you would not create your interface this way is because +your service is used by other applications or across separate processes.</dd> + + <dt><a href="#Messenger">Using a Messenger</a></dt> + <dd>If you need your interface to work across different processes, you can create +an interface for the service with a {@link android.os.Messenger}. In this manner, the service +defines a {@link android.os.Handler} that responds to different types of {@link +android.os.Message} objects. This {@link android.os.Handler} +is the basis for a {@link android.os.Messenger} that can then share an {@link android.os.IBinder} +with the client, allowing the client to send commands to the service using {@link +android.os.Message} objects. Additionally, the client can define a {@link android.os.Messenger} of +its own so the service can send messages back. + <p>This is the simplest way to perform interprocess communication (IPC), because the {@link +android.os.Messenger} queues all requests into a single thread so that you don't have to design +your service to be thread-safe.</p> + </dd> + + <dt>Using AIDL</dt> + <dd>AIDL (Android Interface Definition Language) performs all the work to decompose objects into +primitives that the operating system can understand and marshall them across processes to perform +IPC. The previous technique, using a {@link android.os.Messenger}, is actually based on AIDL as +its underlying structure. As mentioned above, the {@link android.os.Messenger} creates a queue of +all the client requests in a single thread, so the service receives requests one at a time. If, +however, you want your service to handle multiple requests simultaneously, then you can use AIDL +directly. In this case, your service must be capable of multi-threading and be built thread-safe. + <p>To use AIDL directly, you must +create an {@code .aidl} file that defines the programming interface. The Android SDK tools use +this file to generate an abstract class that implements the interface and handles IPC, which you +can then extend within your service.</p> + </dd> +</dl> + + <p class="note"><strong>Note:</strong> Most applications <strong>should not</strong> use AIDL to +create a bound service, because it may require multithreading capabilities and +can result in a more complicated implementation. As such, AIDL is not suitable for most applications +and this document does not discuss how to use it for your service. If you're certain that you need +to use AIDL directly, see the <a href="{@docRoot}guide/topics/advanced/aidl.html">AIDL</a> +document.</p> + + + + +<h3 id="Binder">Extending the Binder class</h3> + +<p>If your service is used only by the local application and does not need to work across processes, +then you can implement your own {@link android.os.Binder} class that provides your client direct +access to public methods in the service.</p> + +<p class="note"><strong>Note:</strong> This works only if the client and service are in the same +application and process, which is most common. For example, this would work well for a music +application that needs to bind an activity to its own service that's playing music in the +background.</p> + +<p>Here's how to set it up:</p> +<ol> + <li>In your service, create an instance of {@link android.os.Binder} that either: + <ul> + <li>contains public methods that the client can call</li> + <li>returns the current {@link android.app.Service} instance, which has public methods the +client can call</li> + <li>or, returns an instance of another class hosted by the service with public methods the +client can call</li> + </ul> + <li>Return this instance of {@link android.os.Binder} from the {@link +android.app.Service#onBind onBind()} callback method.</li> + <li>In the client, receive the {@link android.os.Binder} from the {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} callback method and +make calls to the bound service using the methods provided.</li> +</ol> + +<p class="note"><strong>Note:</strong> The reason the service and client must be in the same +application is so the client can cast the returned object and properly call its APIs. The service +and client must also be in the same process, because this technique does not perform any +marshalling across processes.</p> + +<p>For example, here's a service that provides clients access to methods in the service through +a {@link android.os.Binder} implementation:</p> + +<pre> +public class LocalService extends Service { + // Binder given to clients + private final IBinder mBinder = new LocalBinder(); + // Random number generator + private final Random mGenerator = new Random(); + + /** + * Class used for the client Binder. Because we know this service always + * runs in the same process as its clients, we don't need to deal with IPC. + */ + public class LocalBinder extends Binder { + LocalService getService() { + // Return this instance of LocalService so clients can call public methods + return LocalService.this; + } + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + /** method for clients */ + public int getRandomNumber() { + return mGenerator.nextInt(100); + } +} +</pre> + +<p>The {@code LocalBinder} provides the {@code getService()} method for clients to retrieve the +current instance of {@code LocalService}. This allows clients to call public methods in the +service. For example, clients can call {@code getRandomNumber()} from the service.</p> + +<p>Here's an activity that binds to {@code LocalService} and calls {@code getRandomNumber()} +when a button is clicked:</p> + +<pre> +public class BindingActivity extends Activity { + LocalService mService; + boolean mBound = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } + + @Override + protected void onStart() { + super.onStart(); + // Bind to LocalService + Intent intent = new Intent(this, LocalService.class); + bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + } + + @Override + protected void onStop() { + super.onStop(); + // Unbind from the service + if (mBound) { + unbindService(mConnection); + mBound = false; + } + } + + /** Called when a button is clicked (the button in the layout file attaches to + * this method with the android:onClick attribute) */ + public void onButtonClick(View v) { + if (mBound) { + // Call a method from the LocalService. + // However, if this call were something that might hang, then this request should + // occur in a separate thread to avoid slowing down the activity performance. + int num = mService.getRandomNumber(); + Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); + } + } + + /** Defines callbacks for service binding, passed to bindService() */ + private ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + // We've bound to LocalService, cast the IBinder and get LocalService instance + LocalBinder binder = (LocalBinder) service; + mService = binder.getService(); + mBound = true; + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + mBound = false; + } + }; +} +</pre> + +<p>The above sample shows how the client binds to the service using an implementation of +{@link android.content.ServiceConnection} and the {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} callback. The next +section provides more information about this process of binding to the service.</p> + +<p class="note"><strong>Note:</strong> The example above doesn't explicitly unbind from the service, +but all clients should unbind at an appropriate time (such as when the activity pauses).</p> + +<p>For more sample code, see the <a +href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code +LocalService.java}</a> class and the <a +href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.html">{@code +LocalServiceActivities.java}</a> class in <a +href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a>.</p> + + + + + +<h3 id="Messenger">Using a Messenger</h3> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h4>Compared to AIDL</h4> + <p>When you need to perform IPC, using a {@link android.os.Messenger} for your interface is +simpler than implementing it with AIDL, because {@link android.os.Messenger} queues +all calls to the service, whereas, a pure AIDL interface sends simultaneous requests to the +service, which must then handle multi-threading.</p> + <p>For most applications, the service doesn't need to perform multi-threading, so using a {@link +android.os.Messenger} allows the service to handle one call at a time. If it's important +that your service be multi-threaded, then you should use <a +href="{@docRoot}guide/topics/advanced/aidl.html">AIDL</a> to define your interface.</p> +</div> +</div> + +<p>If you need your service to communicate with remote processes, then you can use a +{@link android.os.Messenger} to provide the interface for your service. This technique allows +you to perform interprocess communication (IPC) without the need to use AIDL.</p> + +<p>Here's a summary of how to use a {@link android.os.Messenger}:</p> + +<ul> + <li>The service implements a {@link android.os.Handler} that receives a callback for each +call from a client.</li> + <li>The {@link android.os.Handler} is used to create a {@link android.os.Messenger} object +(which is a reference to the {@link android.os.Handler}).</li> + <li>The {@link android.os.Messenger} creates an {@link android.os.IBinder} that the service +returns to clients from {@link android.app.Service#onBind onBind()}.</li> + <li>Clients use the {@link android.os.IBinder} to instantiate the {@link android.os.Messenger} +(that references the service's {@link android.os.Handler}), which the client uses to send +{@link android.os.Message} objects to the service.</li> + <li>The service receives each {@link android.os.Message} in its {@link +android.os.Handler}—specifically, in the {@link android.os.Handler#handleMessage +handleMessage()} method.</li> +</ul> + + +<p>In this way, there are no "methods" for the client to call on the service. Instead, the +client delivers "messages" ({@link android.os.Message} objects) that the service receives in +its {@link android.os.Handler}.</p> + +<p>Here's a simple example service that uses a {@link android.os.Messenger} interface:</p> + +<pre> +public class MessengerService extends Service { + /** Command to the service to display a message */ + static final int MSG_SAY_HELLO = 1; + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SAY_HELLO: + Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show(); + break; + default: + super.handleMessage(msg); + } + } + } + + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + final Messenger mMessenger = new Messenger(new IncomingHandler()); + + /** + * When binding to the service, we return an interface to our messenger + * for sending messages to the service. + */ + @Override + public IBinder onBind(Intent intent) { + Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); + return mMessenger.getBinder(); + } +} +</pre> + +<p>Notice that the {@link android.os.Handler#handleMessage handleMessage()} method in the +{@link android.os.Handler} is where the service receives the incoming {@link android.os.Message} +and decides what to do, based on the {@link android.os.Message#what} member.</p> + +<p>All that a client needs to do is create a {@link android.os.Messenger} based on the {@link +android.os.IBinder} returned by the service and send a message using {@link +android.os.Messenger#send send()}. For example, here's a simple activity that binds to the +service and delivers the {@code MSG_SAY_HELLO} message to the service:</p> + +<pre> +public class ActivityMessenger extends Activity { + /** Messenger for communicating with the service. */ + Messenger mService = null; + + /** Flag indicating whether we have called bind on the service. */ + boolean mBound; + + /** + * Class for interacting with the main interface of the service. + */ + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + // This is called when the connection with the service has been + // established, giving us the object we can use to + // interact with the service. We are communicating with the + // service using a Messenger, so here we get a client-side + // representation of that from the raw IBinder object. + mService = new Messenger(service); + mBound = true; + } + + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + mService = null; + mBound = false; + } + }; + + public void sayHello(View v) { + if (!mBound) return; + // Create and send a message to the service, using a supported 'what' value + Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); + try { + mService.send(msg); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } + + @Override + protected void onStart() { + super.onStart(); + // Bind to the service + bindService(new Intent(this, MessengerService.class), mConnection, + Context.BIND_AUTO_CREATE); + } + + @Override + protected void onStop() { + super.onStop(); + // Unbind from the service + if (mBound) { + unbindService(mConnection); + mBound = false; + } + } +} +</pre> + +<p>Notice that this example does not show how the service can respond to the client. If you want the +service to respond, then you need to also create a {@link android.os.Messenger} in the client. Then +when the client receives the {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()} callback, it sends a {@link android.os.Message} to the service that includes +the client's {@link android.os.Messenger} in the {@link android.os.Message#replyTo} parameter +of the {@link android.os.Messenger#send send()} method.</p> + +<p>You can see an example of how to provide two-way messaging in the <a +href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerService.html">{@code +MessengerService.java}</a> (service) and <a +href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerServiceActivities.html">{@code +MessengerServiceActivities.java}</a> (client) samples.</p> + + + + + +<h2 id="Binding">Binding to a Service</h2> + +<p>Application components (clients) can bind to a service by calling +{@link android.content.Context#bindService bindService()}. The Android +system then calls the service's {@link android.app.Service#onBind +onBind()} method, which returns an {@link android.os.IBinder} for interacting with the service.</p> + +<p>The binding is asynchronous. {@link android.content.Context#bindService +bindService()} returns immediately and does <em>not</em> return the {@link android.os.IBinder} to +the client. To receive the {@link android.os.IBinder}, the client must create an instance of {@link +android.content.ServiceConnection} and pass it to {@link android.content.Context#bindService +bindService()}. The {@link android.content.ServiceConnection} includes a callback method that the +system calls to deliver the {@link android.os.IBinder}.</p> + +<p class="note"><strong>Note:</strong> Only activities, services, and content providers can bind +to a service—you <strong>cannot</strong> bind to a service from a broadcast receiver.</p> + +<p>So, to bind to a service from your client, you must: </p> +<ol> + <li>Implement {@link android.content.ServiceConnection}. + <p>Your implementation must override two callback methods:</p> + <dl> + <dt>{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}</dt> + <dd>The system calls this to deliver the {@link android.os.IBinder} returned by +the service's {@link android.app.Service#onBind onBind()} method.</dd> + <dt>{@link android.content.ServiceConnection#onServiceDisconnected +onServiceDisconnected()}</dt> + <dd>The Android system calls this when the connection to the service is unexpectedly +lost, such as when the service has crashed or has been killed. This is <em>not</em> called when the +client unbinds.</dd> + </dl> + </li> + <li>Call {@link +android.content.Context#bindService bindService()}, passing the {@link +android.content.ServiceConnection} implementation. </li> + <li>When the system calls your {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()} callback method, you can begin making calls to the service, using +the methods defined by the interface.</li> + <li>To disconnect from the service, call {@link +android.content.Context#unbindService unbindService()}. + <p>When your client is destroyed, it will unbind from the service, but you should always unbind +when you're done interacting with the service or when your activity pauses so that the service can +shutdown while its not being used. (Appropriate times to bind and unbind is discussed +more below.)</p> + </li> +</ol> + +<p>For example, the following snippet connects the client to the service created above by +<a href="#Binder">extending the Binder class</a>, so all it must do is cast the returned +{@link android.os.IBinder} to the {@code LocalService} class and request the {@code +LocalService} instance:</p> + +<pre> +LocalService mService; +private ServiceConnection mConnection = new ServiceConnection() { + // Called when the connection with the service is established + public void onServiceConnected(ComponentName className, IBinder service) { + // Because we have bound to an explicit + // service that is running in our own process, we can + // cast its IBinder to a concrete class and directly access it. + LocalBinder binder = (LocalBinder) service; + mService = binder.getService(); + mBound = true; + } + + // Called when the connection with the service disconnects unexpectedly + public void onServiceDisconnected(ComponentName className) { + Log.e(TAG, "onServiceDisconnected"); + mBound = false; + } +}; +</pre> + +<p>With this {@link android.content.ServiceConnection}, the client can bind to a service by passing +this it to {@link android.content.Context#bindService bindService()}. For example:</p> + +<pre> +Intent intent = new Intent(this, LocalService.class); +bindService(intent, mConnection, Context.BIND_AUTO_CREATE); +</pre> + +<ul> + <li>The first parameter of {@link android.content.Context#bindService bindService()} is an +{@link android.content.Intent} that explicitly names the service to bind (thought the intent +could be implicit).</li> +<li>The second parameter is the {@link android.content.ServiceConnection} object.</li> +<li>The third parameter is a flag indicating options for the binding. It should usually be {@link +android.content.Context#BIND_AUTO_CREATE} in order to create the service if its not already alive. +Other possible values are {@link android.content.Context#BIND_DEBUG_UNBIND} +and {@link android.content.Context#BIND_NOT_FOREGROUND}, or {@code 0} for none.</li> +</ul> + + +<h3>Additional notes</h3> + +<p>Here are some important notes about binding to a service:</p> +<ul> + <li>You should always trap {@link android.os.DeadObjectException} exceptions, which are thrown +when the connection has broken. This is the only exception thrown by remote methods.</li> + <li>Objects are reference counted across processes. </li> + <li>You should usually pair the binding and unbinding during +matching bring-up and tear-down moments of the client's lifecycle. For example: + <ul> + <li>If you only need to interact with the service while your activity is visible, you +should bind during {@link android.app.Activity#onStart onStart()} and unbind during {@link +android.app.Activity#onStop onStop()}.</li> + <li>If you want your activity to receive responses even while it is stopped in the +background, then you can bind during {@link android.app.Activity#onCreate onCreate()} and unbind +during {@link android.app.Activity#onDestroy onDestroy()}. Beware that this implies that your +activity needs to use the service the entire time it's running (even in the background), so if +the service is in another process, then you increase the weight of the process and it becomes +more likely that the system will kill it.</li> + </ul> + <p class="note"><strong>Note:</strong> You should usually <strong>not</strong> bind and unbind +during your activity's {@link android.app.Activity#onResume onResume()} and {@link +android.app.Activity#onPause onPause()}, because these callbacks occur at every lifecycle transition +and you should keep the processing that occurs at these transitions to a minimum. Also, if +multiple activities in your application bind to the same service and there is a transition between +two of those activities, the service may be destroyed and recreated as the current activity unbinds +(during pause) before the next one binds (during resume). (This activity transition for how +activities coordinate their lifecycles is described in the <a +href="{@docRoot}guide/topics/fundamentals/activities.html#CoordinatingActivities">Activities</a> +document.)</p> +</ul> + +<p>For more sample code, showing how to bind to a service, see the <a +href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code +RemoteService.java}</a> class in <a +href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a>.</p> + + + + + +<h2 id="Lifecycle">Managing the Lifecycle of a Bound Service</h2> + +<div class="figure" style="width:588px"> +<img src="{@docRoot}images/fundamentals/service_binding_tree_lifecycle.png" alt="" /> +<p class="img-caption"><strong>Figure 1.</strong> The lifecycle for a service that is started +and also allows binding.</p> +</div> + +<p>When a service is unbound from all clients, the Android system destroys it (unless it was also +started with {@link android.app.Service#onStartCommand onStartCommand()}). As such, you don't have +to manage the lifecycle of your service if it's purely a bound +service—the Android system manages it for you based on whether it is bound to any clients.</p> + +<p>However, if you choose to implement the {@link android.app.Service#onStartCommand +onStartCommand()} callback method, then you must explicitly stop the service, because the +service is now considered to be <em>started</em>. In this case, the service runs until the service +stops itself with {@link android.app.Service#stopSelf()} or another component calls {@link +android.content.Context#stopService stopService()}, regardless of whether it is bound to any +clients.</p> + +<p>Additionally, if your service is started and accepts binding, then when the system calls +your {@link android.app.Service#onUnbind onUnbind()} method, you can optionally return +{@code true} if you would like to receive a call to {@link android.app.Service#onRebind +onRebind()} the next time a client binds to the service (instead of receiving a call to {@link +android.app.Service#onBind onBind()}). {@link android.app.Service#onRebind +onRebind()} returns void, but the client still receives the {@link android.os.IBinder} in its +{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} callback. +Below, figure 1 illustrates the logic for this kind of lifecycle.</p> + +<p>For more information about the lifecycle of an started service, see the <a +href="{@docRoot}guide/topics/fundamentals/services.html#Lifecycle">Services</a> document.</p> + + + + diff --git a/docs/html/guide/topics/fundamentals/services.jd b/docs/html/guide/topics/fundamentals/services.jd new file mode 100644 index 000000000000..df1eacee5029 --- /dev/null +++ b/docs/html/guide/topics/fundamentals/services.jd @@ -0,0 +1,860 @@ +page.title=Services +parent.title=Application Fundamentals +parent.link=index.html +@jd:body + +<div id="qv-wrapper"> +<ol id="qv"> +<h2>Quickview</h2> +<ul> + <li>A service can run in the background to perform work even while the user is in a different +application</li> + <li>A service can allow other components to bind to it, in order to interact with it and +perform interprocess communication</li> + <li>A service runs in the main thread of the application that hosts it, by default</li> +</ul> +<h2>In this document</h2> +<ol> +<li><a href="#Basics">The Basics</a></li> +<ol> + <li><a href="#Declaring">Declaring a service in the manifest</a></li> +</ol> +<li><a href="#CreatingAService">Creating a Started Service</a> + <ol> + <li><a href="#ExtendingIntentService">Extending the IntentService class</a></li> + <li><a href="#ExtendingService">Extending the Service class</a></li> + <li><a href="#StartingAService">Starting a service</a></li> + <li><a href="#Stopping">Stopping a service</a></li> + </ol> +</li> +<li><a href="#CreatingBoundService">Creating a Bound Service</a></li> +<li><a href="#Notifications">Sending Notifications to the User</a></li> +<li><a href="#Foreground">Running a Service in the Foreground</a></li> +<li><a href="#Lifecycle">Managing the Lifecycle of a Service</a> +<ol> + <li><a href="#LifecycleCallbacks">Implementing the lifecycle callbacks</a></li> +</ol> +</li> +</ol> + +<h2>Key classes</h2> +<ol> + <li>{@link android.app.Service}</li> + <li>{@link android.app.IntentService}</li> +</ol> + +<h2>Samples</h2> +<ol> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArguments.html">{@code + ServiceStartArguments}</a></li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code + LocalService}</a></li> +</ol> + +<h2>See also</h2> +<ol> +<li><a href="{@docRoot}guide/topics/fundamentals/bound-services.html">Bound Services</a></li> +</ol> + +</div> + + +<p>A {@link android.app.Service} is an application component that can perform +long-running operations in the background and does not provide a user interface. Another +application component can start a service and it will continue to run in the background even if the +user switches to another application. Additionally, a component can bind to a service to +interact with it and even perform interprocess communication (IPC). For example, a service might +handle network transactions, play music, perform file I/O, or interact with a content provider, all +from the background.</p> + +<p>A service can essentially take two forms:</p> + +<dl> + <dt>Started</dt> + <dd>A service is "started" when an application component (such as an activity) starts it by +calling {@link android.content.Context#startService startService()}. Once started, a service +can run in the background indefinitely, even if the component that started it is destroyed. Usually, +a started service performs a single operation and does not return a result to the caller. +For example, it might download or upload a file over the network. When the operation is done, the +service should stop itself.</dd> + <dt>Bound</dt> + <dd>A service is "bound" when an application component binds to it by calling {@link +android.content.Context#bindService bindService()}. A bound service offers a client-server +interface that allows components to interact with the service, send requests, get results, and even +do so across processes with interprocess communication (IPC). A bound service runs only as long as +another application component is bound to it. Multiple components can bind to the service at once, +but when all of them unbind, the service is destroyed.</dd> +</dl> + +<p>Although this documentation generally discusses these two types of services separately, your +service can work both ways—it can be started (to run indefinitely) and also allow binding. +It's simply a matter of whether you implement a couple callback methods: {@link +android.app.Service#onStartCommand onStartCommand()} to allow components to start it and {@link +android.app.Service#onBind onBind()} to allow binding.</p> + +<p>Regardless of whether your application is started, bound, or both, any application component +can use the service (even from a separate application), in the same way that any component can use +an activity—by starting it with an {@link android.content.Intent}. However, you can declare +the service as private, in the manifest file, and block access from other applications. This is +discussed more in the section about <a href="#Declaring">Declaring the service in the +manifest</a>.</p> + +<p class="caution"><strong>Caution:</strong> A service runs in the +main thread of its hosting process—the service does <strong>not</strong> create its own thread +and does <strong>not</strong> run in a separate process (unless you specify otherwise). This means +that, if your service is going to do any CPU intensive work or blocking operations (such as MP3 +playback or networking), you should create a new thread within the service to do that work. By using +a separate thread, you will reduce the risk of Application Not Responding (ANR) errors and the +application's main thread can remain dedicated to user interaction with your activities.</p> + + +<h2 id="Basics">The Basics</h2> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>Should you use a service or a thread?</h3> + <p>A service is simply a component that can run in the background even when the user is not +interacting with your application. Thus, you should create a service only if that is what you +need.</p> + <p>If you need to perform work outside your main thread, but only while the user is interacting +with your application, then you should probably instead create a new thread and not a service. For +example, if you want to play some music, but only while your activity is running, you might create +a thread in {@link android.app.Activity#onCreate onCreate()}, start running it in {@link +android.app.Activity#onStart onStart()}, then stop it in {@link android.app.Activity#onStop +onStop()}. Also consider using {@link android.os.AsyncTask} or {@link android.os.HandlerThread}, +instead of the traditional {@link java.lang.Thread} class. See the <a +href="{@docRoot}guide/topics/fundamentals/processes-and-threading.html#Threads">Processes and +Threading</a> document for more information about threads.</p> + <p>Remember that if you do use a service, it still runs in your application's main thread by +default, so you should still create a new thread within the service if it performs intensive or +blocking operations.</p> +</div> +</div> + +<p>To create a service, you must create a subclass of {@link android.app.Service} (or one +of its existing subclasses). In your implementation, you need to override some callback methods that +handle key aspects of the service lifecycle and provide a mechanism for components to bind to +the service, if appropriate. The most important callback methods you should override are:</p> + +<dl> + <dt>{@link android.app.Service#onStartCommand onStartCommand()}</dt> + <dd>The system calls this method when another component, such as an activity, +requests that the service be started, by calling {@link android.content.Context#startService +startService()}. Once this method executes, the service is started and can run in the +background indefinitely. If you implement this, it is your responsibility to stop the service when +its work is done, by calling {@link android.app.Service#stopSelf stopSelf()} or {@link +android.content.Context#stopService stopService()}. (If you only want to provide binding, you don't +need to implement this method.)</dd> + <dt>{@link android.app.Service#onBind onBind()}</dt> + <dd>The system calls this method when another component wants to bind with the +service (such as to perform RPC), by calling {@link android.content.Context#bindService +bindService()}. In your implementation of this method, you must provide an interface that clients +use to communicate with the service, by returning an {@link android.os.IBinder}. You must always +implement this method, but if you don't want to allow binding, then you should return null.</dd> + <dt>{@link android.app.Service#onCreate()}</dt> + <dd>The system calls this method when the service is first created, to perform one-time setup +procedures (before it calls either {@link android.app.Service#onStartCommand onStartCommand()} or +{@link android.app.Service#onBind onBind()}). If the service is already running, this method is not +called.</dd> + <dt>{@link android.app.Service#onDestroy()}</dt> + <dd>The system calls this method when the service is no longer used and is being destroyed. +Your service should implement this to clean up any resources such as threads, registered +listeners, receivers, etc. This is the last call the service receives.</dd> +</dl> + +<p>If a component starts the service by calling {@link +android.content.Context#startService startService()} (which results in a call to {@link +android.app.Service#onStartCommand onStartCommand()}), then the service +remains running until it stops itself with {@link android.app.Service#stopSelf()} or another +component stops it by calling {@link android.content.Context#stopService stopService()}.</p> + +<p>If a component calls +{@link android.content.Context#bindService bindService()} to create the service (and {@link +android.app.Service#onStartCommand onStartCommand()} is <em>not</em> called), then the service runs +only as long as the component is bound to it. Once the service is unbound from all clients, the +system destroys it.</p> + +<p>The Android system will force-stop a service only when memory is low and it must recover system +resources for the activity that has user focus. If the service is bound to an activity that has user +focus, then it's less likely to be killed, and if the service is declared to <a +href="#Foreground">run in the foreground</a> (discussed later), then it will almost never be killed. +Otherwise, if the service was started and is long-running, then the system will lower its position +in the list of background tasks over time and the service will become highly susceptible to +killing—if your service is started, then you must design it to gracefully handle restarts +by the system. If the system kills your service, it restarts it as soon as resources become +available again (though this also depends on the value you return from {@link +android.app.Service#onStartCommand onStartCommand()}, as discussed later). For more information +about when the system might destroy a service, see the <a +href="{@docRoot}guide/topics/fundamentals/processes-and-threading.html">Processes and Threading</a> +document.</p> + +<p>In the following sections, you'll see how you can create each type of service and how to use +it from other application components.</p> + + + +<h3 id="Declaring">Declaring a service in the manifest</h3> + +<p>Like activities (and other components), you must declare all services in your application's +manifest file.</p> + +<p>To decalare your service, add a <a +href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> element +as a child of the <a +href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a> +element. For example:</p> + +<pre> +<manifest ... > + ... + <application ... > + <service android:name=".ExampleService" /> + ... + </application> +</manifest> +</pre> + +<p>There are other attributes you can include in the <a +href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> element to +define properties such as permissions required to start the service and the process in +which the service should run. See the <a +href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> element +reference for more information.</p> + +<p>Just like an activity, a service can define intent filters that allow other components to +invoke the service using implicit intents. By declaring intent filters, components +from any application installed on the user's device can potentially start your service if your +service declares an intent filter that matches the intent another application passes to {@link +android.content.Context#startService startService()}.</p> + +<p>If you plan on using your service only locally (other applications do not use it), then you +don't need to (and should not) supply any intent filters. Without any intent filters, you must +start the service using an intent that explicitly names the service class. More information +about <a href="#StartingAService">starting a service</a> is discussed below.</p> + +<p>Additionally, you can ensure that your service is private to your application only if +you include the <a +href="{@docRoot}guide/topics/manifest/service-element.html#exported">{@code android:exported}</a> +attribute and set it to {@code "false"}. This is effective even if your service supplies intent +filters.</p> + +<p>For more information about creating intent filters for your service, see the <a +href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a> +document.</p> + + + +<h2 id="CreatingStartedService">Creating a Started Service</h2> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h2>Targeting Android 1.6 or lower</h2> + <p>If you're building an application for Android 1.6 or lower, you need +to implement {@link android.app.Service#onStart onStart()}, instead of {@link +android.app.Service#onStartCommand onStartCommand()} (in Android 2.0, +{@link android.app.Service#onStart onStart()} was deprecated in favor of {@link +android.app.Service#onStartCommand onStartCommand()}).</p> + <p>For more information about providing compatibility with versions of Android older than 2.0, see +the {@link android.app.Service#onStartCommand onStartCommand()} documentation.</p> +</div> +</div> + +<p>A started service is one that another component starts by calling {@link +android.content.Context#startService startService()}, resulting in a call to the service's +{@link android.app.Service#onStartCommand onStartCommand()} method.</p> + +<p>When a service is started, it has a lifecycle that's independent of the +component that started it and the service can run in the background indefinitely, even if +the component that started it is destroyed. As such, the service should stop itself when its job +is done by calling {@link android.app.Service#stopSelf stopSelf()}, or another component can stop it +by calling {@link android.content.Context#stopService stopService()}.</p> + +<p>An application component such as an activity can start the service by calling {@link +android.content.Context#startService startService()} and passing an {@link android.content.Intent} +that specifies the service and includes any data for the service to use. The service receives +this {@link android.content.Intent} in the {@link android.app.Service#onStartCommand +onStartCommand()} method.</p> + +<p>For instance, suppose an activity needs to save some data to an online database. The activity can +start a companion service and deliver it the data to save by passing an intent to {@link +android.content.Context#startService startService()}. The service receives the intent in {@link +android.app.Service#onStartCommand onStartCommand()}, connects to the Internet and performs the +database transaction. When the transaction is done, the service stops itself and it is +destroyed.</p> + +<p class="caution"><strong>Caution:</strong> A services runs in the same process as the application +in which it is declared and in the main thread of that application, by default. So, if your service +performs intensive or blocking operations while the user interacts with an activity from the same +application, the service will slow down activity performance. To avoid impacting application +performance, you should start a new thread inside the service.</p> + +<p>Traditionally, there are two classes you can extend to create a started service:</p> +<dl> + <dt>{@link android.app.Service}</dt> + <dd>This is the base class for all services. When you extend this class, it's important that +you create a new thread in which to do all the service's work, because the service uses your +application's main thread, by default, which could slow the performance of any activity your +application is running.</dd> + <dt>{@link android.app.IntentService}</dt> + <dd>This is a subclass of {@link android.app.Service} that uses a worker thread to handle all +start requests, one at a time. This is the best option if you don't require that your service +handle multiple requests simultaneously. All you need to do is implement {@link +android.app.IntentService#onHandleIntent onHandleIntent()}, which receives the intent for each +start request so you can do the background work.</dd> +</dl> + +<p>The following sections describe how you can implement your service using either one for these +classes.</p> + + +<h3 id="ExtendingIntentService">Extending the IntentService class</h3> + +<p>Because most started services don't need to handle multiple requests simultaneously +(which can actually be a dangerous multi-threading scenario), it's probably best if you +implement your service using the {@link android.app.IntentService} class.</p> + +<p>The {@link android.app.IntentService} does the following:</p> + +<ul> + <li>Creates a default worker thread that executes all intents delivered to {@link +android.app.Service#onStartCommand onStartCommand()} separate from your application's main +thread.</li> + <li>Creates a work queue that passes one intent at a time to your {@link +android.app.IntentService#onHandleIntent onHandleIntent()} implementation, so you never have to +worry about multi-threading.</li> + <li>Stops the service after all start requests have been handled, so you never have to call +{@link android.app.Service#stopSelf}.</li> + <li>Provides default implementation of {@link android.app.IntentService#onBind onBind()} that +returns null.</li> + <li>Provides a default implementation of {@link android.app.IntentService#onStartCommand +onStartCommand()} that sends the intent to the work queue and then to your {@link +android.app.IntentService#onHandleIntent onHandleIntent()} implementation.</li> +</ul> + +<p>All this adds up to the fact that all you need to do is implement {@link +android.app.IntentService#onHandleIntent onHandleIntent()} to do the work provided by the +client. (Though, you also need to provide a small constructor for the service.)</p> + +<p>Here's an example implementation of {@link android.app.IntentService}:</p> + +<pre> +public class HelloIntentService extends IntentService { + + /** + * A constructor is required, and must call the super {@link android.app.IntentService#IntentService} + * constructor with a name for the worker thread. + */ + public HelloIntentService() { + super("HelloIntentService"); + } + + /** + * The IntentService calls this method from the default worker thread with + * the intent that started the service. When this method returns, IntentService + * stops the service, as appropriate. + */ + @Override + protected void onHandleIntent(Intent intent) { + // Normally we would do some work here, like download a file. + // For our sample, we just sleep for 5 seconds. + long endTime = System.currentTimeMillis() + 5*1000; + while (System.currentTimeMillis() < endTime) { + synchronized (this) { + try { + wait(endTime - System.currentTimeMillis()); + } catch (Exception e) { + } + } + } + } +} +</pre> + +<p>That's all you need: a constructor and an implementation of {@link +android.app.IntentService#onHandleIntent onHandleIntent()}.</p> + +<p>If you decide to also override other callback methods, such as {@link +android.app.IntentService#onCreate onCreate()}, {@link +android.app.IntentService#onStartCommand onStartCommand()}, or {@link +android.app.IntentService#onDestroy onDestroy()}, be sure to call the super implementation, so +that the {@link android.app.IntentService} can properly handle the life of the worker thread.</p> + +<p>For example, {@link android.app.IntentService#onStartCommand onStartCommand()} must return +the default implementation (which is how the intent gets delivered to {@link +android.app.IntentService#onHandleIntent onHandleIntent()}):</p> + +<pre> +@Override +public int onStartCommand(Intent intent, int flags, int startId) { + Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); + return super.onStartCommand(intent,flags,startId); +} +</pre> + +<p>Besides {@link android.app.IntentService#onHandleIntent onHandleIntent()}, the only method +from which you don't need to call the super class is {@link android.app.IntentService#onBind +onBind()} (but you only need to implement that if your service allows binding).</p> + +<p>In the next section, you'll see how the same kind of service is implemented when extending +the base {@link android.app.Service} class, which is a lot more code, but which might be +appropriate if you need to handle simultaneous start requests.</p> + + +<h3 id="ExtendingService">Extending the Service class</h3> + +<p>As you saw in the previous section, using {@link android.app.IntentService} makes your +implementation of a started service very simple. If, however, you require your service to +perform multi-threading (instead of processing start requests through a work queue), then you +can extend the {@link android.app.Service} class to handle each intent.</p> + +<p>For comparison, the following example code is an implementation of the {@link +android.app.Service} class that performs the exact same work as the example above using {@link +android.app.IntentService}. That is, for each start request, it uses a worker thread to perform the +job and processes only one request at a time.</p> + +<pre> +public class HelloService extends Service { + private Looper mServiceLooper; + private ServiceHandler mServiceHandler; + + // Handler that receives messages from the thread + private final class ServiceHandler extends Handler { + public ServiceHandler(Looper looper) { + super(looper); + } + @Override + public void handleMessage(Message msg) { + // Normally we would do some work here, like download a file. + // For our sample, we just sleep for 5 seconds. + long endTime = System.currentTimeMillis() + 5*1000; + while (System.currentTimeMillis() < endTime) { + synchronized (this) { + try { + wait(endTime - System.currentTimeMillis()); + } catch (Exception e) { + } + } + } + // Stop the service using the startId, so that we don't stop + // the service in the middle of handling another job + stopSelf(msg.arg1); + } + } + + @Override + public void onCreate() { + // Start up the thread running the service. Note that we create a + // separate thread because the service normally runs in the process's + // main thread, which we don't want to block. We also make it + // background priority so CPU-intensive work will not disrupt our UI. + HandlerThread thread = new HandlerThread("ServiceStartArguments", + Process.THREAD_PRIORITY_BACKGROUND); + thread.start(); + + // Get the HandlerThread's Looper and use it for our Handler + mServiceLooper = thread.getLooper(); + mServiceHandler = new ServiceHandler(mServiceLooper); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); + + // For each start request, send a message to start a job and deliver the + // start ID so we know which request we're stopping when we finish the job + Message msg = mServiceHandler.obtainMessage(); + msg.arg1 = startId; + mServiceHandler.sendMessage(msg); + + // If we get killed, after returning from here, restart + return START_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + // We don't provide binding, so return null + return null; + } + + @Override + public void onDestroy() { + Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); + } +} +</pre> + +<p>As you can see, it's a lot more work than using {@link android.app.IntentService}.</p> + +<p>However, because you handle each call to {@link android.app.Service#onStartCommand +onStartCommand()} yourself, you can perform multiple requests simultaneously. That's not what +this example does, but if that's what you want, then you can create a new thread for each +request and run them right away (instead of waiting for the previous request to finish).</p> + +<p>Notice that the {@link android.app.Service#onStartCommand onStartCommand()} method must return an +integer. The integer is a value that describes how the system should continue the service in the +event that the system kills it (as discussed above, the default implementation for {@link +android.app.IntentService} handles this for you, though you are able to modify it). The return value +from {@link android.app.Service#onStartCommand onStartCommand()} must be one of the following +constants:</p> + +<dl> + <dt>{@link android.app.Service#START_NOT_STICKY}</dt> + <dd>If the system kills the service after {@link android.app.Service#onStartCommand +onStartCommand()} returns, <em>do not</em> recreate the service, unless there are pending +intents to deliver. This is the safest option to avoid running your service when not necessary +and when your application can simply restart any unfinished jobs.</dd> + <dt>{@link android.app.Service#START_STICKY}</dt> + <dd>If the system kills the service after {@link android.app.Service#onStartCommand +onStartCommand()} returns, recreate the service and call {@link +android.app.Service#onStartCommand onStartCommand()}, but <em>do not</em> redeliver the last intent. +Instead, the system calls {@link android.app.Service#onStartCommand onStartCommand()} with a +null intent, unless there were pending intents to start the service, in which case, +those intents are delivered. This is suitable for media players (or similar services) that are not +executing commands, but running indefinitely and waiting for a job.</dd> + <dt>{@link android.app.Service#START_REDELIVER_INTENT}</dt> + <dd>If the system kills the service after {@link android.app.Service#onStartCommand +onStartCommand()} returns, recreate the service and call {@link +android.app.Service#onStartCommand onStartCommand()} with the last intent that was delivered to the +service. Any pending intents are delivered in turn. This is suitable for services that are +actively performing a job that should be immediately resumed, such as downloading a file.</dd> +</dl> +<p>For more details about these return values, see the linked reference documentation for each +constant.</p> + + + +<h3 id="StartingAService">Starting a Service</h3> + +<p>You can start a service from an activity or other application component by passing an +{@link android.content.Intent} (specifying the service to start) to {@link +android.content.Context#startService startService()}. The Android system calls the service's {@link +android.app.Service#onStartCommand onStartCommand()} method and passes it the {@link +android.content.Intent}. (You should never call {@link android.app.Service#onStartCommand +onStartCommand()} directly.)</p> + +<p>For example, an activity can start the example service in the previous section ({@code +HelloSevice}) using an explicit intent with {@link android.content.Context#startService +startService()}:</p> + +<pre> +Intent intent = new Intent(this, HelloService.class); +startService(intent); +</pre> + +<p>The {@link android.content.Context#startService startService()} method returns immediately and +the Android system calls the service's {@link android.app.Service#onStartCommand +onStartCommand()} method. If the service is not already running, the system first calls {@link +android.app.Service#onCreate onCreate()}, then calls {@link android.app.Service#onStartCommand +onStartCommand()}.</p> + +<p>If the service does not also provide binding, the intent delivered with {@link +android.content.Context#startService startService()} is the only mode of communication between the +application component and the service. However, if you want the service to send a result back, then +the client that starts the service can create a {@link android.app.PendingIntent} for a broadcast +(with {@link android.app.PendingIntent#getBroadcast getBroadcast()}) and deliver it to the service +in the {@link android.content.Intent} that starts the service. The service can then use the +broadcast to deliver a result.</p> + +<p>Multiple requests to start the service result in multiple corresponding calls to the service's +{@link android.app.Service#onStartCommand onStartCommand()}. However, only one request to stop +the service (with {@link android.app.Service#stopSelf stopSelf()} or {@link +android.content.Context#stopService stopService()}) is required to stop it.</p> + + +<h3 id="Stopping">Stopping a service</h3> + +<p>A started service must manage its own lifecycle. That is, the system does not stop or +destroy the service unless it must recover system memory and the service +continues to run after {@link android.app.Service#onStartCommand onStartCommand()} returns. So, +the service must stop itself by calling {@link android.app.Service#stopSelf stopSelf()} or another +component can stop it by calling {@link android.content.Context#stopService stopService()}.</p> + +<p>Once requested to stop with {@link android.app.Service#stopSelf stopSelf()} or {@link +android.content.Context#stopService stopService()}, the system destroys the service as soon as +possible.</p> + +<p>However, if your service handles multiple requests to {@link +android.app.Service#onStartCommand onStartCommand()} concurrently, then you shouldn't stop the +service when you're done processing a start request, because you might have since received a new +start request (stopping at the end of the first request would terminate the second one). To avoid +this problem, you can use {@link android.app.Service#stopSelf(int)} to ensure that your request to +stop the service is always based on the most recent start request. That is, when you call {@link +android.app.Service#stopSelf(int)}, you pass the ID of the start request (the <code>startId</code> +delivered to {@link android.app.Service#onStartCommand onStartCommand()}) to which your stop request +corresponds. Then if the service received a new start request before you were able to call {@link +android.app.Service#stopSelf(int)}, then the ID will not match and the service will not stop.</p> + +<p class="caution"><strong>Caution:</strong> It's important that your application stops its services +when it's done working, to avoid wasting system resources and consuming battery power. If necessary, +other components can stop the service by calling {@link +android.content.Context#stopService stopService()}. Even if you enable binding for the service, +you must always stop the service yourself if it ever received a call to {@link +android.app.Service#onStartCommand onStartCommand()}.</p> + +<p>For more information about the lifecycle of a service, see the section below about <a +href="#Lifecycle">Managing the Lifecycle of a Service</a>.</p> + + + +<h2 id="CreatingBoundService">Creating a Bound Service</h2> + +<p>A bound service is one that allows application components to bind to it by calling {@link +android.content.Context#bindService bindService()} in order to create a long-standing connection +(and generally does not allow components to <em>start</em> it by calling {@link +android.content.Context#startService startService()}).</p> + +<p>You should create a bound service when you want to interact with the service from activities +and other components in your application or to expose some of your application's functionality to +other applications, through interprocess communication (IPC).</p> + +<p>To create a bound service, you must implement the {@link +android.app.Service#onBind onBind()} callback method to return an {@link android.os.IBinder} that +defines the interface for communication with the service. Other application components can then call +{@link android.content.Context#bindService bindService()} to retrieve the interface and +begin calling methods on the service. The service lives only to serve the application component that +is bound to it, so when there are no components bound to the service, the system destroys it +(you do <em>not</em> need to stop a bound service in the way you must when the service is started +through {@link android.app.Service#onStartCommand onStartCommand()}).</p> + +<p>To create a bound service, the first thing you must do is define the interface that specifies +how a client can communicate with the service. This interface between the service +and a client must be an implementation of {@link android.os.IBinder} and is what your service must +return from the {@link android.app.Service#onBind +onBind()} callback method. Once the client receives the {@link android.os.IBinder}, it can begin +interacting with the service through that interface.</p> + +<p>Multiple clients can bind to the service at once. When a client is done interacting with the +service, it calls {@link android.content.Context#unbindService unbindService()} to unbind. Once +there are no clients bound to the service, the system destroys the service.</p> + +<p>There are multiple ways to implement a bound service and the implementation is more +complicated than a started service, so the bound service discussion appears in a separate +document about <a +href="{@docRoot}guide/topics/fundamentals/bound-services.html">Bound Services</a>.</p> + + + +<h2 id="Notifications">Sending Notifications to the User</h2> + +<p>Once running, a service can notify the user of events using <a +href="{@docRoot}guide/topics/ui/notifiers/toasts.html">Toast Notifications</a> or <a +href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>.</p> + +<p>A toast notification is a message that appears on the surface of the current window for a +moment then disappears, while a status bar notification provides an icon in the status bar with a +message, which the user can select in order to take an action (such as start an activity).</p> + +<p>Usually, a status bar notification is the best technique when some background work has completed +(such as a file completed +downloading) and the user can now act on it. When the user selects the notification from the +expanded view, the notification can start an activity (such as to view the downloaded file).</p> + +<p>See the <a +href="{@docRoot}guide/topics/ui/notifiers/toasts.html">Toast Notifications</a> or <a +href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a> +developer guides for more information.</p> + + + +<h2 id="Foreground">Running a Service in the Foreground</h2> + +<p>A foreground service is a service that's considered to be something the +user is actively aware of and thus not a candidate for the system to kill when low on memory. A +foreground service must provide a notification for the status bar, which is placed under the +"Ongoing" heading, which means that the notification cannot be dismissed unless the service is +either stopped or removed from the foreground.</p> + +<p>For example, a music player that plays music from a service should be set to run in the +foreground, because the user it explicitly aware +of its operation. The notification in the status bar might indicate the current song and allow +the user to launch an activity to interact with the music player.</p> + +<p>To request that your service run in the foreground, call {@link +android.app.Service#startForeground startForeground()}. This method takes two parameters: an integer +that uniquely identifies the notification and the {@link +android.app.Notification} for the status bar. For example:</p> + +<pre> +Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text), + System.currentTimeMillis()); +Intent notificationIntent = new Intent(this, ExampleActivity.class); +PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); +notification.setLatestEventInfo(this, getText(R.string.notification_title), + getText(R.string.notification_message), pendingIntent); +startForeground(ONGOING_NOTIFICATION, notification); +</pre> + + +<p>To remove the service from the foreground, call {@link +android.app.Service#stopForeground stopForeground()}. This method takes a boolean, indicating +whether to remove the status bar notification as well. This method does <em>not</em> stop the +service. However, if you stop the service while it's still running in the foreground, then the +notification is also removed.</p> + +<p class="note"><strong>Note:</strong> The methods {@link +android.app.Service#startForeground startForeground()} and {@link +android.app.Service#stopForeground stopForeground()} were introduced in Android 2.0 (API Level +5). In order to run your service in the foreground on older versions of the platform, you must +use the previous {@code setForeground()} method—see the {@link +android.app.Service#startForeground startForeground()} documentation for information about how +to provide backward compatibility.</p> + +<p>For more information about notifications, see <a +href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Creating Status Bar +Notifications</a>.</p> + + + +<h2 id="Lifecycle">Managing the Lifecycle of a Service</h2> + +<p>The lifecycle of a service is much simpler than that of an activity. However, it's even more important +that you pay close attention to how your service is created and destroyed, because a service +can run in the background without the user being aware.</p> + +<p>The service lifecycle—from when it's created to when it's destroyed—can follow two +different paths:</p> + +<ul> +<li>A started service + <p>The service is created when another component calls {@link +android.content.Context#startService startService()}. The service then runs indefinitely and must +stop itself by calling {@link +android.app.Service#stopSelf() stopSelf()}. Another component can also stop the +service by calling {@link android.content.Context#stopService +stopService()}. When the service is stopped, the system destroys it..</p></li> + +<li>A bound service + <p>The service is created when another component (a client) calls {@link +android.content.Context#bindService bindService()}. The client then communicates with the service +through an {@link android.os.IBinder} interface. The client can close the connection by calling +{@link android.content.Context#unbindService unbindService()}. Multiple clients can bind to +the same service and when all of them unbind, the system destroys the service. (The service +does <em>not</em> need to stop itself.)</p></li> +</ul> + +<p>These two paths are not entirely separate. That is, you can bind to a service that was already +started with {@link android.content.Context#startService startService()}. For example, a background +music service could be started by calling {@link android.content.Context#startService +startService()} with an {@link android.content.Intent} that identifies the music to play. Later, +possibly when the user wants to exercise some control over the player or get information about the +current song, an activity can bind to the service by calling {@link +android.content.Context#bindService bindService()}. In cases like this, {@link +android.content.Context#stopService stopService()} or {@link android.app.Service#stopSelf +stopSelf()} does not actually stop the service until all clients unbind. </p> + + +<h3 id="LifecycleCallbacks">Implementing the lifecycle callbacks</h3> + +<p>Like an activity, a service has lifecycle callback methods that you can implement to monitor +changes in the service's state and perform work at the appropriate times. The following skeleton +service demonstrates each of the lifecycle methods:</p> + + +<div class="figure" style="width:432px"> +<img src="{@docRoot}images/service_lifecycle.png" alt="" /> +<p class="img-caption"><strong>Figure 2.</strong> The service lifecycle. The diagram on the left +shows the lifecycle when the service is created with {@link android.content.Context#startService +startService()} and the diagram on the right shows the lifecycle when the service is created +with {@link android.content.Context#bindService bindService()}.</p> +</div> + +<pre> +public class ExampleService extends Service { + int mStartMode; // indicates how to behave if the service is killed + IBinder mBinder; // interface for clients that bind + boolean mAllowRebind; // indicates whether onRebind should be used + + @Override + public void {@link android.app.Service#onCreate onCreate}() { + // The service is being created + } + @Override + public int {@link android.app.Service#onStartCommand onStartCommand}(Intent intent, int flags, int startId) { + // The service is starting, due to a call to {@link android.content.Context#startService startService()} + return <em>mStartMode</em>; + } + @Override + public IBinder {@link android.app.Service#onBind onBind}(Intent intent) { + // A client is binding to the service with {@link android.content.Context#bindService bindService()} + return <em>mBinder</em>; + } + @Override + public boolean {@link android.app.Service#onUnbind onUnbind}(Intent intent) { + // All clients have unbound with {@link android.content.Context#unbindService unbindService()} + return <em>mAllowRebind</em>; + } + @Override + public void {@link android.app.Service#onRebind onRebind}(Intent intent) { + // A client is binding to the service with {@link android.content.Context#bindService bindService()}, + // after onUnbind() has already been called + } + @Override + public void {@link android.app.Service#onDestroy onDestroy}() { + // The service is no longer used and is being destroyed + } +} +</pre> + +<p class="note"><strong>Note:</strong> Unlike the activity lifecycle callback methods, you are +<em>not</em> required to call the superclass implementation of these callback methods.</p> + +<p>By implementing these methods, you can monitor two nested loops of the service's lifecycle: </p> + +<ul> +<li>The <strong>entire lifetime</strong> of a service happens between the time {@link +android.app.Service#onCreate onCreate()} is called and the time {@link +android.app.Service#onDestroy} returns. Like an activity, a service does its initial setup in +{@link android.app.Service#onCreate onCreate()} and releases all remaining resources in {@link +android.app.Service#onDestroy onDestroy()}. For example, a +music playback service could create the thread where the music will be played in {@link +android.app.Service#onCreate onCreate()}, then stop the thread in {@link +android.app.Service#onDestroy onDestroy()}. + +<p>The {@link android.app.Service#onCreate onCreate()} and {@link android.app.Service#onDestroy +onDestroy()} methods are called for all services, whether +they're created by {@link android.content.Context#startService startService()} or {@link +android.content.Context#bindService bindService()}.</p></li> + +<li>The <strong>active lifetime</strong> of a service begins with a call to either {@link +android.app.Service#onStartCommand onStartCommand()} or {@link android.app.Service#onBind onBind()}. +Each method is handed the {@link +android.content.Intent} that was passed to either {@link android.content.Context#startService +startService()} or {@link android.content.Context#bindService bindService()}, respectively. +<p>If the service is started, the active lifetime ends the same time that the entire lifetime +ends (the service is still active even after {@link android.app.Service#onStartCommand +onStartCommand()} returns). If the service is bound, the active lifetime ends when {@link +android.app.Service#onUnbind onUnbind()} returns.</p> +</li> +</ul> + +<p class="note"><strong>Note:</strong> Although a started service is stopped by a call to +either {@link android.app.Service#stopSelf stopSelf()} or {@link +android.content.Context#stopService stopService()}, there is not a respective callback for the +service (there's no {@code onStop()} callback). So, unless the service is bound to a client, +the system destroys it when the service is stopped—{@link +android.app.Service#onDestroy onDestroy()} is the only callback received.</p> + +<p>Figure 2 illustrates the typical callback methods for a service. Although the figure separates +services that are created by {@link android.content.Context#startService startService()} from those +created by {@link android.content.Context#bindService bindService()}, keep +in mind that any service, no matter how it's started, can potentially allow clients to bind to it. +So, a service that was initially started with {@link android.app.Service#onStartCommand +onStartCommand()} (by a client calling {@link android.content.Context#startService startService()}) +can still receive a call to {@link android.app.Service#onBind onBind()} (when a client calls +{@link android.content.Context#bindService bindService()}).</p> + +<p>For more information about creating a service that provides binding, see the <a +href="{@docRoot}guide/topics/fundamentals/bound-services.html">Bound Services</a> document, +which includes more information about the {@link android.app.Service#onRebind onRebind()} +callback method in the section about <a +href="{@docRoot}guide/topics/fundamentals/bound-services.html#Lifecycle">Managing the Lifecycle of +a Bound Service</a>.</p> + + + +<h2>Beginner's Path</h2> + +<p>To learn how to query data from the system or other applications (such as contacts or media +stored on the device), continue with the <b><a +href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a></b> +document.</p> diff --git a/docs/html/images/fundamentals/service_binding_tree_lifecycle.png b/docs/html/images/fundamentals/service_binding_tree_lifecycle.png Binary files differnew file mode 100644 index 000000000000..46d2df50e85a --- /dev/null +++ b/docs/html/images/fundamentals/service_binding_tree_lifecycle.png diff --git a/docs/html/images/service_lifecycle.png b/docs/html/images/service_lifecycle.png Binary files differindex 0748db280183..f9602f84a93a 100644 --- a/docs/html/images/service_lifecycle.png +++ b/docs/html/images/service_lifecycle.png diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java index 39a7dd2c492e..94a848800906 100644 --- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java @@ -207,7 +207,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac @Override public ConstantState getConstantState() { if (mState.canConstantState()) { - mState.mChangingConfigurations = super.getChangingConfigurations(); + mState.mChangingConfigurations = getChangingConfigurations(); return mState; } return null; diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index 032244f35c83..2c09ddca2b9c 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -439,7 +439,7 @@ public class BitmapDrawable extends Drawable { @Override public final ConstantState getConstantState() { - mBitmapState.mChangingConfigurations = super.getChangingConfigurations(); + mBitmapState.mChangingConfigurations = getChangingConfigurations(); return mBitmapState; } diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java index 0d4459141f09..b333e0165143 100644 --- a/graphics/java/android/graphics/drawable/ClipDrawable.java +++ b/graphics/java/android/graphics/drawable/ClipDrawable.java @@ -232,7 +232,7 @@ public class ClipDrawable extends Drawable implements Drawable.Callback { @Override public ConstantState getConstantState() { if (mClipState.canConstantState()) { - mClipState.mChangingConfigurations = super.getChangingConfigurations(); + mClipState.mChangingConfigurations = getChangingConfigurations(); return mClipState; } return null; diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java index 4d560bea3f03..4418e02a89f5 100644 --- a/graphics/java/android/graphics/drawable/ColorDrawable.java +++ b/graphics/java/android/graphics/drawable/ColorDrawable.java @@ -149,7 +149,7 @@ public class ColorDrawable extends Drawable { @Override public ConstantState getConstantState() { - mState.mChangingConfigurations = super.getChangingConfigurations(); + mState.mChangingConfigurations = getChangingConfigurations(); return mState; } diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index 0a580eb5f41c..a9414e800e69 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -389,7 +389,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { @Override public ConstantState getConstantState() { if (mDrawableContainerState.canConstantState()) { - mDrawableContainerState.mChangingConfigurations = super.getChangingConfigurations(); + mDrawableContainerState.mChangingConfigurations = getChangingConfigurations(); return mDrawableContainerState; } return null; diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index da8bb1b1a80b..65c6ccfa25d7 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -861,7 +861,7 @@ public class GradientDrawable extends Drawable { @Override public ConstantState getConstantState() { - mGradientState.mChangingConfigurations = super.getChangingConfigurations(); + mGradientState.mChangingConfigurations = getChangingConfigurations(); return mGradientState; } diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java index 3a74dfdbfd60..231234cf4ee7 100644 --- a/graphics/java/android/graphics/drawable/InsetDrawable.java +++ b/graphics/java/android/graphics/drawable/InsetDrawable.java @@ -241,7 +241,7 @@ public class InsetDrawable extends Drawable implements Drawable.Callback @Override public ConstantState getConstantState() { if (mInsetState.canConstantState()) { - mInsetState.mChangingConfigurations = super.getChangingConfigurations(); + mInsetState.mChangingConfigurations = getChangingConfigurations(); return mInsetState; } return null; diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java index 84da1706ad5e..49dbbca5a6f3 100644 --- a/graphics/java/android/graphics/drawable/LayerDrawable.java +++ b/graphics/java/android/graphics/drawable/LayerDrawable.java @@ -557,7 +557,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { @Override public ConstantState getConstantState() { if (mLayerState.canConstantState()) { - mLayerState.mChangingConfigurations = super.getChangingConfigurations(); + mLayerState.mChangingConfigurations = getChangingConfigurations(); return mLayerState; } return null; diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index 35b831928de5..a5175c5526a3 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -323,7 +323,7 @@ public class NinePatchDrawable extends Drawable { @Override public ConstantState getConstantState() { - mNinePatchState.mChangingConfigurations = super.getChangingConfigurations(); + mNinePatchState.mChangingConfigurations = getChangingConfigurations(); return mNinePatchState; } diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java index 212ddfac629d..4f74b374cbdb 100644 --- a/graphics/java/android/graphics/drawable/RotateDrawable.java +++ b/graphics/java/android/graphics/drawable/RotateDrawable.java @@ -192,7 +192,7 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { @Override public ConstantState getConstantState() { if (mState.canConstantState()) { - mState.mChangingConfigurations = super.getChangingConfigurations(); + mState.mChangingConfigurations = getChangingConfigurations(); return mState; } return null; diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java index 055576dc89b9..a7ed0d0a14b5 100644 --- a/graphics/java/android/graphics/drawable/ScaleDrawable.java +++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java @@ -241,7 +241,7 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { @Override public ConstantState getConstantState() { if (mScaleState.canConstantState()) { - mScaleState.mChangingConfigurations = super.getChangingConfigurations(); + mScaleState.mChangingConfigurations = getChangingConfigurations(); return mScaleState; } return null; diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java index 92252fc06001..cb8774ddf874 100644 --- a/graphics/java/android/graphics/drawable/ShapeDrawable.java +++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java @@ -356,7 +356,7 @@ public class ShapeDrawable extends Drawable { @Override public ConstantState getConstantState() { - mShapeState.mChangingConfigurations = super.getChangingConfigurations(); + mShapeState.mChangingConfigurations = getChangingConfigurations(); return mShapeState; } diff --git a/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java index fdd138ca72b9..323225f6f5a4 100644 --- a/graphics/java/android/renderscript/Program.java +++ b/graphics/java/android/renderscript/Program.java @@ -33,10 +33,10 @@ import android.util.Log; * **/ public class Program extends BaseObj { - public static final int MAX_INPUT = 8; - public static final int MAX_OUTPUT = 8; - public static final int MAX_CONSTANT = 8; - public static final int MAX_TEXTURE = 8; + static final int MAX_INPUT = 8; + static final int MAX_OUTPUT = 8; + static final int MAX_CONSTANT = 8; + static final int MAX_TEXTURE = 8; /** * diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 75b16713c761..e3593daeee07 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -128,10 +128,10 @@ DisplayList::~DisplayList() { } mBitmapResources.clear(); - for (size_t i = 0; i < mShaderResources.size(); i++) { - caches.resourceCache.decrementRefcount(mShaderResources.itemAt(i)); + for (size_t i = 0; i < mShaders.size(); i++) { + caches.resourceCache.decrementRefcount(mShaders.itemAt(i)); } - mShaderResources.clear(); + mShaders.clear(); for (size_t i = 0; i < mPaints.size(); i++) { delete mPaints.itemAt(i); @@ -179,11 +179,11 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde caches.resourceCache.incrementRefcount(resource); } - const Vector<SkiaShader*> &shaderResources = recorder.getShaderResources(); - for (size_t i = 0; i < shaderResources.size(); i++) { - SkiaShader* resource = shaderResources.itemAt(i); - mShaderResources.add(resource); - caches.resourceCache.incrementRefcount(resource); + const Vector<SkiaShader*> &shaders = recorder.getShaders(); + for (size_t i = 0; i < shaders.size(); i++) { + SkiaShader* shader = shaders.itemAt(i); + mShaders.add(shader); + caches.resourceCache.incrementRefcount(shader); } const Vector<SkPaint*> &paints = recorder.getPaints(); @@ -407,11 +407,11 @@ void DisplayListRenderer::reset() { } mBitmapResources.clear(); - for (size_t i = 0; i < mShaderResources.size(); i++) { - SkiaShader* resource = mShaderResources.itemAt(i); - caches.resourceCache.decrementRefcount(resource); + for (size_t i = 0; i < mShaders.size(); i++) { + caches.resourceCache.decrementRefcount(mShaders.itemAt(i)); } - mShaderResources.clear(); + mShaders.clear(); + mShaderMap.clear(); mPaints.clear(); mPaintMap.clear(); diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index cc5230942e30..71523349f0d3 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -206,11 +206,11 @@ private: PathHeap* mPathHeap; Vector<SkBitmap*> mBitmapResources; - Vector<SkiaShader*> mShaderResources; Vector<SkiaColorFilter*> mFilterResources; Vector<SkPaint*> mPaints; Vector<SkMatrix*> mMatrices; + Vector<SkiaShader*> mShaders; mutable SkFlattenableReadBuffer mReader; @@ -291,8 +291,8 @@ public: return mBitmapResources; } - const Vector<SkiaShader*>& getShaderResources() const { - return mShaderResources; + const Vector<SkiaShader*>& getShaders() const { + return mShaders; } const Vector<SkPaint*>& getPaints() const { @@ -366,12 +366,12 @@ private: } inline void addPaint(SkPaint* paint) { - if (paint == NULL) { + if (!paint) { addInt((int) NULL); return; } - SkPaint *paintCopy = mPaintMap.valueFor(paint); + SkPaint* paintCopy = mPaintMap.valueFor(paint); if (paintCopy == NULL || paintCopy->getGenerationID() != paint->getGenerationID()) { paintCopy = new SkPaint(*paint); mPaintMap.add(paint, paintCopy); @@ -406,10 +406,21 @@ private: } inline void addShader(SkiaShader* shader) { - addInt((int) shader); - mShaderResources.add(shader); - Caches& caches = Caches::getInstance(); - caches.resourceCache.incrementRefcount(shader); + if (!shader) { + addInt((int) NULL); + return; + } + + SkiaShader* shaderCopy = mShaderMap.valueFor(shader); + // TODO: We also need to handle generation ID changes in compose shaders + if (shaderCopy == NULL || shaderCopy->getGenerationId() != shader->getGenerationId()) { + shaderCopy = shader->copy(); + mShaderMap.add(shader, shaderCopy); + mShaders.add(shaderCopy); + Caches::getInstance().resourceCache.incrementRefcount(shaderCopy); + } + + addInt((int) shaderCopy); } inline void addColorFilter(SkiaColorFilter* colorFilter) { @@ -422,11 +433,14 @@ private: SkChunkAlloc mHeap; Vector<SkBitmap*> mBitmapResources; - Vector<SkiaShader*> mShaderResources; Vector<SkiaColorFilter*> mFilterResources; Vector<SkPaint*> mPaints; DefaultKeyedVector<SkPaint*, SkPaint*> mPaintMap; + + Vector<SkiaShader*> mShaders; + DefaultKeyedVector<SkiaShader*, SkiaShader*> mShaderMap; + Vector<SkMatrix*> mMatrices; PathHeap* mPathHeap; diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index 00de39b3068b..70d117a3622b 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -67,12 +67,12 @@ void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) { void ResourceCache::incrementRefcount(SkiaShader* shaderResource) { shaderResource->getSkShader()->safeRef(); - incrementRefcount((void*)shaderResource, kShader); + incrementRefcount((void*) shaderResource, kShader); } void ResourceCache::incrementRefcount(SkiaColorFilter* filterResource) { filterResource->getSkColorFilter()->safeRef(); - incrementRefcount((void*)filterResource, kColorFilter); + incrementRefcount((void*) filterResource, kColorFilter); } void ResourceCache::decrementRefcount(void* resource) { @@ -91,17 +91,17 @@ void ResourceCache::decrementRefcount(void* resource) { void ResourceCache::decrementRefcount(SkBitmap* bitmapResource) { bitmapResource->pixelRef()->safeUnref(); bitmapResource->getColorTable()->safeUnref(); - decrementRefcount((void*)bitmapResource); + decrementRefcount((void*) bitmapResource); } void ResourceCache::decrementRefcount(SkiaShader* shaderResource) { shaderResource->getSkShader()->safeUnref(); - decrementRefcount((void*)shaderResource); + decrementRefcount((void*) shaderResource); } void ResourceCache::decrementRefcount(SkiaColorFilter* filterResource) { filterResource->getSkColorFilter()->safeUnref(); - decrementRefcount((void*)filterResource); + decrementRefcount((void*) filterResource); } void ResourceCache::recycle(SkBitmap* resource) { diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 590a9d7c41b8..8878c709809e 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -47,10 +47,22 @@ static const GLint gTileModes[] = { // Base shader /////////////////////////////////////////////////////////////////////////////// +void SkiaShader::copyFrom(const SkiaShader& shader) { + mType = shader.mType; + mKey = shader.mKey; + mTileX = shader.mTileX; + mTileY = shader.mTileY; + mBlend = shader.mBlend; + mUnitMatrix = shader.mUnitMatrix; + mShaderMatrix = shader.mShaderMatrix; + mGenerationId = shader.mGenerationId; +} + SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX, SkShader::TileMode tileY, SkMatrix* matrix, bool blend): mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend) { setMatrix(matrix); + mGenerationId = 0; } SkiaShader::~SkiaShader() { @@ -90,6 +102,13 @@ SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::Ti updateLocalMatrix(matrix); } +SkiaShader* SkiaBitmapShader::copy() { + SkiaBitmapShader* copy = new SkiaBitmapShader(); + copy->copyFrom(*this); + copy->mBitmap = mBitmap; + return copy; +} + void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) { Texture* texture = mTextureCache->get(mBitmap); if (!texture) return; @@ -183,6 +202,19 @@ SkiaLinearGradientShader::~SkiaLinearGradientShader() { delete[] mPositions; } +SkiaShader* SkiaLinearGradientShader::copy() { + SkiaLinearGradientShader* copy = new SkiaLinearGradientShader(); + copy->copyFrom(*this); + copy->mBounds = new float[4]; + memcpy(copy->mBounds, mBounds, sizeof(float) * 4); + copy->mColors = new uint32_t[mCount]; + memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount); + copy->mPositions = new float[mCount]; + memcpy(copy->mPositions, mPositions, sizeof(float) * mCount); + copy->mCount = mCount; + return copy; +} + void SkiaLinearGradientShader::describe(ProgramDescription& description, const Extensions& extensions) { description.hasGradient = true; @@ -238,6 +270,17 @@ SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float r updateLocalMatrix(matrix); } +SkiaShader* SkiaCircularGradientShader::copy() { + SkiaCircularGradientShader* copy = new SkiaCircularGradientShader(); + copy->copyFrom(*this); + copy->mColors = new uint32_t[mCount]; + memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount); + copy->mPositions = new float[mCount]; + memcpy(copy->mPositions, mPositions, sizeof(float) * mCount); + copy->mCount = mCount; + return copy; +} + void SkiaCircularGradientShader::describe(ProgramDescription& description, const Extensions& extensions) { description.hasGradient = true; @@ -276,6 +319,17 @@ SkiaSweepGradientShader::~SkiaSweepGradientShader() { delete[] mPositions; } +SkiaShader* SkiaSweepGradientShader::copy() { + SkiaSweepGradientShader* copy = new SkiaSweepGradientShader(); + copy->copyFrom(*this); + copy->mColors = new uint32_t[mCount]; + memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount); + copy->mPositions = new float[mCount]; + memcpy(copy->mPositions, mPositions, sizeof(float) * mCount); + copy->mCount = mCount; + return copy; +} + void SkiaSweepGradientShader::describe(ProgramDescription& description, const Extensions& extensions) { description.hasGradient = true; @@ -315,7 +369,25 @@ void SkiaSweepGradientShader::updateTransforms(Program* program, const mat4& mod SkiaComposeShader::SkiaComposeShader(SkiaShader* first, SkiaShader* second, SkXfermode::Mode mode, SkShader* key): SkiaShader(kCompose, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, - NULL, first->blend() || second->blend()), mFirst(first), mSecond(second), mMode(mode) { + NULL, first->blend() || second->blend()), + mFirst(first), mSecond(second), mMode(mode), mCleanup(false) { +} + +SkiaComposeShader::~SkiaComposeShader() { + if (mCleanup) { + delete mFirst; + delete mSecond; + } +} + +SkiaShader* SkiaComposeShader::copy() { + SkiaComposeShader* copy = new SkiaComposeShader(); + copy->copyFrom(*this); + copy->mFirst = mFirst->copy(); + copy->mSecond = mSecond->copy(); + copy->mMode = mMode; + copy->cleanup(); + return copy; } void SkiaComposeShader::set(TextureCache* textureCache, GradientCache* gradientCache) { diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h index 6702129ad297..89dd131f8ab2 100644 --- a/libs/hwui/SkiaShader.h +++ b/libs/hwui/SkiaShader.h @@ -56,6 +56,9 @@ struct SkiaShader { SkMatrix* matrix, bool blend); virtual ~SkiaShader(); + virtual SkiaShader* copy() = 0; + void copyFrom(const SkiaShader& shader); + virtual void describe(ProgramDescription& description, const Extensions& extensions); virtual void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, GLuint* textureUnit); @@ -81,8 +84,13 @@ struct SkiaShader { const Snapshot& snapshot) { } + uint32_t getGenerationId() { + return mGenerationId; + } + void setMatrix(SkMatrix* matrix) { updateLocalMatrix(matrix); + mGenerationId++; } void updateLocalMatrix(const SkMatrix* matrix) { @@ -97,6 +105,9 @@ struct SkiaShader { void computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView); protected: + SkiaShader() { + } + /** * The appropriate texture unit must have been activated prior to invoking * this method. @@ -114,6 +125,9 @@ protected: mat4 mUnitMatrix; mat4 mShaderMatrix; + +private: + uint32_t mGenerationId; }; // struct SkiaShader @@ -127,6 +141,7 @@ protected: struct SkiaBitmapShader: public SkiaShader { SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX, SkShader::TileMode tileY, SkMatrix* matrix, bool blend); + SkiaShader* copy(); void describe(ProgramDescription& description, const Extensions& extensions); void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, @@ -134,6 +149,9 @@ struct SkiaBitmapShader: public SkiaShader { void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot); private: + SkiaBitmapShader() { + } + /** * This method does not work for n == 0. */ @@ -154,6 +172,7 @@ struct SkiaLinearGradientShader: public SkiaShader { SkiaLinearGradientShader(float* bounds, uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend); ~SkiaLinearGradientShader(); + SkiaShader* copy(); void describe(ProgramDescription& description, const Extensions& extensions); void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, @@ -161,6 +180,9 @@ struct SkiaLinearGradientShader: public SkiaShader { void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot); private: + SkiaLinearGradientShader() { + } + float* mBounds; uint32_t* mColors; float* mPositions; @@ -174,6 +196,7 @@ struct SkiaSweepGradientShader: public SkiaShader { SkiaSweepGradientShader(float x, float y, uint32_t* colors, float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend); ~SkiaSweepGradientShader(); + SkiaShader* copy(); virtual void describe(ProgramDescription& description, const Extensions& extensions); void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, @@ -183,6 +206,8 @@ struct SkiaSweepGradientShader: public SkiaShader { protected: SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend); + SkiaSweepGradientShader() { + } uint32_t* mColors; float* mPositions; @@ -195,8 +220,13 @@ protected: struct SkiaCircularGradientShader: public SkiaSweepGradientShader { SkiaCircularGradientShader(float x, float y, float radius, uint32_t* colors, float* positions, int count, SkShader* key,SkShader::TileMode tileMode, SkMatrix* matrix, bool blend); + SkiaShader* copy(); void describe(ProgramDescription& description, const Extensions& extensions); + +private: + SkiaCircularGradientShader() { + } }; // struct SkiaCircularGradientShader /** @@ -204,6 +234,8 @@ struct SkiaCircularGradientShader: public SkiaSweepGradientShader { */ struct SkiaComposeShader: public SkiaShader { SkiaComposeShader(SkiaShader* first, SkiaShader* second, SkXfermode::Mode mode, SkShader* key); + ~SkiaComposeShader(); + SkiaShader* copy(); void set(TextureCache* textureCache, GradientCache* gradientCache); @@ -212,9 +244,18 @@ struct SkiaComposeShader: public SkiaShader { GLuint* textureUnit); private: + SkiaComposeShader(): mCleanup(false) { + } + + void cleanup() { + mCleanup = true; + } + SkiaShader* mFirst; SkiaShader* mSecond; SkXfermode::Mode mMode; + + bool mCleanup; }; // struct SkiaComposeShader }; // namespace uirenderer diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_ime_default.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_ime_default.png Binary files differindex 499244bb681c..056e7e7225a0 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_ime_default.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_ime_default.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_0.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_0.png Binary files differindex b16e436e8d60..20ea73576e64 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_0.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_0.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_1.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_1.png Binary files differindex 1b6ed7434e3e..a5e9c7c4c9ba 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_1.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_1.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_1_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_1_fully.png Binary files differindex 43e35d37fd96..0287d5a4089b 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_1_fully.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_1_fully.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_2.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_2.png Binary files differindex 498adbb6df95..4dfbcb29c555 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_2.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_2.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_2_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_2_fully.png Binary files differindex b7e42a00b054..b9d4cb02a1a3 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_2_fully.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_2_fully.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_3.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_3.png Binary files differindex 959fc5c4804b..4c8ec0e1badf 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_3.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_3.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_3_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_3_fully.png Binary files differindex f905979c4b9a..7e30894a5bbc 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_3_fully.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_3_fully.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_4.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_4.png Binary files differindex 6e8e73c3212a..72635a51cc0c 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_4.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_4.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_4_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_4_fully.png Binary files differindex b5799c8db67c..6f56886f0fcb 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_4_fully.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_4_fully.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_flightmode.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_flightmode.png Binary files differindex 7682bba8136f..118a6b4c4786 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_flightmode.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_flightmode.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_0.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_0.png Binary files differindex 98e874aad97a..ecd880fc236f 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_0.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_0.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png Binary files differindex 16268957db3e..7251b141bc69 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png Binary files differindex 3c2e2b97eab9..01135c862ed0 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_1_fully.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_2.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_2.png Binary files differindex 77e6ee40ae5e..794d9eddef27 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_2.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_2.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_2_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_2_fully.png Binary files differindex 00d86bfd2f38..851ca41e62a3 100755 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_2_fully.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_2_fully.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_3.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_3.png Binary files differindex c2574e1486af..f16783c971dc 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_3.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_3.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_3_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_3_fully.png Binary files differindex 70c030bd3d88..d217d0904ca2 100755 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_3_fully.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_3_fully.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_4.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_4.png Binary files differindex 55caecff88a0..b577ebe4fb59 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_4.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_4.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_4_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_4_fully.png Binary files differindex b5326d257961..6cf482926a5b 100755 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_4_fully.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_signal_4_fully.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png Binary files differindex 25fffd654040..4bcd2be5cb86 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png Binary files differindex b6e4ebc0e519..dadb0cd3704f 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png Binary files differindex 189a089c4545..51d7cc245d70 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_pressed.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png Binary files differindex ebade9217623..83a8b261bdb2 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png Binary files differindex 6ef71c7c60ed..cfeba3e5e7f0 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png Binary files differindex f6b0a17e61d8..b090b9570f02 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_default.png Binary files differindex 259db7aa7329..e1d53bdfb106 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png Binary files differindex 246c6feeb179..1d97e0581c59 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png Binary files differindex 34515c702d26..c9724fc9535c 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png Binary files differindex 9216030a8454..39025f03bb2f 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png Binary files differindex e529f6f98303..a1f7057ad9e5 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png Binary files differindex e529f6f98303..7cc9f027678e 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1x.png Binary files differindex 02c27ee44582..25ca1246983c 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1x.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1x.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png Binary files differindex 57558ad14382..122a47308bb6 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png Binary files differindex 57558ad14382..4b126dcb523f 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png Binary files differindex e4425b2b1eac..828c0e23a269 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png Binary files differindex e4425b2b1eac..f52d8f9c39b6 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3g.png Binary files differindex 84ac92760d18..2725ed23b875 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3g.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3g.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png Binary files differindex 09de6b093a81..61501c16546c 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png Binary files differindex 09de6b093a81..1171ffc754b6 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_edge.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_edge.png Binary files differindex 13cae40b84fa..83cb82035159 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_edge.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_edge.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png Binary files differindex 1534da3c1fd6..98c3b10aaf0a 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_gprs.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_gprs.png Binary files differindex d0a4fd01a788..9c3c44c3d0b2 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_gprs.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_gprs.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_hsdpa.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_hsdpa.png Binary files differindex 05976bd06c79..08b975a2edb9 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_hsdpa.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_hsdpa.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_roam.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_roam.png Binary files differindex 2cc3cd642c06..ae9dc4044d8c 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_roam.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_roam.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png Binary files differindex 1c59b2add7ab..94c41a75fbe5 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png Binary files differindex 32e916587dc1..ac32aa619ff8 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png Binary files differindex 32e916587dc1..9845c46ebeba 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png Binary files differindex ea71298a2c27..34494e30c782 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png Binary files differindex ea71298a2c27..77f4a7bd1cf9 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png Binary files differindex 869a497dae71..a635c3c9db9a 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png Binary files differindex 869a497dae71..c3e924084127 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png Binary files differindex 1711c8252fa9..68ffdc94b913 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png Binary files differindex 1711c8252fa9..93a3dc0830a0 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_notification_row.xml b/packages/SystemUI/res/layout-xlarge/status_bar_notification_row.xml index 6b12d29c3fe1..4991a406efac 100644 --- a/packages/SystemUI/res/layout-xlarge/status_bar_notification_row.xml +++ b/packages/SystemUI/res/layout-xlarge/status_bar_notification_row.xml @@ -27,7 +27,7 @@ <!-- TODO: scaleType should be top-left but ImageView doesn't support that. --> <com.android.systemui.statusbar.LatestItemView android:id="@+id/content" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="64sp" android:layout_alignParentTop="true" android:layout_toRightOf="@id/large_icon" diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java index 1f06dcc4c6f2..5e33f0535c49 100644 --- a/policy/src/com/android/internal/policy/impl/GlobalActions.java +++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java @@ -221,8 +221,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac final AlertDialog.Builder ab = new AlertDialog.Builder(mContext); ab.setAdapter(mAdapter, this) - .setInverseBackgroundForced(true) - .setTitle(R.string.global_actions); + .setInverseBackgroundForced(true); final AlertDialog dialog = ab.create(); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); @@ -249,6 +248,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac } else { mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); } + mDialog.setTitle(R.string.global_actions); } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index f2c2b93f30c4..2a393227745c 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -2564,7 +2564,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { int sState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_S); int dpadState = mWindowManager.getDPadKeycodeState(KeyEvent.KEYCODE_DPAD_CENTER); int trackballState = mWindowManager.getTrackballScancodeState(BTN_MOUSE); - mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0; + int volumeDownState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_VOLUME_DOWN); + mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0 + || volumeDownState > 0; performHapticFeedbackLw(null, mSafeMode ? HapticFeedbackConstants.SAFE_MODE_ENABLED : HapticFeedbackConstants.SAFE_MODE_DISABLED, true); diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java index 6dca52348039..06595ae503cb 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/InputManager.java @@ -423,7 +423,7 @@ public class InputManager { @SuppressWarnings("unused") public void notifyConfigurationChanged(long whenNanos) { - mWindowManagerService.sendNewConfiguration(); + mWindowManagerService.mInputMonitor.notifyConfigurationChanged(); } @SuppressWarnings("unused") diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 9c80b516fd7e..2b98795f3776 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -490,7 +490,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub statusBar.setIconVisibility("ime", false); // mSettings should be created before buildInputMethodListLocked - mSettings = new InputMethodSettings(context.getContentResolver(), mMethodMap, mMethodList); + mSettings = new InputMethodSettings( + mRes, context.getContentResolver(), mMethodMap, mMethodList); buildInputMethodListLocked(mMethodList, mMethodMap); mSettings.enableAllIMEsIfThereIsNoEnabledIME(); @@ -575,7 +576,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (!allowsImplicitlySelectedSubtypes || enabledSubtypes.size() > 0) { return enabledSubtypes; } else { - return getApplicableSubtypesLocked(imi.getSubtypes()); + return getApplicableSubtypesLocked(mRes, imi.getSubtypes()); } } } @@ -1680,7 +1681,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub ArrayList<String> subtypes = immis.get(i).second; if (subtypes != null && subtypes.size() == 0) { ArrayList<InputMethodSubtype> applicableSubtypes = - getApplicableSubtypesLocked(imi.getSubtypes()); + getApplicableSubtypesLocked(mRes, imi.getSubtypes()); final int numSubtypes = applicableSubtypes.size(); for (int j = 0; j < numSubtypes; ++j) { subtypes.add(String.valueOf(applicableSubtypes.get(j).hashCode())); @@ -1977,9 +1978,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return NOT_A_SUBTYPE_ID; } - private ArrayList<InputMethodSubtype> getApplicableSubtypesLocked( - List<InputMethodSubtype> subtypes) { - final String systemLocale = mRes.getConfiguration().locale.toString(); + private static ArrayList<InputMethodSubtype> getApplicableSubtypesLocked( + Resources res, List<InputMethodSubtype> subtypes) { + final String systemLocale = res.getConfiguration().locale.toString(); if (TextUtils.isEmpty(systemLocale)) return new ArrayList<InputMethodSubtype>(); HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = new HashMap<String, InputMethodSubtype>(); @@ -2013,7 +2014,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub applicableModeAndSubtypesMap.values()); if (!containsKeyboardSubtype) { InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked( - subtypes, SUBTYPE_MODE_KEYBOARD, systemLocale, true); + res, subtypes, SUBTYPE_MODE_KEYBOARD, systemLocale, true); if (lastResortKeyboardSubtype != null) { applicableSubtypes.add(lastResortKeyboardSubtype); } @@ -2031,14 +2032,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub * it will return the first subtype matched with mode * @return the most applicable subtypeId */ - private InputMethodSubtype findLastResortApplicableSubtypeLocked( - List<InputMethodSubtype> subtypes, String mode, String locale, + private static InputMethodSubtype findLastResortApplicableSubtypeLocked( + Resources res, List<InputMethodSubtype> subtypes, String mode, String locale, boolean canIgnoreLocaleAsLastResort) { if (subtypes == null || subtypes.size() == 0) { return null; } if (TextUtils.isEmpty(locale)) { - locale = mRes.getConfiguration().locale.toString(); + locale = res.getConfiguration().locale.toString(); } final String language = locale.substring(0, 2); boolean partialMatchFound = false; @@ -2095,29 +2096,29 @@ public class InputMethodManagerService extends IInputMethodManager.Stub continue; } InputMethodSubtype subtype = null; - final List<InputMethodSubtype> explicitlyEnabledSubtypes = - mSettings.getEnabledInputMethodSubtypeListLocked(imi); - // 1. Search by the current subtype's locale from explicitlyEnabledSubtypes. + final List<InputMethodSubtype> enabledSubtypes = + getEnabledInputMethodSubtypeList(imi, true); + // 1. Search by the current subtype's locale from enabledSubtypes. if (mCurrentSubtype != null) { subtype = findLastResortApplicableSubtypeLocked( - explicitlyEnabledSubtypes, mode, mCurrentSubtype.getLocale(), false); + mRes, enabledSubtypes, mode, mCurrentSubtype.getLocale(), false); } - // 2. Search by the system locale from explicitlyEnabledSubtypes. - // 3. Search the first enabled subtype matched with mode from explicitlyEnabledSubtypes. + // 2. Search by the system locale from enabledSubtypes. + // 3. Search the first enabled subtype matched with mode from enabledSubtypes. if (subtype == null) { subtype = findLastResortApplicableSubtypeLocked( - explicitlyEnabledSubtypes, mode, null, true); + mRes, enabledSubtypes, mode, null, true); } // 4. Search by the current subtype's locale from all subtypes. if (subtype == null && mCurrentSubtype != null) { subtype = findLastResortApplicableSubtypeLocked( - imi.getSubtypes(), mode, mCurrentSubtype.getLocale(), false); + mRes, imi.getSubtypes(), mode, mCurrentSubtype.getLocale(), false); } // 5. Search by the system locale from all subtypes. // 6. Search the first enabled subtype matched with mode from all subtypes. if (subtype == null) { subtype = findLastResortApplicableSubtypeLocked( - imi.getSubtypes(), mode, null, true); + mRes, imi.getSubtypes(), mode, null, true); } if (subtype != null) { if (imiId.equals(mCurMethodId)) { @@ -2177,8 +2178,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // the most applicable subtype from all subtypes whose mode is // SUBTYPE_MODE_KEYBOARD. This is an exceptional case, so we will hardcode // the mode. - mCurrentSubtype = findLastResortApplicableSubtypeLocked(imi.getSubtypes(), - SUBTYPE_MODE_KEYBOARD, null, true); + mCurrentSubtype = findLastResortApplicableSubtypeLocked( + mRes, imi.getSubtypes(), SUBTYPE_MODE_KEYBOARD, null, true); } } else { mCurrentSubtype = @@ -2256,6 +2257,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private final TextUtils.SimpleStringSplitter mSubtypeSplitter = new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATER); + private final Resources mRes; private final ContentResolver mResolver; private final HashMap<String, InputMethodInfo> mMethodMap; private final ArrayList<InputMethodInfo> mMethodList; @@ -2275,8 +2277,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } public InputMethodSettings( - ContentResolver resolver, HashMap<String, InputMethodInfo> methodMap, - ArrayList<InputMethodInfo> methodList) { + Resources res, ContentResolver resolver, + HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList) { + mRes = res; mResolver = resolver; mMethodMap = methodMap; mMethodList = methodList; @@ -2516,8 +2519,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // If imeId is empty, returns the first IME and subtype in the history if (TextUtils.isEmpty(imeId) || imeInTheHistory.equals(imeId)) { final String subtypeInTheHistory = imeAndSubtype.second; - final String subtypeHashCode = getEnabledSubtypeForInputMethodAndSubtypeLocked( - enabledImes, imeInTheHistory, subtypeInTheHistory); + final String subtypeHashCode = + getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked( + enabledImes, imeInTheHistory, subtypeInTheHistory); if (!TextUtils.isEmpty(subtypeHashCode)) { if (DEBUG) { Slog.d(TAG, "Enabled subtype found in the history:" + subtypeHashCode); @@ -2532,14 +2536,36 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return null; } - private String getEnabledSubtypeForInputMethodAndSubtypeLocked(List<Pair<String, + private String getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(List<Pair<String, ArrayList<String>>> enabledImes, String imeId, String subtypeHashCode) { for (Pair<String, ArrayList<String>> enabledIme: enabledImes) { if (enabledIme.first.equals(imeId)) { - for (String s: enabledIme.second) { - if (s.equals(subtypeHashCode)) { - // If both imeId and subtypeId are enabled, return subtypeId. - return s; + final ArrayList<String> enabledSubtypes = enabledIme.second; + if (enabledSubtypes.size() == 0) { + // If there are no enabled subtypes, applicable subtypes are enabled + // implicitly. + InputMethodInfo ime = mMethodMap.get(imeId); + // If IME is enabled and no subtypes are enabled, applicable subtypes + // are enabled implicitly, so needs to treat them to be enabled. + if (ime != null && ime.getSubtypes().size() > 0) { + List<InputMethodSubtype> implicitlySelectedSubtypes = + getApplicableSubtypesLocked(mRes, ime.getSubtypes()); + if (implicitlySelectedSubtypes != null) { + final int N = implicitlySelectedSubtypes.size(); + for (int i = 0; i < N; ++i) { + final InputMethodSubtype st = implicitlySelectedSubtypes.get(i); + if (String.valueOf(st.hashCode()).equals(subtypeHashCode)) { + return subtypeHashCode; + } + } + } + } + } else { + for (String s: enabledSubtypes) { + if (s.equals(subtypeHashCode)) { + // If both imeId and subtypeId are enabled, return subtypeId. + return s; + } } } // If imeId was enabled but subtypeId was disabled. diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 95534e356110..6f4b4c5be403 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -481,14 +481,11 @@ class ServerThread extends Thread { // we are in safe mode. final boolean safeMode = wm.detectSafeMode(); if (safeMode) { - try { - ActivityManagerNative.getDefault().enterSafeMode(); - // Post the safe mode state in the Zygote class - Zygote.systemInSafeMode = true; - // Disable the JIT for the system_server process - VMRuntime.getRuntime().disableJitCompilation(); - } catch (RemoteException e) { - } + ActivityManagerService.self().enterSafeMode(); + // Post the safe mode state in the Zygote class + Zygote.systemInSafeMode = true; + // Disable the JIT for the system_server process + VMRuntime.getRuntime().disableJitCompilation(); } else { // Enable the JIT for the system_server process VMRuntime.getRuntime().startJitCompilation(); @@ -506,6 +503,10 @@ class ServerThread extends Thread { wm.systemReady(); + if (safeMode) { + ActivityManagerService.self().showSafeModeOverlay(); + } + // Update the configuration for this context by hand, because we're going // to start using it before the config change done in wm.systemReady() will // propagate to it. diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 8d9cb31c013b..bdc779c3e11e 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -212,6 +212,10 @@ public class WindowManagerService extends IWindowManager.Stub // Maximum number of milliseconds to wait for input event injection. // FIXME is this value reasonable? private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000; + + // Maximum number of milliseconds to wait for input devices to be enumerated before + // proceding with safe mode detection. + private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000; // Default input dispatching timeout in nanoseconds. private static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L; @@ -5812,6 +5816,11 @@ public class WindowManagerService extends IWindowManager.Stub // Temporary input application object to provide to the input dispatcher. private InputApplication mTempInputApplication = new InputApplication(); + // Set to true when the first input device configuration change notification + // is received to indicate that the input devices are ready. + private final Object mInputDevicesReadyMonitor = new Object(); + private boolean mInputDevicesReady; + /* Notifies the window manager about a broken input channel. * * Called by the InputManager. @@ -6007,7 +6016,32 @@ public class WindowManagerService extends IWindowManager.Stub // Also avoids keeping InputChannel objects referenced unnecessarily. mTempInputWindows.clear(); } - + + /* Notifies that the input device configuration has changed. */ + public void notifyConfigurationChanged() { + sendNewConfiguration(); + + synchronized (mInputDevicesReadyMonitor) { + if (!mInputDevicesReady) { + mInputDevicesReady = true; + mInputDevicesReadyMonitor.notifyAll(); + } + } + } + + /* Waits until the built-in input devices have been configured. */ + public boolean waitForInputDevicesReady(long timeoutMillis) { + synchronized (mInputDevicesReadyMonitor) { + if (!mInputDevicesReady) { + try { + mInputDevicesReadyMonitor.wait(timeoutMillis); + } catch (InterruptedException ex) { + } + } + return mInputDevicesReady; + } + } + /* Notifies that the lid switch changed state. */ public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen); @@ -6329,6 +6363,13 @@ public class WindowManagerService extends IWindowManager.Stub } public boolean detectSafeMode() { + if (!mInputMonitor.waitForInputDevicesReady( + INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) { + Slog.w(TAG, "Devices still not ready after waiting " + + INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS + + " milliseconds before attempting to detect safe mode."); + } + mSafeMode = mPolicy.detectSafeMode(); return mSafeMode; } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 17ef88d96f44..dbf9a96a8739 100755 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -6075,23 +6075,25 @@ public final class ActivityManagerService extends ActivityManagerNative AppGlobals.getPackageManager().enterSafeMode(); } catch (RemoteException e) { } - - View v = LayoutInflater.from(mContext).inflate( - com.android.internal.R.layout.safe_mode, null); - WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); - lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; - lp.width = WindowManager.LayoutParams.WRAP_CONTENT; - lp.height = WindowManager.LayoutParams.WRAP_CONTENT; - lp.gravity = Gravity.BOTTOM | Gravity.LEFT; - lp.format = v.getBackground().getOpacity(); - lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; - ((WindowManager)mContext.getSystemService( - Context.WINDOW_SERVICE)).addView(v, lp); } } } + public final void showSafeModeOverlay() { + View v = LayoutInflater.from(mContext).inflate( + com.android.internal.R.layout.safe_mode, null); + WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); + lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; + lp.width = WindowManager.LayoutParams.WRAP_CONTENT; + lp.height = WindowManager.LayoutParams.WRAP_CONTENT; + lp.gravity = Gravity.BOTTOM | Gravity.LEFT; + lp.format = v.getBackground().getOpacity(); + lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + ((WindowManager)mContext.getSystemService( + Context.WINDOW_SERVICE)).addView(v, lp); + } + public void noteWakeupAlarm(IIntentSender sender) { if (!(sender instanceof PendingIntentRecord)) { return; diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java index 9ccb3eed7892..a3466e27e597 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java @@ -75,6 +75,7 @@ public class TestShellActivity extends Activity implements LayoutTestController public void handleMessage(Message msg) { if (msg.what == MSG_TIMEOUT) { mTimedOut = true; + mWebView.stopLoading(); if (mCallback != null) mCallback.timedOut(mWebView.getUrl()); if (!mRequestedWebKitData) { diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java index 7020d9cbfcea..db3cf44d89c8 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java @@ -353,7 +353,7 @@ public class BitmapFactory { If it happened on close, bm is still valid. */ Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ, - String.format("Error decoding bitmap of id 0x%x", id), e); + String.format("Error decoding bitmap of id 0x%x", id), e, null /*data*/); } finally { try { if (is != null) is.close(); @@ -454,7 +454,7 @@ public class BitmapFactory { if (is instanceof AssetManager.AssetInputStream) { Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, "Bitmap.decodeStream: " + - "InputStream is unsupported (AssetManager.AssetInputStream)"); + "InputStream is unsupported (AssetManager.AssetInputStream)", null /*data*/); return null; } else { // pass some temp storage down to the native code. 1024 is made up, diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java index 03d65b2ed969..73c5a1aceab4 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java @@ -114,7 +114,7 @@ public class BitmapShader_Delegate extends Shader_Delegate { canvasMatrix = xform.createInverse(); } catch (java.awt.geom.NoninvertibleTransformException e) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE, - "Unable to inverse matrix in BitmapShader", e); + "Unable to inverse matrix in BitmapShader", e, null /*data*/); canvasMatrix = new java.awt.geom.AffineTransform(); } @@ -123,7 +123,7 @@ public class BitmapShader_Delegate extends Shader_Delegate { localMatrix = localMatrix.createInverse(); } catch (java.awt.geom.NoninvertibleTransformException e) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE, - "Unable to inverse matrix in BitmapShader", e); + "Unable to inverse matrix in BitmapShader", e, null /*data*/); localMatrix = new java.awt.geom.AffineTransform(); } diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java index 18bf4b58ff08..108d1838db7b 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java @@ -250,7 +250,7 @@ public final class Bitmap_Delegate { /*package*/ static boolean nativeCompress(int nativeBitmap, int format, int quality, OutputStream stream, byte[] tempStorage) { Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, - "Bitmap.compress() is not supported"); + "Bitmap.compress() is not supported", null /*data*/); return true; } @@ -386,7 +386,8 @@ public final class Bitmap_Delegate { // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only // used during aidl call so really this should not be called. Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, - "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels."); + "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels.", + null /*data*/); return null; } @@ -395,7 +396,8 @@ public final class Bitmap_Delegate { // This is only called when sending a bitmap through aidl, so really this should not // be called. Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, - "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels."); + "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.", + null /*data*/); return false; } @@ -412,7 +414,7 @@ public final class Bitmap_Delegate { if (paint != null && paint.getMaskFilter() != null) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER, "MaskFilter not supported in Bitmap.extractAlpha", - null); + null, null /*data*/); } int alpha = paint != null ? paint.getAlpha() : 0xFF; diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 8bfaf4041200..5a6902c074dc 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -425,7 +425,7 @@ public final class Canvas_Delegate { assert false; Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_AFFINE, "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " + - "supports affine transformations.", null); + "supports affine transformations.", null, null /*data*/); } } @@ -494,7 +494,7 @@ public final class Canvas_Delegate { if (filterDelegate.isSupported() == false) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_DRAWFILTER, - filterDelegate.getSupportMessage(), null); + filterDelegate.getSupportMessage(), null, null /*data*/); } } diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java index aab310aa626e..9525dcf8e049 100644 --- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java @@ -137,7 +137,7 @@ public final class LinearGradient_Delegate extends Gradient_Delegate { canvasMatrix = xform.createInverse(); } catch (java.awt.geom.NoninvertibleTransformException e) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE, - "Unable to inverse matrix in LinearGradient", e); + "Unable to inverse matrix in LinearGradient", e, null /*data*/); canvasMatrix = new java.awt.geom.AffineTransform(); } @@ -146,7 +146,7 @@ public final class LinearGradient_Delegate extends Gradient_Delegate { localMatrix = localMatrix.createInverse(); } catch (java.awt.geom.NoninvertibleTransformException e) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE, - "Unable to inverse matrix in LinearGradient", e); + "Unable to inverse matrix in LinearGradient", e, null /*data*/); localMatrix = new java.awt.geom.AffineTransform(); } diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java index 0c934fccaa4d..2d77d40f6f54 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java @@ -615,7 +615,7 @@ public final class Matrix_Delegate { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, "Matrix.setPolyToPoly is not supported.", - null); + null, null /*data*/); return false; } diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java index 049ac454c165..7a6da95dbb89 100644 --- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java @@ -74,7 +74,7 @@ public final class NinePatch_Delegate { oos = new ObjectOutputStream(baos); oos.writeObject(chunk); } catch (IOException e) { - Bridge.getLog().error(null, "Failed to serialize NinePatchChunk.", e); + Bridge.getLog().error(null, "Failed to serialize NinePatchChunk.", e, null /*data*/); return null; } finally { if (oos != null) { @@ -198,11 +198,11 @@ public final class NinePatch_Delegate { } } catch (IOException e) { Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Failed to deserialize NinePatchChunk content.", e); + "Failed to deserialize NinePatchChunk content.", e, null /*data*/); return null; } catch (ClassNotFoundException e) { Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Failed to deserialize NinePatchChunk class.", e); + "Failed to deserialize NinePatchChunk class.", e, null /*data*/); return null; } finally { if (ois != null) { diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index 67afecae3493..87164fb42e0b 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -182,7 +182,7 @@ public class Paint_Delegate { } else { Bridge.getLog().fidelityWarning(LayoutLog.TAG_PATHEFFECT, effectDelegate.getSupportMessage(), - null); + null, null /*data*/); } } @@ -377,7 +377,7 @@ public class Paint_Delegate { int color) { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, - "Paint.setShadowLayer is not supported.", null); + "Paint.setShadowLayer is not supported.", null, null /*data*/); } /*package*/ static float getTextSize(Paint thisPaint) { @@ -694,7 +694,7 @@ public class Paint_Delegate { ColorFilter_Delegate filterDelegate = delegate.getColorFilter(); if (filterDelegate != null && filterDelegate.isSupported() == false) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER, - filterDelegate.getSupportMessage(), null); + filterDelegate.getSupportMessage(), null, null /*data*/); } return filter; @@ -733,7 +733,7 @@ public class Paint_Delegate { MaskFilter_Delegate filterDelegate = delegate.getMaskFilter(); if (filterDelegate != null && filterDelegate.isSupported() == false) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER, - filterDelegate.getSupportMessage(), null); + filterDelegate.getSupportMessage(), null, null /*data*/); } return maskfilter; @@ -764,7 +764,7 @@ public class Paint_Delegate { Rasterizer_Delegate rasterizerDelegate = delegate.getRasterizer(); if (rasterizerDelegate != null && rasterizerDelegate.isSupported() == false) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER, - rasterizerDelegate.getSupportMessage(), null); + rasterizerDelegate.getSupportMessage(), null, null /*data*/); } return rasterizer; diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index 62ea62230061..a4e43c111879 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -698,7 +698,7 @@ public final class Path_Delegate { assert false; Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_AFFINE, "android.graphics.Path#transform() only " + - "supports affine transformations.", null); + "supports affine transformations.", null, null /*data*/); } GeneralPath newPath = new GeneralPath(); diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java index 314dcff212a9..147e1d0e7607 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java @@ -74,7 +74,7 @@ public class PorterDuffXfermode_Delegate extends Xfermode_Delegate { } Bridge.getLog().error(LayoutLog.TAG_BROKEN, - String.format("Unknown PorterDuff.Mode: %d", mode)); + String.format("Unknown PorterDuff.Mode: %d", mode), null /*data*/); assert false; return PorterDuff.Mode.SRC_OVER; } @@ -118,7 +118,7 @@ public class PorterDuffXfermode_Delegate extends Xfermode_Delegate { Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN, String.format("Unsupported PorterDuff Mode: %s", mode.name()), - null); + null, null /*data*/); return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha); } diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java index 2c26175d75d9..ffdf5ddb02c9 100644 --- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java @@ -126,7 +126,7 @@ public class RadialGradient_Delegate extends Gradient_Delegate { canvasMatrix = xform.createInverse(); } catch (java.awt.geom.NoninvertibleTransformException e) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE, - "Unable to inverse matrix in RadialGradient", e); + "Unable to inverse matrix in RadialGradient", e, null /*data*/); canvasMatrix = new java.awt.geom.AffineTransform(); } @@ -135,7 +135,7 @@ public class RadialGradient_Delegate extends Gradient_Delegate { localMatrix = localMatrix.createInverse(); } catch (java.awt.geom.NoninvertibleTransformException e) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE, - "Unable to inverse matrix in RadialGradient", e); + "Unable to inverse matrix in RadialGradient", e, null /*data*/); localMatrix = new java.awt.geom.AffineTransform(); } diff --git a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java index f86c56cc2a2c..9b6fb82a6329 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java @@ -417,7 +417,8 @@ public class Region_Delegate { // This is only called by Region.CREATOR (Parcelable.Creator<Region>), which is only // used during aidl call so really this should not be called. Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, - "AIDL is not suppored, and therefore Regions cannot be created from parcels."); + "AIDL is not suppored, and therefore Regions cannot be created from parcels.", + null /*data*/); return 0; } @@ -426,7 +427,8 @@ public class Region_Delegate { // This is only called when sending a region through aidl, so really this should not // be called. Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, - "AIDL is not suppored, and therefore Regions cannot be written to parcels."); + "AIDL is not suppored, and therefore Regions cannot be written to parcels.", + null /*data*/); return false; } diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java index e812f7f0031e..048990a21a75 100644 --- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java @@ -118,7 +118,7 @@ public class SweepGradient_Delegate extends Gradient_Delegate { canvasMatrix = xform.createInverse(); } catch (java.awt.geom.NoninvertibleTransformException e) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE, - "Unable to inverse matrix in SweepGradient", e); + "Unable to inverse matrix in SweepGradient", e, null /*data*/); canvasMatrix = new java.awt.geom.AffineTransform(); } @@ -127,7 +127,7 @@ public class SweepGradient_Delegate extends Gradient_Delegate { localMatrix = localMatrix.createInverse(); } catch (java.awt.geom.NoninvertibleTransformException e) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_INVERSE, - "Unable to inverse matrix in SweepGradient", e); + "Unable to inverse matrix in SweepGradient", e, null /*data*/); localMatrix = new java.awt.geom.AffineTransform(); } diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java index 44275d6ab928..00a2a57951dd 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java @@ -127,13 +127,13 @@ public final class Typeface_Delegate { /*package*/ static synchronized int nativeCreateFromAsset(AssetManager mgr, String path) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, - "Typeface.createFromAsset() is not supported.", null); + "Typeface.createFromAsset() is not supported.", null /*throwable*/, null /*data*/); return 0; } /*package*/ static synchronized int nativeCreateFromFile(String path) { Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, - "Typeface.createFromFile() is not supported.", null); + "Typeface.createFromFile() is not supported.", null /*throwable*/, null /*data*/); return 0; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 6e9f4d5ab76e..37576b4ec9c0 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -137,17 +137,17 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { */ private final static LayoutLog sDefaultLog = new LayoutLog() { @Override - public void error(String tag, String message) { + public void error(String tag, String message, Object data) { System.err.println(message); } @Override - public void error(String tag, String message, Throwable throwable) { + public void error(String tag, String message, Throwable throwable, Object data) { System.err.println(message); } @Override - public void warning(String tag, String message) { + public void warning(String tag, String message, Object data) { System.out.println(message); } }; @@ -207,7 +207,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { @Override public void onInvokeV(String signature, boolean isNative, Object caller) { sDefaultLog.error(null, "Missing Stub: " + signature + - (isNative ? " (native)" : "")); + (isNative ? " (native)" : ""), null /*data*/); if (debug.equalsIgnoreCase("throw")) { // Throwing this exception doesn't seem that useful. It breaks 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 dc027b7df0f6..82e217a677ab 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 @@ -254,6 +254,11 @@ public final class BridgeContext extends Activity { return null; } + // needed by SearchView + if (INPUT_METHOD_SERVICE.equals(service)) { + return null; + } + throw new UnsupportedOperationException("Unsupported Service: " + service); } @@ -338,7 +343,7 @@ public final class BridgeContext extends Activity { } else if (set != null) { // null parser is ok // really this should not be happening since its instantiated in Bridge Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Parser is not a BridgeXmlBlockParser!"); + "Parser is not a BridgeXmlBlockParser!", null /*data*/); return null; } @@ -386,7 +391,8 @@ public final class BridgeContext extends Activity { } else { Bridge.getLog().error(null, String.format( - "Failed to find style '%s' in current theme", defStyleName)); + "Failed to find style '%s' in current theme", defStyleName), + null /*data*/); } } } @@ -443,7 +449,7 @@ public final class BridgeContext extends Activity { } else { // there is a value in the XML, but we need to resolve it in case it's // referencing another resource or a theme value. - ta.bridgeSetValue(index, name, resolveValue(null, name, value)); + ta.bridgeSetValue(index, name, resolveValue(null, name, value, isPlatformFile)); } } } @@ -506,21 +512,24 @@ public final class BridgeContext extends Activity { * @param type the type of the resource * @param name the name of the attribute containing this value. * @param value the resource value, or reference to resolve + * @param isFrameworkValue whether the value is a framework value. + * * @return the resolved resource value or <code>null</code> if it failed to resolve it. */ - private ResourceValue resolveValue(String type, String name, String value) { + private ResourceValue resolveValue(String type, String name, String value, + boolean isFrameworkValue) { if (value == null) { return null; } // get the ResourceValue referenced by this value - ResourceValue resValue = findResValue(value, false /*forceFrameworkOnly*/); + ResourceValue resValue = findResValue(value, isFrameworkValue); // if resValue is null, but value is not null, this means it was not a reference. // we return the name/value wrapper in a ResourceValue. the isFramework flag doesn't // matter. if (resValue == null) { - return new ResourceValue(type, name, value, false /*isFramework*/); + return new ResourceValue(type, name, value, isFrameworkValue); } // we resolved a first reference, but we need to make sure this isn't a reference also. @@ -701,6 +710,15 @@ public final class BridgeContext extends Activity { if (item != null) { return item; } + + // if it was not found and the type is an id, it is possible that the ID was + // generated dynamically when compiling the framework resources. + // Look for it in the R map. + if (BridgeConstants.RES_ID.equals(resType)) { + if (Bridge.getResourceValue(resType, resName) != null) { + return new ResourceValue(resType, resName, true); + } + } } // didn't find the resource anywhere. @@ -709,7 +727,8 @@ public final class BridgeContext extends Activity { if ("+id".equals(resType) == false && "+android:id".equals(resType) == false) { //$NON-NLS-1$ //$NON-NLS-2$ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE, "Couldn't resolve resource @" + - (frameworkOnly ? "android:" : "") + resType + "/" + resName); + (frameworkOnly ? "android:" : "") + resType + "/" + resName, + new ResourceValue(resType, resName, frameworkOnly)); } return null; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java index 61ac81bee4a6..e95d295bb608 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java @@ -180,7 +180,7 @@ public final class BridgeInflater extends LayoutInflater { return inflate(bridgeParser, root); } catch (Exception e) { Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ, - "Failed to parse file " + f.getAbsolutePath(), e); + "Failed to parse file " + f.getAbsolutePath(), e, null /*data*/); return null; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java index 8446a990a9c3..23d81a2b7ba7 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java @@ -143,7 +143,8 @@ public final class BridgeResources extends Resources { try { return ResourceHelper.getColor(value.getValue()); } catch (NumberFormatException e) { - Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e); + Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e, + null /*data*/); return 0; } } @@ -176,13 +177,13 @@ public final class BridgeResources extends Resources { new BridgeXmlBlockParser(parser, mContext, resValue.isFramework())); } catch (XmlPullParserException e) { Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Failed to configure parser for " + value, e); + "Failed to configure parser for " + value, e, null /*data*/); // we'll return null below. } catch (Exception e) { // this is an error and not warning since the file existence is checked before // attempting to parse it. Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ, - "Failed to parse file " + value, e); + "Failed to parse file " + value, e, null /*data*/); return null; } @@ -193,7 +194,8 @@ public final class BridgeResources extends Resources { return ColorStateList.valueOf(color); } catch (NumberFormatException e) { Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, - "Failed to convert " + value + " into a ColorStateList", e); + "Failed to convert " + value + " into a ColorStateList", e, + null /*data*/); return null; } } @@ -253,7 +255,7 @@ public final class BridgeResources extends Resources { } } catch (XmlPullParserException e) { Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Failed to configure parser for " + value.getValue(), e); + "Failed to configure parser for " + value.getValue(), e, null /*data*/); // we'll return null below. } catch (FileNotFoundException e) { // this shouldn't happen since we check above. @@ -288,7 +290,7 @@ public final class BridgeResources extends Resources { } } catch (XmlPullParserException e) { Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Failed to configure parser for " + value.getValue(), e); + "Failed to configure parser for " + value.getValue(), e, null /*data*/); // we'll return null below. } catch (FileNotFoundException e) { // this shouldn't happen since we check above. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java index 42f05e381594..84bb4d1dd304 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java @@ -207,10 +207,10 @@ public final class BridgeTypedArray extends TypedArray { if (i != null) { result |= i.intValue(); } else { - Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE, + Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, String.format( - "Unknown constant \"%s\" in attribute \"%2$s\"", - keyword, mNames[index])); + "\"%s\" in attribute \"%2$s\" is not a valid value", + keyword, mNames[index]), null /*data*/); } } return result; @@ -238,10 +238,10 @@ public final class BridgeTypedArray extends TypedArray { try { return Float.parseFloat(s); } catch (NumberFormatException e) { - Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE, + Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, String.format( - "Unable to convert \"%s\" into a float in attribute \"%2$s\"", - s, mNames[index])); + "\"%s\" in attribute \"%2$s\" cannot be converted to float.", + s, mNames[index]), null /*data*/); // we'll return the default value below. } @@ -271,7 +271,7 @@ public final class BridgeTypedArray extends TypedArray { try { return ResourceHelper.getColor(s); } catch (NumberFormatException e) { - Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e); + Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e, null /*data*/); // we'll return the default value below. } @@ -315,13 +315,13 @@ public final class BridgeTypedArray extends TypedArray { return colorStateList; } catch (XmlPullParserException e) { Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Failed to configure parser for " + value, e); + "Failed to configure parser for " + value, e, null /*data*/); return null; } catch (Exception e) { // this is an error and not warning since the file existence is checked before // attempting to parse it. Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ, - "Failed to parse file " + value, e); + "Failed to parse file " + value, e, null /*data*/); return null; } @@ -331,7 +331,7 @@ public final class BridgeTypedArray extends TypedArray { int color = ResourceHelper.getColor(value); return ColorStateList.valueOf(color); } catch (NumberFormatException e) { - Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e); + Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e, null /*data*/); } assert false; @@ -360,10 +360,10 @@ public final class BridgeTypedArray extends TypedArray { try { return Integer.parseInt(s); } catch (NumberFormatException e) { - Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE, + Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, String.format( - "Unable to convert \"%s\" into a integer in attribute \"%2$s\"", - s, mNames[index])); + "\"%s\" in attribute \"%2$s\" cannont be converted to an integer.", + s, mNames[index]), null /*data*/); // The default value is returned below. } @@ -410,10 +410,10 @@ public final class BridgeTypedArray extends TypedArray { } // looks like we were unable to resolve the dimension value - Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE, + Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, String.format( - "Unable to resolve dimension value \"%1$s\" in attribute \"%2$s\"", - s, mNames[index])); + "\"%1$s\" in attribute \"%2$s\" is not a valid format.", + s, mNames[index]), null /*data*/); assert false; @@ -542,10 +542,10 @@ public final class BridgeTypedArray extends TypedArray { } // looks like we were unable to resolve the fraction value - Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE, + Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, String.format( - "Unable to resolve fraction value \"%1$s\" in attribute \"%2$s\"", - value, mNames[index])); + "\"%1$s\" in attribute \"%2$s\" cannont be converted to a fraction.", + value, mNames[index]), null /*data*/); assert false; @@ -656,7 +656,8 @@ public final class BridgeTypedArray extends TypedArray { Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE, String.format( - "Unable to resolve id \"%1$s\" for attribute \"%2$s\"", value, mNames[index])); + "Unable to resolve id \"%1$s\" for attribute \"%2$s\"", value, mNames[index]), + resValue); assert false; @@ -685,21 +686,7 @@ public final class BridgeTypedArray extends TypedArray { return null; } - Drawable d = ResourceHelper.getDrawable(value, mContext, mResourceData[index].isFramework()); - - if (d != null) { - return d; - } - - // looks like we were unable to resolve the drawable - Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE, - String.format( - "Unable to resolve drawable \"%1$s\" in attribute \"%2$s\"", stringValue, - mNames[index])); - - assert false; - - return null; + return ResourceHelper.getDrawable(value, mContext, mResourceData[index].isFramework()); } @@ -724,10 +711,10 @@ public final class BridgeTypedArray extends TypedArray { return new CharSequence[] { value }; } - Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE, + Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, String.format( String.format("Unknown value for getTextArray(%d) => %s", //DEBUG - index, mResourceData[index].getName()))); + index, mResourceData[index].getName())), null /*data*/); return null; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java index b3f1fff2c3c7..21d6b1a67505 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java @@ -393,27 +393,26 @@ public class GcSnapshot { * @param bitmap the bitmap to link to. */ public void setBitmap(Bitmap_Delegate bitmap) { - assert mLayers.size() == 0; - // create a new Layer for the bitmap. This will be the base layer. Graphics2D graphics2D = bitmap.getImage().createGraphics(); Layer baseLayer = new Layer(graphics2D, bitmap); - // add it to the list. - mLayers.add(baseLayer); + // Set the current transform and clip which can either come from mTransform/mClip if they + // were set when there was no bitmap/layers or from the current base layers if there is + // one already. - // if transform and clip where modified before, get the information and give it to the - // layer. + graphics2D.setTransform(getTransform()); + // reset mTransform in case there was one. + mTransform = null; - if (mTransform != null) { - graphics2D.setTransform(mTransform); - mTransform = null; - } + baseLayer.setClip(getClip()); + // reset mClip in case there was one. + mClip = null; + + // replace whatever current layers we have with this. + mLayers.clear(); + mLayers.add(baseLayer); - if (mClip != null) { - baseLayer.setClip(mClip); - mClip = null; - } } public void translate(float dx, float dy) { @@ -731,7 +730,7 @@ public class GcSnapshot { } else { Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER, shaderDelegate.getSupportMessage(), - null); + null /*throwable*/, null /*data*/); } } @@ -765,7 +764,7 @@ public class GcSnapshot { } else { Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE, xfermodeDelegate.getSupportMessage(), - null); + null /*throwable*/, null /*data*/); } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index 566d4d4a87f8..2439791f0097 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -1046,7 +1046,8 @@ public class RenderSessionImpl { assert false; mParams.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE, - String.format("Unable to resolve parent style name: %s", parentName)); + String.format("Unable to resolve parent style name: %s", parentName), + null /*data*/); return null; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java index 4e331d1f9368..475b4be25314 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java @@ -166,7 +166,7 @@ public final class ResourceHelper { } catch (IOException e) { // failed to read the file, we'll return null below. Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ, - "Failed lot load " + file.getAbsolutePath(), e); + "Failed lot load " + file.getAbsolutePath(), e, null /*data*/); } } @@ -197,11 +197,12 @@ public final class ResourceHelper { } catch (Exception e) { // this is an error and not warning since the file existence is checked before // attempting to parse it. - Bridge.getLog().error(null, "Failed to parse file " + value, e); + Bridge.getLog().error(null, "Failed to parse file " + value, e, null /*data*/); } } else { Bridge.getLog().error(LayoutLog.TAG_BROKEN, - String.format("File %s does not exist (or is not a file)", stringValue)); + String.format("File %s does not exist (or is not a file)", stringValue), + null /*data*/); } return null; @@ -228,7 +229,7 @@ public final class ResourceHelper { } catch (IOException e) { // we'll return null below Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ, - "Failed lot load " + bmpFile.getAbsolutePath(), e); + "Failed lot load " + bmpFile.getAbsolutePath(), e, null /*data*/); } } else { // attempt to get a color from the value @@ -238,7 +239,8 @@ public final class ResourceHelper { } catch (NumberFormatException e) { // we'll return null below. Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, - "Failed to convert " + stringValue + " into a drawable", e); + "Failed to convert " + stringValue + " into a drawable", e, + null /*data*/); } } } |