Merge "Optimize text GL setup" into jb-mr2-dev
diff --git a/api/current.txt b/api/current.txt
index 73eed6e..12dc6b4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2445,6 +2445,7 @@
method public static android.animation.ObjectAnimator ofObject(java.lang.Object, java.lang.String, android.animation.TypeEvaluator, java.lang.Object...);
method public static android.animation.ObjectAnimator ofObject(T, android.util.Property<T, V>, android.animation.TypeEvaluator<V>, V...);
method public static android.animation.ObjectAnimator ofPropertyValuesHolder(java.lang.Object, android.animation.PropertyValuesHolder...);
+ method public void setAutoCancel(boolean);
method public void setProperty(android.util.Property);
method public void setPropertyName(java.lang.String);
}
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index d196392..e1270af 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -28,6 +28,8 @@
#include <gui/SurfaceComposerClient.h>
#include <gui/ISurfaceComposer.h>
+#include <ui/PixelFormat.h>
+
#include <SkImageEncoder.h>
#include <SkBitmap.h>
#include <SkData.h>
@@ -138,7 +140,7 @@
ssize_t mapsize = -1;
void const* base = 0;
- uint32_t w, h, f;
+ uint32_t w, s, h, f;
size_t size = 0;
ScreenshotClient screenshot;
@@ -147,6 +149,7 @@
base = screenshot.getPixels();
w = screenshot.getWidth();
h = screenshot.getHeight();
+ s = screenshot.getStride();
f = screenshot.getFormat();
size = screenshot.getSize();
} else {
@@ -160,6 +163,7 @@
size_t offset = (vinfo.xoffset + vinfo.yoffset*vinfo.xres) * bytespp;
w = vinfo.xres;
h = vinfo.yres;
+ s = vinfo.xres;
size = w*h*bytespp;
mapsize = offset + size;
mapbase = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, fb, 0);
@@ -187,7 +191,11 @@
write(fd, &w, 4);
write(fd, &h, 4);
write(fd, &f, 4);
- write(fd, base, size);
+ size_t Bpp = bytesPerPixel(f);
+ for (size_t y=0 ; y<h ; y++) {
+ write(fd, base, w*Bpp);
+ base = (void *)((char *)base + s*Bpp);
+ }
}
}
close(fd);
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 0372cb0..173ee73 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -19,7 +19,6 @@
import android.util.Log;
import android.util.Property;
-import java.lang.reflect.Method;
import java.util.ArrayList;
/**
@@ -49,6 +48,8 @@
private Property mProperty;
+ private boolean mAutoCancel = false;
+
/**
* Sets the name of the property that will be animated. This name is used to derive
* a setter function that will be called to set animated values.
@@ -346,17 +347,83 @@
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
- setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator)null, values));
+ setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator) null, values));
} else {
- setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values));
+ setValues(PropertyValuesHolder.ofObject(mPropertyName,
+ (TypeEvaluator) null, values));
}
} else {
super.setObjectValues(values);
}
}
+ /**
+ * autoCancel controls whether an ObjectAnimator will be canceled automatically
+ * when any other ObjectAnimator with the same target and properties is started.
+ * Setting this flag may make it easier to run different animators on the same target
+ * object without having to keep track of whether there are conflicting animators that
+ * need to be manually canceled. Canceling animators must have the same exact set of
+ * target properties, in the same order.
+ *
+ * @param cancel Whether future ObjectAnimators with the same target and properties
+ * as this ObjectAnimator will cause this ObjectAnimator to be canceled.
+ */
+ public void setAutoCancel(boolean cancel) {
+ mAutoCancel = cancel;
+ }
+
+ private boolean hasSameTargetAndProperties(Animator anim) {
+ if (anim instanceof ObjectAnimator) {
+ PropertyValuesHolder[] theirValues = ((ObjectAnimator) anim).getValues();
+ if (((ObjectAnimator) anim).getTarget() == mTarget &&
+ mValues.length == theirValues.length) {
+ for (int i = 0; i < mValues.length; ++i) {
+ PropertyValuesHolder pvhMine = mValues[i];
+ PropertyValuesHolder pvhTheirs = theirValues[i];
+ if (pvhMine.getPropertyName() == null ||
+ !pvhMine.getPropertyName().equals(pvhTheirs.getPropertyName())) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public void start() {
+ // See if any of the current active/pending animators need to be canceled
+ AnimationHandler handler = sAnimationHandler.get();
+ if (handler != null) {
+ int numAnims = handler.mAnimations.size();
+ for (int i = numAnims - 1; i >= 0; i--) {
+ if (handler.mAnimations.get(i) instanceof ObjectAnimator) {
+ ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i);
+ if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
+ anim.cancel();
+ }
+ }
+ }
+ numAnims = handler.mPendingAnimations.size();
+ for (int i = numAnims - 1; i >= 0; i--) {
+ if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) {
+ ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i);
+ if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
+ anim.cancel();
+ }
+ }
+ }
+ numAnims = handler.mDelayedAnims.size();
+ for (int i = numAnims - 1; i >= 0; i--) {
+ if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) {
+ ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i);
+ if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
+ anim.cancel();
+ }
+ }
+ }
+ }
if (DBG) {
Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration());
for (int i = 0; i < mValues.length; ++i) {
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 4a58072..ea605b9 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -83,7 +83,10 @@
// The static sAnimationHandler processes the internal timing loop on which all animations
// are based
- private static ThreadLocal<AnimationHandler> sAnimationHandler =
+ /**
+ * @hide
+ */
+ protected static ThreadLocal<AnimationHandler> sAnimationHandler =
new ThreadLocal<AnimationHandler>();
// The time interpolator to be used if none is set on the animation
@@ -531,22 +534,27 @@
* animations possible.
*
* The handler uses the Choreographer for executing periodic callbacks.
+ *
+ * @hide
*/
- private static class AnimationHandler implements Runnable {
+ protected static class AnimationHandler implements Runnable {
// The per-thread list of all active animations
- private final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
+ /** @hide */
+ protected final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
// Used in doAnimationFrame() to avoid concurrent modifications of mAnimations
private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>();
// The per-thread set of animations to be started on the next animation frame
- private final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>();
+ /** @hide */
+ protected final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>();
/**
* Internal per-thread collections used to avoid set collisions as animations start and end
* while being processed.
+ * @hide
*/
- private final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>();
+ protected final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>();
private final ArrayList<ValueAnimator> mEndingAnims = new ArrayList<ValueAnimator>();
private final ArrayList<ValueAnimator> mReadyAnims = new ArrayList<ValueAnimator>();
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d57d56a..614ac07 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1136,6 +1136,7 @@
<java-symbol type="xml" name="power_profile" />
<java-symbol type="xml" name="time_zones_by_country" />
<java-symbol type="xml" name="sms_short_codes" />
+ <java-symbol type="xml" name="audio_assets" />
<java-symbol type="raw" name="accessibility_gestures" />
<java-symbol type="raw" name="incognito_mode_start_page" />
diff --git a/core/res/res/xml/audio_assets.xml b/core/res/res/xml/audio_assets.xml
new file mode 100644
index 0000000..746dbab
--- /dev/null
+++ b/core/res/res/xml/audio_assets.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Mapping of UI sound effects to audio assets under /system/media/audio/ui.
+ Modify this file to override default sound assets.
+ Currently only touch sounds can be overridden. Other groups can be added
+ in the future for other UI sounds like camera, lock, dock...
+-->
+
+<audio_assets version="1.0">
+ <group name="touch_sounds">
+ <asset id="FX_KEY_CLICK" file="Effect_Tick.ogg"/>
+ <asset id="FX_FOCUS_NAVIGATION_UP" file="Effect_Tick.ogg"/>
+ <asset id="FX_FOCUS_NAVIGATION_DOWN" file="Effect_Tick.ogg"/>
+ <asset id="FX_FOCUS_NAVIGATION_LEFT" file="Effect_Tick.ogg"/>
+ <asset id="FX_FOCUS_NAVIGATION_RIGHT" file="Effect_Tick.ogg"/>
+ <asset id="FX_KEYPRESS_STANDARD" file="KeypressStandard.ogg"/>
+ <asset id="FX_KEYPRESS_SPACEBAR" file="KeypressSpacebar.ogg"/>
+ <asset id="FX_KEYPRESS_DELETE" file="KeypressDelete.ogg"/>
+ <asset id="FX_KEYPRESS_RETURN" file="KeypressReturn.ogg"/>
+ </group>
+</audio_assets>
diff --git a/core/tests/coretests/src/android/animation/AutoCancelTest.java b/core/tests/coretests/src/android/animation/AutoCancelTest.java
new file mode 100644
index 0000000..b1f88db
--- /dev/null
+++ b/core/tests/coretests/src/android/animation/AutoCancelTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.animation;
+
+import android.os.Handler;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+public class AutoCancelTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
+
+ boolean mAnimX1Canceled = false;
+ boolean mAnimXY1Canceled = false;
+ boolean mAnimX2Canceled = false;
+ boolean mAnimXY2Canceled = false;
+
+ private static final long START_DELAY = 100;
+ private static final long DELAYED_START_DURATION = 200;
+ private static final long FUTURE_TIMEOUT = 1000;
+
+ HashMap<Animator, Boolean> mCanceledMap = new HashMap<Animator, Boolean>();
+
+ public AutoCancelTest() {
+ super(BasicAnimatorActivity.class);
+ }
+
+ ObjectAnimator setupAnimator(long startDelay, String... properties) {
+ ObjectAnimator returnVal;
+ if (properties.length == 1) {
+ returnVal = ObjectAnimator.ofFloat(this, properties[0], 0, 1);
+ } else {
+ PropertyValuesHolder[] pvhArray = new PropertyValuesHolder[properties.length];
+ for (int i = 0; i < properties.length; i++) {
+ pvhArray[i] = PropertyValuesHolder.ofFloat(properties[i], 0, 1);
+ }
+ returnVal = ObjectAnimator.ofPropertyValuesHolder(this, pvhArray);
+ }
+ returnVal.setAutoCancel(true);
+ returnVal.setStartDelay(startDelay);
+ returnVal.addListener(mCanceledListener);
+ return returnVal;
+ }
+
+ private void setupAnimators(long startDelay, boolean startLater, final FutureWaiter future)
+ throws Exception {
+ // Animators to be auto-canceled
+ final ObjectAnimator animX1 = setupAnimator(startDelay, "x");
+ final ObjectAnimator animY1 = setupAnimator(startDelay, "y");
+ final ObjectAnimator animXY1 = setupAnimator(startDelay, "x", "y");
+ final ObjectAnimator animXZ1 = setupAnimator(startDelay, "x", "z");
+
+ animX1.start();
+ animY1.start();
+ animXY1.start();
+ animXZ1.start();
+
+ final ObjectAnimator animX2 = setupAnimator(0, "x");
+ animX2.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // We expect only animX1 to be canceled at this point
+ if (mCanceledMap.get(animX1) == null ||
+ mCanceledMap.get(animX1) != true ||
+ mCanceledMap.get(animY1) != null ||
+ mCanceledMap.get(animXY1) != null ||
+ mCanceledMap.get(animXZ1) != null) {
+ future.set(false);
+ }
+ }
+ });
+
+ final ObjectAnimator animXY2 = setupAnimator(0, "x", "y");
+ animXY2.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // We expect only animXY1 to be canceled at this point
+ if (mCanceledMap.get(animXY1) == null ||
+ mCanceledMap.get(animXY1) != true ||
+ mCanceledMap.get(animY1) != null ||
+ mCanceledMap.get(animXZ1) != null) {
+ future.set(false);
+ }
+
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Release future if not done already via failures during start
+ future.release();
+ }
+ });
+
+ if (startLater) {
+ Handler handler = new Handler();
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ animX2.start();
+ animXY2.start();
+ }
+ }, DELAYED_START_DURATION);
+ } else {
+ animX2.start();
+ animXY2.start();
+ }
+ }
+
+ @SmallTest
+ public void testAutoCancel() throws Exception {
+ final FutureWaiter future = new FutureWaiter();
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ setupAnimators(0, false, future);
+ } catch (Exception e) {
+ future.setException(e);
+ }
+ }
+ });
+ assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
+ }
+
+ @SmallTest
+ public void testAutoCancelDelayed() throws Exception {
+ final FutureWaiter future = new FutureWaiter();
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ setupAnimators(START_DELAY, false, future);
+ } catch (Exception e) {
+ future.setException(e);
+ }
+ }
+ });
+ assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
+ }
+
+ @SmallTest
+ public void testAutoCancelTestLater() throws Exception {
+ final FutureWaiter future = new FutureWaiter();
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ setupAnimators(0, true, future);
+ } catch (Exception e) {
+ future.setException(e);
+ }
+ }
+ });
+ assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
+ }
+
+ @SmallTest
+ public void testAutoCancelDelayedTestLater() throws Exception {
+ final FutureWaiter future = new FutureWaiter();
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ setupAnimators(START_DELAY, true, future);
+ } catch (Exception e) {
+ future.setException(e);
+ }
+ }
+ });
+ assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
+ }
+
+ private AnimatorListenerAdapter mCanceledListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCanceledMap.put(animation, true);
+ }
+ };
+
+ public void setX(float x) {}
+
+ public void setY(float y) {}
+
+ public void setZ(float z) {}
+}
+
+
diff --git a/core/tests/coretests/src/android/animation/FutureWaiter.java b/core/tests/coretests/src/android/animation/FutureWaiter.java
index 320a1c2..0c65e20 100644
--- a/core/tests/coretests/src/android/animation/FutureWaiter.java
+++ b/core/tests/coretests/src/android/animation/FutureWaiter.java
@@ -23,14 +23,21 @@
* {@link com.google.common.util.concurrent.AbstractFuture#set(Object)} method internally. It
* also exposes the protected {@link AbstractFuture#setException(Throwable)} method.
*/
-public class FutureWaiter extends AbstractFuture<Void> {
+public class FutureWaiter extends AbstractFuture<Boolean> {
/**
* Release the Future currently waiting on
* {@link com.google.common.util.concurrent.AbstractFuture#get()}.
*/
public void release() {
- super.set(null);
+ super.set(true);
+ }
+
+ /**
+ * Used to indicate failure (when the result value is false).
+ */
+ public void set(boolean result) {
+ super.set(result);
}
@Override
diff --git a/docs/html/training/notify-user/build-notification.jd b/docs/html/training/notify-user/build-notification.jd
index ba66028..80f2cd5 100644
--- a/docs/html/training/notify-user/build-notification.jd
+++ b/docs/html/training/notify-user/build-notification.jd
@@ -149,12 +149,14 @@
<p>For example:</p>
<pre>
+NotificationCompat.Builder mBuilder;
+...
// Sets an ID for the notification
int mNotificationId = 001;
// Gets an instance of the NotificationManager service
NotificationManager mNotifyMgr =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Builds the notification and issues it.
-mNotifyMgr.notify(mNotificationId, builder.build());
+mNotifyMgr.notify(mNotificationId, mBuilder.build());
</pre>
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 1ace42f..7907224 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -78,7 +78,7 @@
renderer.restoreDisplayState(op->state, kStateDeferFlag_Draw);
#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
- renderer.eventMark(strlen(op->name()), op->name());
+ renderer.eventMark(op->name());
#endif
status |= op->applyDraw(renderer, dirty, 0);
logBuffer.writeCommand(0, op->name());
@@ -134,7 +134,6 @@
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount);
renderer.restoreToCount(mRestoreCount);
-
return DrawGlInfo::kStatusDone;
}
@@ -373,9 +372,10 @@
DEFER_LOGD("--flushing");
renderer.eventMark("Flush");
+ DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers();
renderer.restoreToCount(1);
status |= replayBatchList(mBatches, renderer, dirty);
- renderer.resetDrawModifiers();
+ renderer.setDrawModifiers(restoreDrawModifiers);
DEFER_LOGD("--flush complete, returning %x", status);
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 8e908fa..cb9da8f 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -72,7 +72,7 @@
void addDrawOp(OpenGLRenderer& renderer, DrawOp* op);
private:
- /*
+ /**
* Resets the batching back-pointers, creating a barrier in the operation stream so that no ops
* added in the future will be inserted into a batch that already exist.
*/
@@ -88,9 +88,10 @@
int getStateOpDeferFlags() const;
int getDrawOpDeferFlags() const;
- /*
- *
- * at defer time, stores the savecount of save/saveLayer ops that were
+ /**
+ * At defer time, stores the *defer time* savecount of save/saveLayer ops that were deferred, so
+ * that when an associated restoreToCount is deferred, it can be recorded as a
+ * RestoreToCountBatch
*/
Vector<int> mSaveStack;
int mComplexClipStackStart;
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 257c875..55ddd17 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -443,7 +443,7 @@
: mReplayStruct(replayStruct), mLevel(level) {}
inline void operator()(DisplayListOp* operation, int saveCount) {
#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
- replayStruct.mRenderer.eventMark(operation->name());
+ mReplayStruct.mRenderer.eventMark(operation->name());
#endif
operation->replay(mReplayStruct, saveCount, mLevel);
}
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 4561c61..4f2db69 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -284,8 +284,13 @@
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
// NOTE: don't bother with actual saveLayer, instead issuing it at flush time
- int newSaveCount = deferStruct.mRenderer.save(mFlags);
+ int newSaveCount = deferStruct.mRenderer.getSaveCount();
deferStruct.mDeferredList.addSaveLayer(deferStruct.mRenderer, this, newSaveCount);
+
+ // NOTE: don't issue full saveLayer, since that has side effects/is costly. instead just
+ // setup the snapshot for deferral, and re-issue the op at flush time
+ deferStruct.mRenderer.saveLayerDeferred(mArea.left, mArea.top, mArea.right, mArea.bottom,
+ mAlpha, mMode, mFlags);
}
virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index e7dfa66..bc28d65 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -112,7 +112,10 @@
OpenGLRenderer::OpenGLRenderer():
mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
- resetDrawModifiers();
+ mDrawModifiers.mShader = NULL;
+ mDrawModifiers.mColorFilter = NULL;
+ mDrawModifiers.mHasShadow = false;
+ mDrawModifiers.mHasDrawFilter = false;
memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
@@ -643,6 +646,63 @@
return count;
}
+void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer) {
+ const Rect untransformedBounds(bounds);
+
+ currentTransform().mapRect(bounds);
+
+ // Layers only make sense if they are in the framebuffer's bounds
+ if (bounds.intersect(*mSnapshot->clipRect)) {
+ // We cannot work with sub-pixels in this case
+ bounds.snapToPixelBoundaries();
+
+ // When the layer is not an FBO, we may use glCopyTexImage so we
+ // need to make sure the layer does not extend outside the bounds
+ // of the framebuffer
+ if (!bounds.intersect(mSnapshot->previous->viewport)) {
+ bounds.setEmpty();
+ } else if (fboLayer) {
+ clip.set(bounds);
+ mat4 inverse;
+ inverse.loadInverse(currentTransform());
+ inverse.mapRect(clip);
+ clip.snapToPixelBoundaries();
+ if (clip.intersect(untransformedBounds)) {
+ clip.translate(-untransformedBounds.left, -untransformedBounds.top);
+ bounds.set(untransformedBounds);
+ } else {
+ clip.setEmpty();
+ }
+ }
+ } else {
+ bounds.setEmpty();
+ }
+}
+
+int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float bottom,
+ int alpha, SkXfermode::Mode mode, int flags) {
+ const GLuint previousFbo = mSnapshot->fbo;
+ const int count = saveSnapshot(flags);
+
+ if (!mSnapshot->isIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) {
+ // initialize the snapshot as though it almost represents an FBO layer so deferred draw
+ // operations will be able to store and restore the current clip and transform info, and
+ // quick rejection will be correct (for display lists)
+
+ Rect bounds(left, top, right, bottom);
+ Rect clip;
+ calculateLayerBoundsAndClip(bounds, clip, true);
+
+ if (!bounds.isEmpty() && !clip.isEmpty()) {
+ mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
+ mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
+ }
+ }
+
+ return count;
+}
+
+
/**
* Layers are viewed by Skia are slightly different than layers in image editing
* programs (for instance.) When a layer is created, previously created layers
@@ -704,35 +764,7 @@
// Window coordinates of the layer
Rect clip;
Rect bounds(left, top, right, bottom);
- Rect untransformedBounds(bounds);
- currentTransform().mapRect(bounds);
-
- // Layers only make sense if they are in the framebuffer's bounds
- if (bounds.intersect(*mSnapshot->clipRect)) {
- // We cannot work with sub-pixels in this case
- bounds.snapToPixelBoundaries();
-
- // When the layer is not an FBO, we may use glCopyTexImage so we
- // need to make sure the layer does not extend outside the bounds
- // of the framebuffer
- if (!bounds.intersect(mSnapshot->previous->viewport)) {
- bounds.setEmpty();
- } else if (fboLayer) {
- clip.set(bounds);
- mat4 inverse;
- inverse.loadInverse(currentTransform());
- inverse.mapRect(clip);
- clip.snapToPixelBoundaries();
- if (clip.intersect(untransformedBounds)) {
- clip.translate(-left, -top);
- bounds.set(untransformedBounds);
- } else {
- clip.setEmpty();
- }
- }
- } else {
- bounds.setEmpty();
- }
+ calculateLayerBoundsAndClip(bounds, clip, fboLayer);
if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
bounds.getHeight() > mCaches.maxTextureSize ||
@@ -900,7 +932,7 @@
}
void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
- float alpha = layer->getAlpha() / 255.0f;
+ float alpha = layer->getAlpha() / 255.0f * mSnapshot->alpha;
setupDraw();
if (layer->getRenderTarget() == GL_TEXTURE_2D) {
@@ -964,9 +996,10 @@
layer->setFilter(GL_LINEAR, true);
}
+ float alpha = layer->getAlpha() / 255.0f * mSnapshot->alpha;
+ bool blend = layer->isBlend() || alpha < 1.0f;
drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(),
- layer->getTexture(), layer->getAlpha() / 255.0f,
- layer->getMode(), layer->isBlend(),
+ layer->getTexture(), alpha, layer->getMode(), blend,
&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
GL_TRIANGLE_STRIP, gMeshCount, swap, swap || simpleTransform);
@@ -1001,7 +1034,7 @@
rects = safeRegion.getArray(&count);
}
- const float alpha = layer->getAlpha() / 255.0f;
+ const float alpha = layer->getAlpha() / 255.0f * mSnapshot->alpha;
const float texX = 1.0f / float(layer->getWidth());
const float texY = 1.0f / float(layer->getHeight());
const float height = rect.getHeight();
@@ -1201,13 +1234,6 @@
// State Deferral
///////////////////////////////////////////////////////////////////////////////
-void OpenGLRenderer::resetDrawModifiers() {
- mDrawModifiers.mShader = NULL;
- mDrawModifiers.mColorFilter = NULL;
- mDrawModifiers.mHasShadow = false;
- mDrawModifiers.mHasDrawFilter = false;
-}
-
bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) {
const Rect& currentClip = *(mSnapshot->clipRect);
const mat4& currentMatrix = *(mSnapshot->transform);
@@ -1246,7 +1272,7 @@
mSnapshot->alpha = state.mAlpha;
}
- if (!state.mClip.isEmpty()) { //stateDeferFlags & kStateDeferFlag_Clip) {
+ if (!state.mClip.isEmpty()) {
mSnapshot->setClip(state.mClip.left, state.mClip.top, state.mClip.right, state.mClip.bottom);
dirtyClip();
}
@@ -1805,7 +1831,7 @@
// All the usual checks and setup operations (quickReject, setupDraw, etc.)
// will be performed by the display list itself
if (displayList && displayList->isRenderable()) {
- if (true || CC_UNLIKELY(mCaches.drawDeferDisabled)) { // NOTE: temporary workaround
+ if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
ReplayStateStruct replayStruct(*this, dirty, replayFlags);
displayList->replay(replayStruct, 0);
return replayStruct.mDrawGlStatus;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 554ccb0..71bd6bb 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -206,6 +206,9 @@
virtual int saveLayer(float left, float top, float right, float bottom,
int alpha, SkXfermode::Mode mode, int flags);
+ int saveLayerDeferred(float left, float top, float right, float bottom,
+ int alpha, SkXfermode::Mode mode, int flags);
+
virtual void translate(float dx, float dy);
virtual void rotate(float degrees);
virtual void scale(float sx, float sy);
@@ -274,10 +277,12 @@
SkPaint* filterPaint(SkPaint* paint);
- void resetDrawModifiers();
bool storeDisplayState(DeferredDisplayState& state, int stateDeferFlags);
void restoreDisplayState(const DeferredDisplayState& state, int stateDeferFlags);
+ const DrawModifiers& getDrawModifiers() { return mDrawModifiers; }
+ void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; }
+
// TODO: what does this mean? no perspective? no rotate?
ANDROID_API bool isCurrentTransformSimple() {
return mSnapshot->transform->isSimple();
@@ -539,6 +544,11 @@
bool quickRejectPreStroke(float left, float top, float right, float bottom, SkPaint* paint);
/**
+ * given the local bounds of the layer, calculates ...
+ */
+ void calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer);
+
+ /**
* Creates a new layer stored in the specified snapshot.
*
* @param snapshot The snapshot associated with the new layer
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index f0a5c28..3e4d6f3 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -42,6 +42,8 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
@@ -70,10 +72,14 @@
import android.view.VolumePanel;
import com.android.internal.telephony.ITelephony;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.HashMap;
@@ -197,28 +203,12 @@
/* Sound effect file names */
private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
- private static final String[] SOUND_EFFECT_FILES = new String[] {
- "Effect_Tick.ogg",
- "KeypressStandard.ogg",
- "KeypressSpacebar.ogg",
- "KeypressDelete.ogg",
- "KeypressReturn.ogg"
- };
+ private static final List<String> SOUND_EFFECT_FILES = new ArrayList<String>();
/* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
* file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
* uses soundpool (second column) */
- private final int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
- {0, -1}, // FX_KEY_CLICK
- {0, -1}, // FX_FOCUS_NAVIGATION_UP
- {0, -1}, // FX_FOCUS_NAVIGATION_DOWN
- {0, -1}, // FX_FOCUS_NAVIGATION_LEFT
- {0, -1}, // FX_FOCUS_NAVIGATION_RIGHT
- {1, -1}, // FX_KEYPRESS_STANDARD
- {2, -1}, // FX_KEYPRESS_SPACEBAR
- {3, -1}, // FX_FOCUS_DELETE
- {4, -1} // FX_FOCUS_RETURN
- };
+ private final int[][] SOUND_EFFECT_FILES_MAP = new int[AudioManager.NUM_SOUND_EFFECTS][2];
/** @hide Maximum volume index values for audio streams */
private final int[] MAX_STREAM_VOLUME = new int[] {
@@ -1634,6 +1624,99 @@
return mMode;
}
+ //==========================================================================================
+ // Sound Effects
+ //==========================================================================================
+
+ private static final String TAG_AUDIO_ASSETS = "audio_assets";
+ private static final String ATTR_VERSION = "version";
+ private static final String TAG_GROUP = "group";
+ private static final String ATTR_GROUP_NAME = "name";
+ private static final String TAG_ASSET = "asset";
+ private static final String ATTR_ASSET_ID = "id";
+ private static final String ATTR_ASSET_FILE = "file";
+
+ private static final String ASSET_FILE_VERSION = "1.0";
+ private static final String GROUP_TOUCH_SOUNDS = "touch_sounds";
+
+ private void loadTouchSoundAssetDefaults() {
+ SOUND_EFFECT_FILES.add("Effect_Tick.ogg");
+ for (int i = 0; i < AudioManager.NUM_SOUND_EFFECTS; i++) {
+ SOUND_EFFECT_FILES_MAP[i][0] = 0;
+ SOUND_EFFECT_FILES_MAP[i][1] = -1;
+ }
+ }
+
+ private void loadTouchSoundAssets() {
+ XmlResourceParser parser = null;
+
+ loadTouchSoundAssetDefaults();
+
+ try {
+ parser = mContext.getResources().getXml(com.android.internal.R.xml.audio_assets);
+
+ XmlUtils.beginDocument(parser, TAG_AUDIO_ASSETS);
+ String version = parser.getAttributeValue(null, ATTR_VERSION);
+ boolean inTouchSoundsGroup = false;
+
+ if (ASSET_FILE_VERSION.equals(version)) {
+ while (true) {
+ XmlUtils.nextElement(parser);
+ String element = parser.getName();
+ if (element == null) {
+ break;
+ }
+ if (element.equals(TAG_GROUP)) {
+ String name = parser.getAttributeValue(null, ATTR_GROUP_NAME);
+ if (GROUP_TOUCH_SOUNDS.equals(name)) {
+ inTouchSoundsGroup = true;
+ break;
+ }
+ }
+ }
+ while (inTouchSoundsGroup) {
+ XmlUtils.nextElement(parser);
+ String element = parser.getName();
+ if (element == null) {
+ break;
+ }
+ if (element.equals(TAG_ASSET)) {
+ String id = parser.getAttributeValue(null, ATTR_ASSET_ID);
+ String file = parser.getAttributeValue(null, ATTR_ASSET_FILE);
+ int fx;
+
+ try {
+ Field field = AudioManager.class.getField(id);
+ fx = field.getInt(null);
+ } catch (Exception e) {
+ Log.w(TAG, "Invalid touch sound ID: "+id);
+ continue;
+ }
+
+ int i = SOUND_EFFECT_FILES.indexOf(file);
+ if (i == -1) {
+ i = SOUND_EFFECT_FILES.size();
+ SOUND_EFFECT_FILES.add(file);
+ }
+ SOUND_EFFECT_FILES_MAP[fx][0] = i;
+ } else {
+ break;
+ }
+ }
+ }
+ } catch (Resources.NotFoundException e) {
+ Log.w(TAG, "audio assets file not found", e);
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "XML parser exception reading touch sound assets", e);
+ } catch (IOException e) {
+ Log.w(TAG, "I/O exception reading touch sound assets", e);
+ } finally {
+ if (parser != null) {
+ parser.close();
+ }
+ }
+ }
+
/** @see AudioManager#playSoundEffect(int) */
public void playSoundEffect(int effectType) {
sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
@@ -1654,6 +1737,8 @@
public boolean loadSoundEffects() {
int status;
+ loadTouchSoundAssets();
+
synchronized (mSoundEffectsLock) {
if (!mBootCompleted) {
Log.w(TAG, "loadSoundEffects() called before boot complete");
@@ -1692,8 +1777,8 @@
* Once loaded, the value in poolId is the sample ID and the same
* sample can be reused for another effect using the same file.
*/
- int[] poolId = new int[SOUND_EFFECT_FILES.length];
- for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
+ int[] poolId = new int[SOUND_EFFECT_FILES.size()];
+ for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
poolId[fileIdx] = -1;
}
/*
@@ -1711,7 +1796,7 @@
if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
String filePath = Environment.getRootDirectory()
+ SOUND_EFFECTS_PATH
- + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
+ + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]);
int sampleId = mSoundPool.load(filePath, 0);
if (sampleId <= 0) {
Log.w(TAG, "Soundpool could not load file: "+filePath);
@@ -1776,8 +1861,8 @@
mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS);
mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT);
- int[] poolId = new int[SOUND_EFFECT_FILES.length];
- for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
+ int[] poolId = new int[SOUND_EFFECT_FILES.size()];
+ for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
poolId[fileIdx] = 0;
}
@@ -3279,7 +3364,8 @@
} else {
MediaPlayer mediaPlayer = new MediaPlayer();
try {
- String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
+ String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH +
+ SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]);
mediaPlayer.setDataSource(filePath);
mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
mediaPlayer.prepare();
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 46f6430..4885407 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -147,6 +147,16 @@
// In other words, mUserId should never change - hence it's marked final.
mUserId = mLockPatternUtils.getCurrentUser();
+ DevicePolicyManager dpm =
+ (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ if (dpm != null) {
+ mDisabledFeatures = getDisabledFeatures(dpm);
+ mCameraDisabled = dpm.getCameraDisabled(null);
+ }
+
+ mSafeModeEnabled = LockPatternUtils.isSafeModeEnabled();
+
+ // These need to be created with the user context...
Context userContext = null;
try {
final String packageName = "system";
@@ -159,29 +169,13 @@
userContext = context;
}
- // These need to be created with the user context...
mAppWidgetHost = new AppWidgetHost(userContext, APPWIDGET_HOST_ID, mOnClickHandler,
Looper.myLooper());
+
+ cleanupAppWidgetIds();
+
mAppWidgetManager = AppWidgetManager.getInstance(userContext);
- cleanupAppWidgetIds();
-
- mSecurityModel = new KeyguardSecurityModel(context);
-
- mViewStateManager = new KeyguardViewStateManager(this);
-
- DevicePolicyManager dpm =
- (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
- if (dpm != null) {
- mDisabledFeatures = getDisabledFeatures(dpm);
- mCameraDisabled = dpm.getCameraDisabled(null);
- }
-
- mSafeModeEnabled = LockPatternUtils.isSafeModeEnabled();
-
- cleanupAppWidgetIds();
-
- mAppWidgetManager = AppWidgetManager.getInstance(mContext);
mSecurityModel = new KeyguardSecurityModel(context);
mViewStateManager = new KeyguardViewStateManager(this);