summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk2
-rw-r--r--api/current.xml207
-rw-r--r--cmds/bootanimation/BootAnimation.cpp15
-rwxr-xr-xcore/java/android/animation/Animator.java39
-rw-r--r--core/java/android/app/ActivityManager.java6
-rw-r--r--core/java/android/app/Dialog.java10
-rw-r--r--core/java/android/appwidget/AppWidgetHost.java9
-rw-r--r--core/java/android/appwidget/AppWidgetHostView.java2
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java71
-rw-r--r--core/java/android/net/DownloadManager.java12
-rw-r--r--core/java/android/os/BatteryStats.java50
-rw-r--r--core/java/android/os/Build.java3
-rw-r--r--core/java/android/os/Looper.java3
-rw-r--r--core/java/android/os/Message.java7
-rw-r--r--core/java/android/preference/PreferenceActivity.java54
-rw-r--r--core/java/android/provider/BrowserContract.java383
-rw-r--r--core/java/android/provider/Downloads.java10
-rw-r--r--core/java/android/util/TimeUtils.java125
-rw-r--r--core/java/android/view/Display.java3
-rw-r--r--core/java/android/view/HardwareRenderer.java2
-rw-r--r--core/java/android/view/MenuInflater.java12
-rw-r--r--core/java/android/view/View.java14
-rw-r--r--core/java/android/view/ViewRoot.java2
-rw-r--r--core/java/android/widget/AdapterViewAnimator.java148
-rw-r--r--core/java/android/widget/AdapterViewFlipper.java38
-rw-r--r--core/java/android/widget/RemoteViews.java78
-rw-r--r--core/java/android/widget/RemoteViewsAdapter.java51
-rw-r--r--core/java/android/widget/RemoteViewsService.java22
-rw-r--r--core/java/android/widget/ScrollBarDrawable.java3
-rw-r--r--core/java/android/widget/StackView.java131
-rw-r--r--core/java/android/widget/TextView.java237
-rw-r--r--core/java/com/android/internal/app/ActionBarImpl.java3
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl4
-rw-r--r--core/java/com/android/internal/app/PlatLogoActivity.java14
-rw-r--r--core/java/com/android/internal/appwidget/IAppWidgetHost.aidl2
-rw-r--r--core/java/com/android/internal/appwidget/IAppWidgetService.aidl3
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java290
-rw-r--r--core/java/com/android/internal/view/StandaloneActionMode.java2
-rw-r--r--core/java/com/android/internal/view/menu/MenuBuilder.java13
-rw-r--r--core/java/com/android/internal/view/menu/MenuItemImpl.java3
-rw-r--r--core/java/com/android/internal/widget/ActionBarContextView.java6
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java26
-rw-r--r--core/java/com/android/internal/widget/IRemoteViewsFactory.aidl1
-rw-r--r--core/java/com/android/internal/widget/SlidingTab.java11
-rw-r--r--core/jni/Android.mk2
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android/graphics/AutoDecodeCancel.cpp100
-rw-r--r--core/jni/android/graphics/AutoDecodeCancel.h27
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp275
-rw-r--r--core/jni/android/graphics/BitmapFactory.h21
-rw-r--r--core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp10
-rw-r--r--core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h2
-rw-r--r--core/jni/android/graphics/Graphics.cpp56
-rw-r--r--core/jni/android/graphics/GraphicsJNI.h17
-rw-r--r--core/jni/android/graphics/LargeBitmap.cpp138
-rw-r--r--core/res/res/drawable-hdpi/cursor_controller.pngbin2886 -> 0 bytes
-rw-r--r--core/res/res/drawable-hdpi/selection_end_handle.pngbin2077 -> 0 bytes
-rw-r--r--core/res/res/drawable-hdpi/selection_start_handle.pngbin2107 -> 0 bytes
-rw-r--r--core/res/res/drawable-hdpi/text_select_handle.pngbin0 -> 6605 bytes
-rw-r--r--core/res/res/drawable-mdpi/cursor_controller.pngbin1622 -> 0 bytes
-rw-r--r--core/res/res/drawable-mdpi/selection_end_handle.pngbin1139 -> 0 bytes
-rw-r--r--core/res/res/drawable-mdpi/selection_start_handle.pngbin1199 -> 0 bytes
-rw-r--r--core/res/res/drawable-mdpi/text_select_handle.pngbin0 -> 6605 bytes
-rw-r--r--core/res/res/values-zh-rTW/strings.xml4
-rwxr-xr-xcore/res/res/values/attrs.xml7
-rw-r--r--core/res/res/values/colors.xml6
-rw-r--r--core/res/res/values/config.xml15
-rw-r--r--core/res/res/values/dimens.xml2
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/res/res/values/styles.xml13
-rw-r--r--core/res/res/values/themes.xml12
-rw-r--r--graphics/java/android/graphics/BitmapFactory.java143
-rw-r--r--graphics/java/android/graphics/LargeBitmap.java128
-rw-r--r--include/media/stagefright/MPEG4Writer.h6
-rw-r--r--include/media/stagefright/MetaData.h4
-rw-r--r--include/ui/EventHub.h1
-rw-r--r--libs/hwui/Matrix.cpp12
-rw-r--r--libs/hwui/OpenGLRenderer.cpp417
-rw-r--r--libs/hwui/OpenGLRenderer.h49
-rw-r--r--libs/hwui/Snapshot.h17
-rw-r--r--libs/hwui/TextDropShadowCache.h2
-rw-r--r--libs/rs/rsFont.cpp58
-rw-r--r--libs/rs/rsFont.h10
-rw-r--r--libs/rs/rsProgramVertex.cpp6
-rw-r--r--libs/rs/rsProgramVertex.h1
-rw-r--r--libs/rs/rsScriptC_LibGL.cpp8
-rw-r--r--libs/rs/scriptc/rs_core.rsh86
-rw-r--r--libs/rs/scriptc/rs_graphics.rsh5
-rw-r--r--libs/ui/EventHub.cpp15
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.cpp18
-rw-r--r--media/libstagefright/MPEG4Writer.cpp124
-rw-r--r--packages/DefaultContainerService/res/values-zh-rCN/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java4
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindow.java19
-rw-r--r--services/java/com/android/server/AppWidgetService.java35
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java31
-rw-r--r--services/java/com/android/server/PowerManagerService.java28
-rw-r--r--services/java/com/android/server/WindowManagerService.java4
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java101
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java12
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java18
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java37
-rwxr-xr-xservices/java/com/android/server/location/GpsLocationProvider.java8
-rw-r--r--services/jni/com_android_server_PowerManagerService.cpp27
-rw-r--r--services/jni/com_android_server_PowerManagerService.h2
-rw-r--r--services/surfaceflinger/TextureManager.cpp2
-rw-r--r--telephony/java/android/telephony/gsm/SmsMessage.java12
-rwxr-xr-xtelephony/java/com/android/internal/telephony/cdma/SmsMessage.java2
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java10
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java14
-rw-r--r--telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java194
-rw-r--r--tests/BatteryWaster/res/layout/battery_waster.xml12
-rw-r--r--tests/BatteryWaster/res/values/strings.xml2
-rw-r--r--tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java82
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml9
-rw-r--r--tests/HwAccelerationTest/res/layout/list_activity.xml25
-rw-r--r--tests/HwAccelerationTest/res/layout/stack.xml33
-rw-r--r--tests/HwAccelerationTest/res/layout/stack_item.xml38
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java27
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/StackActivity.java59
-rw-r--r--tools/aapt/Resource.cpp66
-rw-r--r--voip/java/android/net/sip/SipAudioCall.java5
-rw-r--r--voip/java/android/net/sip/SipAudioCallImpl.java21
123 files changed, 4043 insertions, 846 deletions
diff --git a/Android.mk b/Android.mk
index 65f7a35..39bbbdd 100644
--- a/Android.mk
+++ b/Android.mk
@@ -380,6 +380,8 @@ sample_dir := development/samples
# (see development/build/sdk.atree)
web_docs_sample_code_flags := \
-hdf android.hasSamples 1 \
+ -samplecode $(sample_dir)/AccessibilityService \
+ resources/samples/AccessibilityService "Accessibility Service" \
-samplecode $(sample_dir)/ApiDemos \
resources/samples/ApiDemos "API Demos" \
-samplecode $(sample_dir)/BackupRestore \
diff --git a/api/current.xml b/api/current.xml
index 6950d8c..dd39281 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -20079,6 +20079,17 @@
visibility="public"
>
</method>
+<method name="isRunning"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="removeUpdateListener"
return="void"
abstract="false"
@@ -20092,6 +20103,17 @@
<parameter name="listener" type="android.animation.Animator.AnimatorUpdateListener">
</parameter>
</method>
+<method name="reverse"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="setCurrentPlayTime"
return="void"
abstract="false"
@@ -26597,6 +26619,17 @@
visibility="public"
>
</method>
+<method name="invalidateOptionsMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isShowing"
return="boolean"
abstract="false"
@@ -36145,8 +36178,6 @@
>
<parameter name="appWidgetIds" type="int[]">
</parameter>
-<parameter name="views" type="android.widget.RemoteViews">
-</parameter>
<parameter name="viewId" type="int">
</parameter>
</method>
@@ -36162,9 +36193,37 @@
>
<parameter name="appWidgetId" type="int">
</parameter>
+<parameter name="viewId" type="int">
+</parameter>
+</method>
+<method name="partiallyUpdateAppWidget"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="appWidgetIds" type="int[]">
+</parameter>
<parameter name="views" type="android.widget.RemoteViews">
</parameter>
-<parameter name="viewId" type="int">
+</method>
+<method name="partiallyUpdateAppWidget"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="appWidgetId" type="int">
+</parameter>
+<parameter name="views" type="android.widget.RemoteViews">
</parameter>
</method>
<method name="updateAppWidget"
@@ -103089,6 +103148,17 @@
visibility="public"
>
</field>
+<field name="ERROR_CANNOT_RESUME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1008"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ERROR_DEVICE_NOT_FOUND"
type="int"
transient="false"
@@ -128150,6 +128220,16 @@
visibility="public"
>
</field>
+<field name="SERIAL"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="TAGS"
type="java.lang.String"
transient="false"
@@ -138894,6 +138974,66 @@
visibility="public"
>
</constructor>
+<field name="fragment"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentArguments"
+ type="android.os.Bundle"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="icon"
+ type="android.graphics.drawable.Drawable"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="iconRes"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="summary"
+ type="java.lang.CharSequence"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="title"
+ type="java.lang.CharSequence"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="PreferenceCategory"
extends="android.preference.PreferenceGroup"
@@ -217349,6 +217489,30 @@
visibility="public"
>
</method>
+<method name="onRestoreInstanceState"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="state" type="android.os.Parcelable">
+</parameter>
+</method>
+<method name="onSaveInstanceState"
+ return="android.os.Parcelable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="setAdapter"
return="void"
abstract="false"
@@ -228190,6 +228354,32 @@
<parameter name="visibility" type="int">
</parameter>
</method>
+<method name="showNext"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+</method>
+<method name="showPrevious"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+</method>
<method name="writeToParcel"
return="void"
abstract="false"
@@ -228387,6 +228577,17 @@
visibility="public"
>
</method>
+<method name="onDataSetChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="onDestroy"
return="void"
abstract="true"
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 7a7f8ed..9fe1fb8 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -49,6 +49,9 @@
#include "BootAnimation.h"
+#define USER_BOOTANIMATION_FILE "/data/local/bootanimation.zip"
+#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
+
namespace android {
// ---------------------------------------------------------------------------
@@ -244,12 +247,12 @@ status_t BootAnimation::readyToRun() {
mFlingerSurfaceControl = control;
mFlingerSurface = s;
- mAndroidAnimation = false;
- status_t err = mZip.open("/data/local/bootanimation.zip");
- if (err != NO_ERROR) {
- err = mZip.open("/system/media/bootanimation.zip");
- if (err != NO_ERROR) {
- mAndroidAnimation = true;
+ mAndroidAnimation = true;
+ if ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) ||
+ (access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0)) {
+ if ((mZip.open(USER_BOOTANIMATION_FILE) != NO_ERROR) ||
+ (mZip.open(SYSTEM_BOOTANIMATION_FILE) != NO_ERROR)) {
+ mAndroidAnimation = false;
}
}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 1013e01..cd6531b 100755
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -422,7 +422,6 @@ public class Animator extends Animatable {
mEvaluator = (mValueType == int.class) ? sIntEvaluator :
(mValueType == double.class) ? sDoubleEvaluator : sFloatEvaluator;
}
- mPlayingBackwards = false;
mCurrentIteration = 0;
mInitialized = true;
}
@@ -790,7 +789,8 @@ public class Animator extends Animatable {
}
}
- public void start() {
+ private void start(boolean playBackwards) {
+ mPlayingBackwards = playBackwards;
mPlayingState = STOPPED;
sPendingAnimations.add(this);
if (sAnimationHandler == null) {
@@ -801,6 +801,12 @@ public class Animator extends Animatable {
sAnimationHandler.sendEmptyMessage(ANIMATION_START);
}
+ @Override
+ public void start() {
+ start(false);
+ }
+
+ @Override
public void cancel() {
if (mListeners != null) {
ArrayList<AnimatableListener> tmpListeners =
@@ -814,6 +820,7 @@ public class Animator extends Animatable {
mPlayingState = CANCELED;
}
+ @Override
public void end() {
// Just set the ENDED flag - this causes the animation to end the next time a frame
// is processed.
@@ -821,6 +828,33 @@ public class Animator extends Animatable {
}
/**
+ * Returns whether this Animator is currently running (having been started and not yet ended).
+ * @return Wehther the Animator is running.
+ */
+ public boolean isRunning() {
+ return mPlayingState == RUNNING;
+ }
+
+ /**
+ * Plays the Animator in reverse. If the animation is already running,
+ * it will stop itself and play backwards from the point reached when reverse was called.
+ * If the animation is not currently running, then it will start from the end and
+ * play backwards. This behavior is only set for the current animation; future playing
+ * of the animation will use the default behavior of playing forward.
+ */
+ public void reverse() {
+ mPlayingBackwards = !mPlayingBackwards;
+ if (mPlayingState == RUNNING) {
+ long currentTime = AnimationUtils.currentAnimationTimeMillis();
+ long currentPlayTime = currentTime - mStartTime;
+ long timeLeft = mDuration - currentPlayTime;
+ mStartTime = currentTime - timeLeft;
+ } else {
+ start(true);
+ }
+ }
+
+ /**
* Called internally to end an animation by removing it from the animations list. Must be
* called on the UI thread.
*/
@@ -892,7 +926,6 @@ public class Animator extends Animatable {
* <code>repeatCount</code> has been exceeded and the animation should be ended.
*/
private boolean animationFrame(long currentTime) {
-
boolean done = false;
if (mPlayingState == STOPPED) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d66e98b..d5741fc 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -365,7 +365,8 @@ public class ActivityManager {
/**
* The time when the service was first made active, either by someone
- * starting or binding to it.
+ * starting or binding to it. This
+ * is in units of {@link android.os.SystemClock#elapsedRealtime()}.
*/
public long activeSince;
@@ -387,7 +388,8 @@ public class ActivityManager {
/**
* The time when there was last activity in the service (either
- * explicit requests to start it or clients binding to it).
+ * explicit requests to start it or clients binding to it). This
+ * is in units of {@link android.os.SystemClock#uptimeMillis()}.
*/
public long lastActivityTime;
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index b4c138e..274a266 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -228,6 +228,9 @@ public class Dialog implements DialogInterface, Window.Callback,
public void show() {
if (mShowing) {
if (mDecor != null) {
+ if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
+ mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
+ }
mDecor.setVisibility(View.VISIBLE);
}
return;
@@ -792,6 +795,13 @@ public class Dialog implements DialogInterface, Window.Callback,
}
/**
+ * @see Activity#invalidateOptionsMenu()
+ */
+ public void invalidateOptionsMenu() {
+ mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
+ }
+
+ /**
* @see Activity#onCreateContextMenu(ContextMenu, View, ContextMenuInfo)
*/
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 6011eec..7730942 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -62,11 +62,10 @@ public class AppWidgetHost {
msg.sendToTarget();
}
- public void viewDataChanged(int appWidgetId, RemoteViews views, int viewId) {
+ public void viewDataChanged(int appWidgetId, int viewId) {
Message msg = mHandler.obtainMessage(HANDLE_VIEW_DATA_CHANGED);
msg.arg1 = appWidgetId;
msg.arg2 = viewId;
- msg.obj = views;
msg.sendToTarget();
}
}
@@ -87,7 +86,7 @@ public class AppWidgetHost {
break;
}
case HANDLE_VIEW_DATA_CHANGED: {
- viewDataChanged(msg.arg1, (RemoteViews) msg.obj, msg.arg2);
+ viewDataChanged(msg.arg1, msg.arg2);
break;
}
}
@@ -264,13 +263,13 @@ public class AppWidgetHost {
}
}
- void viewDataChanged(int appWidgetId, RemoteViews views, int viewId) {
+ void viewDataChanged(int appWidgetId, int viewId) {
AppWidgetHostView v;
synchronized (mViews) {
v = mViews.get(appWidgetId);
}
if (v != null) {
- v.viewDataChanged(views, viewId);
+ v.viewDataChanged(viewId);
}
}
}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 22f4266..4f8ee93 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -264,7 +264,7 @@ public class AppWidgetHostView extends FrameLayout {
* Process data-changed notifications for the specified view in the specified
* set of {@link RemoteViews} views.
*/
- void viewDataChanged(RemoteViews remoteViews, int viewId) {
+ void viewDataChanged(int viewId) {
View v = findViewById(viewId);
if ((v != null) && (v instanceof AdapterView<?>)) {
AdapterView<?> adapterView = (AdapterView<?>) v;
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 5ee721f..2a583c1 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -233,6 +233,10 @@ public class AppWidgetManager {
/**
* Set the RemoteViews to use for the specified appWidgetIds.
*
+ * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
+ * contain a complete representation of the widget. For performing partial widget updates, see
+ * {@link #partiallyUpdateAppWidget(int[], RemoteViews)}.
+ *
* <p>
* It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
* and outside of the handler.
@@ -253,6 +257,10 @@ public class AppWidgetManager {
/**
* Set the RemoteViews to use for the specified appWidgetId.
*
+ * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
+ * contain a complete representation of the widget. For performing partial widget updates, see
+ * {@link #partiallyUpdateAppWidget(int, RemoteViews)}.
+ *
* <p>
* It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
* and outside of the handler.
@@ -266,6 +274,59 @@ public class AppWidgetManager {
}
/**
+ * Perform an incremental update or command on the widget(s) specified by appWidgetIds.
+ *
+ * This update differs from {@link #updateAppWidget(int[], RemoteViews)} in that the
+ * RemoteViews object which is passed is understood to be an incomplete representation of the
+ * widget, and hence is not cached by the AppWidgetService. Note that because these updates are
+ * not cached, any state that they modify that is not restored by restoreInstanceState will not
+ * persist in the case that the widgets are restored using the cached version in
+ * AppWidgetService.
+ *
+ * Use with {@link RemoteViews#showNext(int)}, {@link RemoteViews#showPrevious(int)},
+ * {@link RemoteViews#setScrollPosition(int, int)} and similar commands.
+ *
+ * <p>
+ * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
+ * and outside of the handler.
+ * This method will only work when called from the uid that owns the AppWidget provider.
+ *
+ * @param appWidgetIds The AppWidget instances for which to set the RemoteViews.
+ * @param views The RemoteViews object containing the incremental update / command.
+ */
+ public void partiallyUpdateAppWidget(int[] appWidgetIds, RemoteViews views) {
+ try {
+ sService.partiallyUpdateAppWidgetIds(appWidgetIds, views);
+ } catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
+ }
+
+ /**
+ * Perform an incremental update or command on the widget specified by appWidgetId.
+ *
+ * This update differs from {@link #updateAppWidget(int, RemoteViews)} in that the RemoteViews
+ * object which is passed is understood to be an incomplete representation of the widget, and
+ * hence is not cached by the AppWidgetService. Note that because these updates are not cached,
+ * any state that they modify that is not restored by restoreInstanceState will not persist in
+ * the case that the widgets are restored using the cached version in AppWidgetService.
+ *
+ * Use with {@link RemoteViews#showNext(int)}, {@link RemoteViews#showPrevious(int)},
+ * {@link RemoteViews#setScrollPosition(int, int)} and similar commands.
+ *
+ * <p>
+ * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
+ * and outside of the handler.
+ * This method will only work when called from the uid that owns the AppWidget provider.
+ *
+ * @param appWidgetId The AppWidget instance for which to set the RemoteViews.
+ * @param views The RemoteViews object containing the incremental update / command.
+ */
+ public void partiallyUpdateAppWidget(int appWidgetId, RemoteViews views) {
+ partiallyUpdateAppWidget(new int[] { appWidgetId }, views);
+ }
+
+ /**
* Set the RemoteViews to use for all AppWidget instances for the supplied AppWidget provider.
*
* <p>
@@ -292,12 +353,11 @@ public class AppWidgetManager {
* to invalidate their currently data.
*
* @param appWidgetIds The AppWidget instances for which to notify of view data changes.
- * @param views The RemoteViews which contains the view referenced at viewId.
* @param viewId The collection view id.
*/
- public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, RemoteViews views, int viewId) {
+ public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
try {
- sService.notifyAppWidgetViewDataChanged(appWidgetIds, views, viewId);
+ sService.notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -309,11 +369,10 @@ public class AppWidgetManager {
* to invalidate it's currently data.
*
* @param appWidgetId The AppWidget instance for which to notify of view data changes.
- * @param views The RemoteViews which contains the view referenced at viewId.
* @param viewId The collection view id.
*/
- public void notifyAppWidgetViewDataChanged(int appWidgetId, RemoteViews views, int viewId) {
- notifyAppWidgetViewDataChanged(new int[] { appWidgetId }, views, viewId);
+ public void notifyAppWidgetViewDataChanged(int appWidgetId, int viewId) {
+ notifyAppWidgetViewDataChanged(new int[] { appWidgetId }, viewId);
}
/**
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java
index e69c324..447e642 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/net/DownloadManager.java
@@ -185,6 +185,12 @@ public class DownloadManager {
public final static int ERROR_DEVICE_NOT_FOUND = 1007;
/**
+ * Value of {@link #COLUMN_ERROR_CODE} when some possibly transient error occurred but we can't
+ * resume the download.
+ */
+ public final static int ERROR_CANNOT_RESUME = 1008;
+
+ /**
* Broadcast intent action sent by the download manager when a download completes.
*/
public final static String ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE";
@@ -715,7 +721,8 @@ public class DownloadManager {
if (translateStatus(status) != STATUS_FAILED) {
return 0; // arbitrary value when status is not an error
}
- if ((400 <= status && status < 490) || (500 <= status && status < 600)) {
+ if ((400 <= status && status < Downloads.Impl.MIN_ARTIFICIAL_ERROR_STATUS)
+ || (500 <= status && status < 600)) {
// HTTP status code
return status;
}
@@ -740,6 +747,9 @@ public class DownloadManager {
case Downloads.STATUS_DEVICE_NOT_FOUND_ERROR:
return ERROR_DEVICE_NOT_FOUND;
+ case Downloads.Impl.STATUS_CANNOT_RESUME:
+ return ERROR_CANNOT_RESUME;
+
default:
return ERROR_UNKNOWN;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index a699388..95f217f 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -23,6 +23,7 @@ import java.util.Map;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
+import android.util.TimeUtils;
/**
* A class providing access to battery usage statistics, including information on
@@ -290,6 +291,11 @@ public abstract class BatteryStats implements Parcelable {
*/
public static abstract class Proc {
+ public static class ExcessiveWake {
+ public long overTime;
+ public long usedTime;
+ }
+
/**
* Returns the total time (in 1/100 sec) spent executing in user code.
*
@@ -326,6 +332,10 @@ public abstract class BatteryStats implements Parcelable {
* @see BatteryStats#getCpuSpeedSteps()
*/
public abstract long getTimeAtCpuSpeedStep(int speedStep, int which);
+
+ public abstract int countExcessiveWakes();
+
+ public abstract ExcessiveWake getExcessiveWake(int i);
}
/**
@@ -421,6 +431,8 @@ public abstract class BatteryStats implements Parcelable {
public static final int STATE_BLUETOOTH_ON_FLAG = 1<<20;
public static final int STATE_AUDIO_ON_FLAG = 1<<19;
public static final int STATE_VIDEO_ON_FLAG = 1<<18;
+ public static final int STATE_WAKE_LOCK_FLAG = 1<<17;
+ public static final int STATE_SENSOR_ON_FLAG = 1<<16;
public int states;
@@ -470,6 +482,16 @@ public abstract class BatteryStats implements Parcelable {
batteryVoltage = o.batteryVoltage;
states = o.states;
}
+
+ public boolean same(HistoryItem o) {
+ return batteryLevel == o.batteryLevel
+ && batteryStatus == o.batteryStatus
+ && batteryHealth == o.batteryHealth
+ && batteryPlugType == o.batteryPlugType
+ && batteryTemperature == o.batteryTemperature
+ && batteryVoltage == o.batteryVoltage
+ && states == o.states;
+ }
}
public static final class BitDescription {
@@ -633,6 +655,8 @@ public abstract class BatteryStats implements Parcelable {
new BitDescription(HistoryItem.STATE_BLUETOOTH_ON_FLAG, "bluetooth"),
new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio"),
new BitDescription(HistoryItem.STATE_VIDEO_ON_FLAG, "video"),
+ new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock"),
+ new BitDescription(HistoryItem.STATE_SENSOR_ON_FLAG, "sensor"),
new BitDescription(HistoryItem.STATE_BRIGHTNESS_MASK,
HistoryItem.STATE_BRIGHTNESS_SHIFT, "brightness",
SCREEN_BRIGHTNESS_NAMES),
@@ -1376,7 +1400,6 @@ public abstract class BatteryStats implements Parcelable {
pw.println(getDischargeStartLevel());
pw.print(prefix); pw.print(" Discharge cycle current level: ");
pw.println(getDischargeCurrentLevel());
- } else {
pw.print(prefix); pw.println(" Device is currently plugged into power");
pw.print(prefix); pw.print(" Last discharge cycle start level: ");
pw.println(getDischargeStartLevel());
@@ -1384,6 +1407,13 @@ public abstract class BatteryStats implements Parcelable {
pw.println(getDischargeCurrentLevel());
}
pw.println(" ");
+ } else {
+ pw.print(prefix); pw.println(" Device battery use since last full charge");
+ pw.print(prefix); pw.print(" Amount discharged (lower bound): ");
+ pw.println(getLowDischargeAmountSinceCharge());
+ pw.print(prefix); pw.print(" Amount discharged (upper bound): ");
+ pw.println(getHighDischargeAmountSinceCharge());
+ pw.println(" ");
}
@@ -1524,12 +1554,16 @@ public abstract class BatteryStats implements Parcelable {
long userTime;
long systemTime;
int starts;
+ int numExcessive;
userTime = ps.getUserTime(which);
systemTime = ps.getSystemTime(which);
starts = ps.getStarts(which);
+ numExcessive = which == STATS_SINCE_CHARGED
+ ? ps.countExcessiveWakes() : 0;
- if (userTime != 0 || systemTime != 0 || starts != 0) {
+ if (userTime != 0 || systemTime != 0 || starts != 0
+ || numExcessive != 0) {
sb.setLength(0);
sb.append(prefix); sb.append(" Proc ");
sb.append(ent.getKey()); sb.append(":\n");
@@ -1539,6 +1573,18 @@ public abstract class BatteryStats implements Parcelable {
sb.append(prefix); sb.append(" "); sb.append(starts);
sb.append(" proc starts");
pw.println(sb.toString());
+ for (int e=0; e<numExcessive; e++) {
+ Uid.Proc.ExcessiveWake ew = ps.getExcessiveWake(e);
+ if (ew != null) {
+ pw.print(prefix); pw.print(" * Killed for wake lock use: ");
+ TimeUtils.formatDuration(ew.usedTime, pw);
+ pw.print(" over ");
+ TimeUtils.formatDuration(ew.overTime, pw);
+ pw.print(" (");
+ pw.print((ew.usedTime*100)/ew.overTime);
+ pw.println("%)");
+ }
+ }
uidActivity = true;
}
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 78ec638..8624467 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -62,6 +62,9 @@ public class Build {
/** The name of the hardware (from the kernel command line or /proc). */
public static final String HARDWARE = getString("ro.hardware");
+ /** A hardware serial number, if available. Alphanumeric only, case-insensitive. */
+ public static final String SERIAL = getString("ro.serialno");
+
/** Various version strings. */
public static class VERSION {
/**
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index a9d7342..d360140 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -192,10 +192,11 @@ public class Looper {
pw.println(prefix + "mQueue=" + ((mQueue != null) ? mQueue : "(null"));
if (mQueue != null) {
synchronized (mQueue) {
+ long now = SystemClock.uptimeMillis();
Message msg = mQueue.mMessages;
int n = 0;
while (msg != null) {
- pw.println(prefix + " Message " + n + ": " + msg);
+ pw.println(prefix + " Message " + n + ": " + msg.toString(now));
n++;
msg = msg.next;
}
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 476da1d..49b72fe 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -19,6 +19,7 @@ package android.os;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.TimeUtils;
/**
*
@@ -366,13 +367,17 @@ public final class Message implements Parcelable {
}
public String toString() {
+ return toString(SystemClock.uptimeMillis());
+ }
+
+ String toString(long now) {
StringBuilder b = new StringBuilder();
b.append("{ what=");
b.append(what);
b.append(" when=");
- b.append(when);
+ TimeUtils.formatDuration(when-now, b);
if (arg1 != 0) {
b.append(" arg1=");
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 6b00690..03fc399 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -194,13 +194,13 @@ public abstract class PreferenceActivity extends ListActivity implements
}
};
- private class HeaderViewHolder {
- ImageView icon;
- TextView title;
- TextView summary;
- }
+ private static class HeaderAdapter extends ArrayAdapter<Header> {
+ private static class HeaderViewHolder {
+ ImageView icon;
+ TextView title;
+ TextView summary;
+ }
- private class HeaderAdapter extends ArrayAdapter<Header> {
private LayoutInflater mInflater;
public HeaderAdapter(Context context, List<Header> objects) {
@@ -217,23 +217,31 @@ public abstract class PreferenceActivity extends ListActivity implements
view = mInflater.inflate(com.android.internal.R.layout.preference_list_item,
parent, false);
holder = new HeaderViewHolder();
- holder.icon = (ImageView)view.findViewById(
- com.android.internal.R.id.icon);
- holder.title = (TextView)view.findViewById(
- com.android.internal.R.id.title);
- holder.summary = (TextView)view.findViewById(
- com.android.internal.R.id.summary);
+ holder.icon = (ImageView) view.findViewById(com.android.internal.R.id.icon);
+ holder.title = (TextView) view.findViewById(com.android.internal.R.id.title);
+ holder.summary = (TextView) view.findViewById(com.android.internal.R.id.summary);
view.setTag(holder);
} else {
view = convertView;
- holder = (HeaderViewHolder)view.getTag();
+ holder = (HeaderViewHolder) view.getTag();
}
+ // All view fields must be updated every time, because the view may be recycled
Header header = getItem(position);
- if (header.icon != null) holder.icon.setImageDrawable(header.icon);
- else if (header.iconRes != 0) holder.icon.setImageResource(header.iconRes);
- if (header.title != null) holder.title.setText(header.title);
- if (header.summary != null) holder.summary.setText(header.summary);
+ if (header.icon == null) {
+ holder.icon.setImageDrawable(null);
+ holder.icon.setImageResource(header.iconRes);
+ } else {
+ holder.icon.setImageResource(0);
+ holder.icon.setImageDrawable(header.icon);
+ }
+ holder.title.setText(header.title);
+ if (TextUtils.isEmpty(header.summary)) {
+ holder.summary.setVisibility(View.GONE);
+ } else {
+ holder.summary.setVisibility(View.VISIBLE);
+ holder.summary.setText(header.summary);
+ }
return view;
}
@@ -247,38 +255,38 @@ public abstract class PreferenceActivity extends ListActivity implements
* Title of the header that is shown to the user.
* @attr ref android.R.styleable#PreferenceHeader_title
*/
- CharSequence title;
+ public CharSequence title;
/**
* Optional summary describing what this header controls.
* @attr ref android.R.styleable#PreferenceHeader_summary
*/
- CharSequence summary;
+ public CharSequence summary;
/**
* Optional icon resource to show for this header.
* @attr ref android.R.styleable#PreferenceHeader_icon
*/
- int iconRes;
+ public int iconRes;
/**
* Optional icon drawable to show for this header. (If this is non-null,
* the iconRes will be ignored.)
*/
- Drawable icon;
+ public Drawable icon;
/**
* Full class name of the fragment to display when this header is
* selected.
* @attr ref android.R.styleable#PreferenceHeader_fragment
*/
- String fragment;
+ public String fragment;
/**
* Optional arguments to supply to the fragment when it is
* instantiated.
*/
- Bundle fragmentArguments;
+ public Bundle fragmentArguments;
}
@Override
diff --git a/core/java/android/provider/BrowserContract.java b/core/java/android/provider/BrowserContract.java
new file mode 100644
index 0000000..37f1ff2
--- /dev/null
+++ b/core/java/android/provider/BrowserContract.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.provider;
+
+import android.accounts.Account;
+import android.content.ContentProviderClient;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.provider.SyncStateContract;
+import android.util.Pair;
+
+/**
+ * @hide
+ */
+public class BrowserContract {
+ /** The authority for the browser provider */
+ public static final String AUTHORITY = "com.android.browser";
+
+ /** A content:// style uri to the authority for the browser provider */
+ public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+ /**
+ * An optional insert, update or delete URI parameter that allows the caller
+ * to specify that it is a sync adapter. The default value is false. If true
+ * the dirty flag is not automatically set and the "syncToNetwork" parameter
+ * is set to false when calling
+ * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}.
+ */
+ public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
+
+ /**
+ * Generic columns for use by sync adapters. The specific functions of
+ * these columns are private to the sync adapter. Other clients of the API
+ * should not attempt to either read or write these columns.
+ */
+ interface BaseSyncColumns {
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC1 = "sync1";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC2 = "sync2";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC3 = "sync3";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC4 = "sync4";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC5 = "sync5";
+ }
+
+ /**
+ * Convenience definitions for use in implementing chrome bookmarks sync in the Bookmarks table.
+ */
+ public static final class ChromeSyncColumns {
+ private ChromeSyncColumns() {}
+
+ /** The server unique ID for an item */
+ public static final String SERVER_UNIQUE = BaseSyncColumns.SYNC3;
+
+ public static final String FOLDER_NAME_ROOT = "google_chrome";
+ public static final String FOLDER_NAME_BOOKMARKS = "google_chrome_bookmarks";
+ public static final String FOLDER_NAME_BOOKMARKS_BAR = "bookmark_bar";
+ public static final String FOLDER_NAME_OTHER_BOOKMARKS = "other_bookmarks";
+
+ /** The client unique ID for an item */
+ public static final String CLIENT_UNIQUE = BaseSyncColumns.SYNC4;
+ }
+
+ /**
+ * Columns that appear when each row of a table belongs to a specific
+ * account, including sync information that an account may need.
+ */
+ interface SyncColumns extends BaseSyncColumns {
+ /**
+ * The name of the account instance to which this row belongs, which when paired with
+ * {@link #ACCOUNT_TYPE} identifies a specific account.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ACCOUNT_NAME = "account_name";
+
+ /**
+ * The type of account to which this row belongs, which when paired with
+ * {@link #ACCOUNT_NAME} identifies a specific account.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ACCOUNT_TYPE = "account_type";
+
+ /**
+ * String that uniquely identifies this row to its source account.
+ * <P>Type: TEXT</P>
+ */
+ public static final String SOURCE_ID = "sourceid";
+
+ /**
+ * Version number that is updated whenever this row or its related data
+ * changes.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String VERSION = "version";
+
+ /**
+ * Flag indicating that {@link #VERSION} has changed, and this row needs
+ * to be synchronized by its owning account.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String DIRTY = "dirty";
+ }
+
+ interface BookmarkColumns {
+ /**
+ * The unique ID for a row.
+ * <P>Type: INTEGER (long)</P>
+ */
+ public static final String _ID = "_id";
+
+ /**
+ * The URL of the bookmark.
+ * <P>Type: TEXT (URL)</P>
+ */
+ public static final String URL = "url";
+
+ /**
+ * The user visible title of the bookmark.
+ * <P>Type: TEXT</P>
+ */
+ public static final String TITLE = "title";
+
+ /**
+ * The favicon of the bookmark, may be NULL.
+ * Must decode via {@link BitmapFactory#decodeByteArray}.
+ * <p>Type: BLOB (image)</p>
+ */
+ public static final String FAVICON = "favicon";
+
+ /**
+ * A thumbnail of the page,may be NULL.
+ * Must decode via {@link BitmapFactory#decodeByteArray}.
+ * <p>Type: BLOB (image)</p>
+ */
+ public static final String THUMBNAIL = "thumbnail";
+
+ /**
+ * The touch icon for the web page, may be NULL.
+ * Must decode via {@link BitmapFactory#decodeByteArray}.
+ * <p>Type: BLOB (image)</p>
+ * @hide
+ */
+ public static final String TOUCH_ICON = "touch_icon";
+ }
+
+ /**
+ * The bookmarks table, which holds the user's browser bookmarks.
+ */
+ public static final class Bookmarks implements BookmarkColumns, SyncColumns {
+ /**
+ * This utility class cannot be instantiated.
+ */
+ private Bookmarks() {}
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "bookmarks");
+
+ /**
+ * The content:// style URI for the default folder
+ */
+ public static final Uri CONTENT_URI_DEFAULT_FOLDER =
+ Uri.withAppendedPath(CONTENT_URI, "folder");
+
+ /**
+ * Builds a URI that points to a specific folder.
+ * @param folderId the ID of the folder to point to
+ */
+ public static final Uri buildFolderUri(long folderId) {
+ return ContentUris.withAppendedId(CONTENT_URI_DEFAULT_FOLDER, folderId);
+ }
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of bookmarks.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/bookmark";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} of a single bookmark.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/bookmark";
+
+ /**
+ * Query parameter to use if you want to see deleted bookmarks that are still
+ * around on the device and haven't been synced yet.
+ * @see #IS_DELETED
+ */
+ public static final String QUERY_PARAMETER_SHOW_DELETED = "show_deleted";
+
+ /**
+ * Flag indicating if an item is a folder or bookmark. Non-zero values indicate
+ * a folder and zero indicates a bookmark.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String IS_FOLDER = "folder";
+
+ /**
+ * The ID of the parent folder. ID 0 is the root folder.
+ * <P>Type: INTEGER (reference to item in the same table)</P>
+ */
+ public static final String PARENT = "parent";
+
+ /**
+ * The position of the bookmark in relation to it's siblings that share the same
+ * {@link #PARENT}. May be negative.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String POSITION = "position";
+
+ /**
+ * The item that the bookmark should be inserted after.
+ * May be negative.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String INSERT_AFTER = "insert_after";
+
+ /**
+ * A flag to indicate if an item has been deleted. Queries will not return deleted
+ * entries unless you add the {@link #QUERY_PARAMETER_SHOW_DELETED} query paramter
+ * to the URI when performing your query.
+ * <p>Type: INTEGER (non-zero if the item has been deleted, zero if it hasn't)
+ * @see #QUERY_PARAMETER_SHOW_DELETED
+ */
+ public static final String IS_DELETED = "deleted";
+ }
+
+ /**
+ * The history table, which holds the browsing history.
+ */
+ public static final class History implements BookmarkColumns {
+ /**
+ * This utility class cannot be instantiated.
+ */
+ private History() {}
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "history");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of browser history items.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/browser-history";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} of a single browser history item.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/browser-history";
+
+ /**
+ * The date the item was last visited, in milliseconds since the epoch.
+ * <p>Type: INTEGER (date in milliseconds since January 1, 1970)</p>
+ */
+ public static final String DATE_LAST_VISITED = "date";
+
+ /**
+ * The date the item created, in milliseconds since the epoch.
+ * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p>
+ */
+ public static final String DATE_CREATED = "created";
+
+ /**
+ * The number of times the item has been visited.
+ * <p>Type: INTEGER</p>
+ */
+ public static final String VISITS = "visits";
+ }
+
+ /**
+ * The search history table.
+ * @hide
+ */
+ public static final class Searches {
+ private Searches() {}
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "searches");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of browser search items.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/searches";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} of a single browser search item.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/searches";
+
+ /**
+ * The unique ID for a row.
+ * <P>Type: INTEGER (long)</P>
+ */
+ public static final String _ID = "_id";
+
+ /**
+ * The user entered search term.
+ */
+ public static final String SEARCH = "search";
+
+ /**
+ * The date the search was performed, in milliseconds since the epoch.
+ * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p>
+ */
+ public static final String DATE = "date";
+ }
+
+ /**
+ * A table provided for sync adapters to use for storing private sync state data.
+ *
+ * @see SyncStateContract
+ */
+ public static final class SyncState implements SyncStateContract.Columns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private SyncState() {}
+
+ public static final String CONTENT_DIRECTORY =
+ SyncStateContract.Constants.CONTENT_DIRECTORY;
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI =
+ Uri.withAppendedPath(AUTHORITY_URI, CONTENT_DIRECTORY);
+
+ /**
+ * @see android.provider.SyncStateContract.Helpers#get
+ */
+ public static byte[] get(ContentProviderClient provider, Account account)
+ throws RemoteException {
+ return SyncStateContract.Helpers.get(provider, CONTENT_URI, account);
+ }
+
+ /**
+ * @see android.provider.SyncStateContract.Helpers#get
+ */
+ public static Pair<Uri, byte[]> getWithUri(ContentProviderClient provider, Account account)
+ throws RemoteException {
+ return SyncStateContract.Helpers.getWithUri(provider, CONTENT_URI, account);
+ }
+
+ /**
+ * @see android.provider.SyncStateContract.Helpers#set
+ */
+ public static void set(ContentProviderClient provider, Account account, byte[] data)
+ throws RemoteException {
+ SyncStateContract.Helpers.set(provider, CONTENT_URI, account, data);
+ }
+
+ /**
+ * @see android.provider.SyncStateContract.Helpers#newSetOperation
+ */
+ public static ContentProviderOperation newSetOperation(Account account, byte[] data) {
+ return SyncStateContract.Helpers.newSetOperation(CONTENT_URI, account, data);
+ }
+ }
+}
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index c9b5512..6bf0d5b 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -1060,6 +1060,16 @@ public final class Downloads {
public static final int STATUS_PRECONDITION_FAILED = 412;
/**
+ * The lowest-valued error status that is not an actual HTTP status code.
+ */
+ public static final int MIN_ARTIFICIAL_ERROR_STATUS = 489;
+
+ /**
+ * Some possibly transient error occurred, but we can't resume the download.
+ */
+ public static final int STATUS_CANNOT_RESUME = 489;
+
+ /**
* This download was canceled
*/
public static final int STATUS_CANCELED = 490;
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 0fc70d5..b01a71d 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -24,6 +24,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.PrintWriter;
import java.util.TimeZone;
import java.util.Date;
@@ -130,4 +131,128 @@ public class TimeUtils {
public static String getTimeZoneDatabaseVersion() {
return ZoneInfoDB.getVersion();
}
+
+ private static final int SECONDS_PER_MINUTE = 60;
+ private static final int SECONDS_PER_HOUR = 60 * 60;
+ private static final int SECONDS_PER_DAY = 24 * 60 * 60;
+
+ /** @hide Just for debugging; not internationalized. */
+ public static void formatDuration(long duration, StringBuilder builder) {
+ if (duration == 0) {
+ builder.append("0");
+ return;
+ }
+ if (duration > 0) {
+ builder.append("+");
+ } else {
+ builder.append("-");
+ duration = -duration;
+ }
+
+ int millis = (int)(duration%1000);
+ int seconds = (int) Math.floor(duration / 1000);
+ int days = 0, hours = 0, minutes = 0;
+
+ if (seconds > SECONDS_PER_DAY) {
+ days = seconds / SECONDS_PER_DAY;
+ seconds -= days * SECONDS_PER_DAY;
+ }
+ if (seconds > SECONDS_PER_HOUR) {
+ hours = seconds / SECONDS_PER_HOUR;
+ seconds -= hours * SECONDS_PER_HOUR;
+ }
+ if (seconds > SECONDS_PER_MINUTE) {
+ minutes = seconds / SECONDS_PER_MINUTE;
+ seconds -= minutes * SECONDS_PER_MINUTE;
+ }
+
+ boolean doall = false;
+ if (days > 0) {
+ builder.append(days);
+ builder.append('d');
+ doall = true;
+ }
+ if (doall || hours > 0) {
+ builder.append(hours);
+ builder.append('h');
+ doall = true;
+ }
+ if (doall || minutes > 0) {
+ builder.append(minutes);
+ builder.append('m');
+ doall = true;
+ }
+ if (doall || seconds > 0) {
+ builder.append(seconds);
+ builder.append('s');
+ doall = true;
+ }
+ builder.append(millis);
+ builder.append("ms");
+ }
+
+ /** @hide Just for debugging; not internationalized. */
+ public static void formatDuration(long duration, PrintWriter pw) {
+ if (duration == 0) {
+ pw.print("0");
+ return;
+ }
+ if (duration > 0) {
+ pw.print("+");
+ } else {
+ pw.print("-");
+ duration = -duration;
+ }
+
+ int millis = (int)(duration%1000);
+ int seconds = (int) Math.floor(duration / 1000);
+ int days = 0, hours = 0, minutes = 0;
+
+ if (seconds > SECONDS_PER_DAY) {
+ days = seconds / SECONDS_PER_DAY;
+ seconds -= days * SECONDS_PER_DAY;
+ }
+ if (seconds > SECONDS_PER_HOUR) {
+ hours = seconds / SECONDS_PER_HOUR;
+ seconds -= hours * SECONDS_PER_HOUR;
+ }
+ if (seconds > SECONDS_PER_MINUTE) {
+ minutes = seconds / SECONDS_PER_MINUTE;
+ seconds -= minutes * SECONDS_PER_MINUTE;
+ }
+
+ boolean doall = false;
+ if (days > 0) {
+ pw.print(days);
+ pw.print('d');
+ doall = true;
+ }
+ if (doall || hours > 0) {
+ pw.print(hours);
+ pw.print('h');
+ doall = true;
+ }
+ if (doall || minutes > 0) {
+ pw.print(minutes);
+ pw.print('m');
+ doall = true;
+ }
+ if (doall || seconds > 0) {
+ pw.print(seconds);
+ pw.print('s');
+ doall = true;
+ }
+ pw.print(millis);
+ pw.print("ms");
+ }
+
+
+ /** @hide Just for debugging; not internationalized. */
+ public static void formatDuration(long time, long now, PrintWriter pw) {
+ if (time == 0) {
+ pw.print("--");
+ return;
+ }
+ formatDuration(time-now, pw);
+ }
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index fabe5c8..34d7935 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -27,7 +27,8 @@ public class Display
/**
- * Use the WindowManager interface to create a Display object.
+ * Use {@link android.view.WindowManager#getDefaultDisplay()
+ * WindowManager.getDefaultDisplay()} to create a Display object.
* Display gives you access to some information about a particular display
* connected to the device.
*/
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 44bd6d4..ca60a89 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -385,7 +385,7 @@ public abstract class HardwareRenderer {
onPreDraw();
Canvas canvas = mCanvas;
- int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ int saveCount = canvas.save();
canvas.translate(0, -yOffset);
try {
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index a959e0d..d0985d9 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -246,8 +246,9 @@ public class MenuInflater {
* - 0: never
* - 1: ifRoom
* - 2: always
+ * - -1: Safe sentinel for "no value".
*/
- private int itemShowAsAction = MenuItem.SHOW_AS_ACTION_NEVER;
+ private int itemShowAsAction;
private String itemListenerMethodName;
@@ -322,7 +323,7 @@ public class MenuInflater {
itemChecked = a.getBoolean(com.android.internal.R.styleable.MenuItem_checked, defaultItemChecked);
itemVisible = a.getBoolean(com.android.internal.R.styleable.MenuItem_visible, groupVisible);
itemEnabled = a.getBoolean(com.android.internal.R.styleable.MenuItem_enabled, groupEnabled);
- itemShowAsAction = a.getInt(com.android.internal.R.styleable.MenuItem_showAsAction, 0);
+ itemShowAsAction = a.getInt(com.android.internal.R.styleable.MenuItem_showAsAction, -1);
itemListenerMethodName = a.getString(com.android.internal.R.styleable.MenuItem_onClick);
a.recycle();
@@ -346,8 +347,11 @@ public class MenuInflater {
.setTitleCondensed(itemTitleCondensed)
.setIcon(itemIconResId)
.setAlphabeticShortcut(itemAlphabeticShortcut)
- .setNumericShortcut(itemNumericShortcut)
- .setShowAsAction(itemShowAsAction);
+ .setNumericShortcut(itemNumericShortcut);
+
+ if (itemShowAsAction >= 0) {
+ item.setShowAsAction(itemShowAsAction);
+ }
if (itemListenerMethodName != null) {
if (mContext.isRestricted()) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 570793b..a0e840d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6630,9 +6630,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @see android.widget.ScrollBarDrawable
* @hide
*/
- protected void onDrawHorizontalScrollBar(Canvas canvas,
- Drawable scrollBar,
- int l, int t, int r, int b) {
+ protected void onDrawHorizontalScrollBar(Canvas canvas, Drawable scrollBar,
+ int l, int t, int r, int b) {
scrollBar.setBounds(l, t, r, b);
scrollBar.draw(canvas);
}
@@ -6651,9 +6650,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @see android.widget.ScrollBarDrawable
* @hide
*/
- protected void onDrawVerticalScrollBar(Canvas canvas,
- Drawable scrollBar,
- int l, int t, int r, int b) {
+ protected void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
+ int l, int t, int r, int b) {
scrollBar.setBounds(l, t, r, b);
scrollBar.draw(canvas);
}
@@ -10018,8 +10016,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
public final Interpolator scrollBarInterpolator = new Interpolator(1, 2);
- private final float[] mOpaque = {255.0f};
- private final float[] mTransparent = {0.0f};
+ private final float[] mOpaque = { 255.0f };
+ private final float[] mTransparent = { 0.0f };
/**
* When fading should start. This time moves into the future every time
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index faa4783..d32ccb1 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -1363,7 +1363,6 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
", metrics=" + cxt.getResources().getDisplayMetrics() +
", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
}
- int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
try {
canvas.translate(0, -yoff);
if (mTranslator != null) {
@@ -1374,7 +1373,6 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
mView.draw(canvas);
} finally {
mAttachInfo.mIgnoreDirtyState = false;
- canvas.restoreToCount(saveCount);
}
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index ef00d88..f11c5c6 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -17,8 +17,6 @@
package android.widget;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
import android.animation.PropertyAnimator;
import android.content.Context;
@@ -27,11 +25,11 @@ import android.content.res.TypedArray;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -98,15 +96,6 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
int mCurrentWindowStartUnbounded = 0;
/**
- * Indicates whether to treat the adapter to be a circular structure, ie.
- * the view before 0 is considered to be <code>mAdapter.getCount() - 1</code>
- *
- * TODO: this doesn't do anything yet
- *
- */
- boolean mCycleViews = false;
-
- /**
* Handler to post events to the main thread
*/
Handler mMainQueue;
@@ -132,6 +121,12 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
boolean mFirstTime = true;
/**
+ * Specifies if the animator should wrap from 0 to the end and vice versa
+ * or have hard boundaries at the beginning and end
+ */
+ boolean mShouldLoop = true;
+
+ /**
* TODO: Animation stuff is still in flux, waiting on the new framework to settle a bit.
*/
Animation mInAnimation;
@@ -140,7 +135,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
public AdapterViewAnimator(Context context) {
super(context);
- initViewAnimator(context, null);
+ initViewAnimator();
}
public AdapterViewAnimator(Context context, AttributeSet attrs) {
@@ -165,13 +160,13 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
a.recycle();
- initViewAnimator(context, attrs);
+ initViewAnimator();
}
/**
* Initialize this {@link AdapterViewAnimator}
*/
- private void initViewAnimator(Context context, AttributeSet attrs) {
+ private void initViewAnimator() {
mMainQueue = new Handler(Looper.myLooper());
mActiveViews = new View[mNumActiveViews];
mPreviousViews = new ArrayList<View>();
@@ -183,13 +178,15 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
* desired number of views, and specify the offset
*
* @param numVisibleViews The number of views the animator keeps in the {@link ViewGroup}
- * @param activeOffset This parameter specifies where the current index ({@link mWhichChild})
+ * @param activeOffset This parameter specifies where the current index ({@link #mWhichChild})
* sits within the window. For example if activeOffset is 1, and numVisibleViews is 3,
- * and {@link setDisplayedChild} is called with 10, then the effective window will be
- * the indexes 9, 10, and 11. In the same example, if activeOffset were 0, then the
+ * and {@link #setDisplayedChild(int)} is called with 10, then the effective window will
+ * be the indexes 9, 10, and 11. In the same example, if activeOffset were 0, then the
* window would instead contain indexes 10, 11 and 12.
+ * @param shouldLoop If the animator is show view 0, and setPrevious() is called, do we
+ * we loop back to the end, or do we do nothing
*/
- void configureViewAnimator(int numVisibleViews, int activeOffset) {
+ void configureViewAnimator(int numVisibleViews, int activeOffset, boolean shouldLoop) {
if (activeOffset > numVisibleViews - 1) {
// Throw an exception here.
}
@@ -200,6 +197,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
removeAllViewsInLayout();
mCurrentWindowStart = 0;
mCurrentWindowEnd = -1;
+ mShouldLoop = shouldLoop;
}
/**
@@ -215,6 +213,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
void animateViewForTransition(int fromIndex, int toIndex, View view) {
PropertyAnimator pa;
if (fromIndex == -1) {
+ view.setAlpha(0.0f);
pa = new PropertyAnimator(400, view, "alpha", 0.0f, 1.0f);
pa.start();
} else if (toIndex == -1) {
@@ -232,9 +231,9 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
if (mAdapter != null) {
mWhichChild = whichChild;
if (whichChild >= mAdapter.getCount()) {
- mWhichChild = 0;
+ mWhichChild = mShouldLoop ? 0 : mAdapter.getCount() - 1;
} else if (whichChild < 0) {
- mWhichChild = mAdapter.getCount() - 1;
+ mWhichChild = mShouldLoop ? mAdapter.getCount() - 1 : 0;
}
boolean hasFocus = getFocusedChild() != null;
@@ -325,9 +324,12 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
}
private LayoutParams createOrReuseLayoutParams(View v) {
- final LayoutParams currentLp = (LayoutParams) v.getLayoutParams();
+ final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
if (currentLp instanceof LayoutParams) {
- return currentLp;
+ LayoutParams lp = (LayoutParams) currentLp;
+ lp.setHorizontalOffset(0);
+ lp.setVerticalOffset(0);
+ return lp;
}
return new LayoutParams(v);
}
@@ -362,6 +364,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
int previousViewRelativeIndex = modulo(index - mCurrentWindowStart,
mNumActiveViews);
animateViewForTransition(previousViewRelativeIndex, -1, previousView);
+ mActiveViews[index] = null;
}
}
}
@@ -468,6 +471,66 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
mDataChanged = false;
}
+ static class SavedState extends BaseSavedState {
+ int whichChild;
+
+ /**
+ * Constructor called from {@link AdapterViewAnimator#onSaveInstanceState()}
+ */
+ SavedState(Parcelable superState, int whichChild) {
+ super(superState);
+ this.whichChild = whichChild;
+ }
+
+ /**
+ * Constructor called from {@link #CREATOR}
+ */
+ private SavedState(Parcel in) {
+ super(in);
+ whichChild = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(whichChild);
+ }
+
+ @Override
+ public String toString() {
+ return "AdapterViewAnimator.SavedState{ whichChild = " + whichChild + " }";
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR
+ = new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ return new SavedState(superState, mWhichChild);
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+
+ // Here we set mWhichChild in addition to setDisplayedChild
+ // We do the former in case mAdapter is null, and hence setDisplayedChild won't
+ // set mWhichChild
+ mWhichChild = ss.whichChild;
+ setDisplayedChild(mWhichChild);
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int count = getChildCount();
@@ -667,6 +730,24 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
}
}
+ private final Rect dirtyRect = new Rect();
+ @Override
+ public void removeViewInLayout(View view) {
+ // TODO: need to investigate this block a bit more
+ // and perhaps fix some other invalidations issues.
+ View parent = null;
+ view.setVisibility(INVISIBLE);
+ if (view.getLayoutParams() instanceof LayoutParams) {
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ parent = lp.getParentAndDirtyRegion(dirtyRect);
+ }
+
+ super.removeViewInLayout(view);
+
+ if (parent != null)
+ parent.invalidate(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
+ }
+
static class LayoutParams extends ViewGroup.LayoutParams {
int horizontalOffset;
int verticalOffset;
@@ -701,6 +782,25 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
p.invalidate(r.left, r.top, r.right, r.bottom);
}
+ public View getParentAndDirtyRegion(Rect globalRect) {
+ globalRect.set(mView.getLeft(), mView.getTop(), mView.getRight(), mView.getBottom());
+ View p = mView;
+ boolean firstPass = true;
+ parentRect.set(0, 0, 0, 0);
+ while (p.getParent() != null && p.getParent() instanceof View
+ && !parentRect.contains(globalRect)) {
+ if (!firstPass) {
+ globalRect.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY());
+ }
+
+ firstPass = false;
+ p = (View) p.getParent();
+ parentRect.set(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY(),
+ p.getRight() - p.getScrollX(), p.getBottom() - p.getScrollY());
+ }
+ return p;
+ }
+
private Rect invalidateRect = new Rect();
// This is public so that PropertyAnimator can access it
public void setVerticalOffset(int newVerticalOffset) {
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
index 901c761..895683d 100644
--- a/core/java/android/widget/AdapterViewFlipper.java
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -25,8 +25,8 @@ import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.RemotableViewMethod;
import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
import android.widget.RemoteViews.RemoteView;
/**
@@ -163,6 +163,40 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
}
/**
+ * {@inheritDoc}
+ */
+ @Override
+ @RemotableViewMethod
+ public void showNext() {
+ // if the flipper is currently flipping automatically, and showNext() is called
+ // we should we should make sure to reset the timer
+ if (mRunning) {
+ mHandler.removeMessages(FLIP_MSG);
+ Message msg = mHandler.obtainMessage(FLIP_MSG);
+ mHandler.sendMessageDelayed(msg, mFlipInterval);
+ }
+ super.showNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ @RemotableViewMethod
+ public void showPrevious() {
+ // if the flipper is currently flipping automatically, and showPrevious() is called
+ // we should we should make sure to reset the timer
+ if (mRunning) {
+ mHandler.removeMessages(FLIP_MSG);
+ Message msg = mHandler.obtainMessage(FLIP_MSG);
+ mHandler.sendMessageDelayed(msg, mFlipInterval);
+ }
+ super.showPrevious();
+ }
+
+ /**
+
+ /**
* Internal method to start or stop dispatching flip {@link Message} based
* on {@link #mRunning} and {@link #mVisible} state.
*/
@@ -229,8 +263,6 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
if (msg.what == FLIP_MSG) {
if (mRunning) {
showNext();
- msg = obtainMessage(FLIP_MSG);
- sendMessageDelayed(msg, mFlipInterval);
}
}
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 50745dc0..f23a723 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -266,6 +266,63 @@ public class RemoteViews implements Parcelable, Filter {
public final static int TAG = 3;
}
+ private class ReflectionActionWithoutParams extends Action {
+ int viewId;
+ String methodName;
+
+ public final static int TAG = 5;
+
+ ReflectionActionWithoutParams(int viewId, String methodName) {
+ this.viewId = viewId;
+ this.methodName = methodName;
+ }
+
+ ReflectionActionWithoutParams(Parcel in) {
+ this.viewId = in.readInt();
+ this.methodName = in.readString();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(TAG);
+ out.writeInt(this.viewId);
+ out.writeString(this.methodName);
+ }
+
+ @Override
+ public void apply(View root) {
+ final View view = root.findViewById(viewId);
+ if (view == null) {
+ throw new ActionException("can't find view: 0x" + Integer.toHexString(viewId));
+ }
+
+ Class klass = view.getClass();
+ Method method;
+ try {
+ method = klass.getMethod(this.methodName);
+ } catch (NoSuchMethodException ex) {
+ throw new ActionException("view: " + klass.getName() + " doesn't have method: "
+ + this.methodName + "()");
+ }
+
+ if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
+ throw new ActionException("view: " + klass.getName()
+ + " can't use method with RemoteViews: "
+ + this.methodName + "()");
+ }
+
+ try {
+ //noinspection ConstantIfStatement
+ if (false) {
+ Log.d("RemoteViews", "view: " + klass.getName() + " calling method: "
+ + this.methodName + "()");
+ }
+ method.invoke(view);
+ } catch (Exception ex) {
+ throw new ActionException(ex);
+ }
+ }
+ }
+
/**
* Base class for the reflection actions.
*/
@@ -571,6 +628,9 @@ public class RemoteViews implements Parcelable, Filter {
case ViewGroupAction.TAG:
mActions.add(new ViewGroupAction(parcel));
break;
+ case ReflectionActionWithoutParams.TAG:
+ mActions.add(new ReflectionActionWithoutParams(parcel));
+ break;
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -632,6 +692,24 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Equivalent to calling {@link AdapterViewFlipper#showNext()}
+ *
+ * @param viewId The id of the view on which to call {@link AdapterViewFlipper#showNext()}
+ */
+ public void showNext(int viewId) {
+ addAction(new ReflectionActionWithoutParams(viewId, "showNext"));
+ }
+
+ /**
+ * Equivalent to calling {@link AdapterViewFlipper#showPrevious()}
+ *
+ * @param viewId The id of the view on which to call {@link AdapterViewFlipper#showPrevious()}
+ */
+ public void showPrevious(int viewId) {
+ addAction(new ReflectionActionWithoutParams(viewId, "showPrevious"));
+ }
+
+ /**
* Equivalent to calling View.setVisibility
*
* @param viewId The id of the view whose visibility should change
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index ebf5d6e..cd1e422 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -202,6 +202,7 @@ public class RemoteViewsAdapter extends BaseAdapter {
int count;
int viewTypeCount;
boolean hasStableIds;
+ boolean isDataDirty;
Map<Integer, Integer> mTypeIdIndexMap;
RemoteViewsInfo() {
@@ -209,6 +210,7 @@ public class RemoteViewsAdapter extends BaseAdapter {
// by default there is at least one dummy view type
viewTypeCount = 1;
hasStableIds = true;
+ isDataDirty = false;
mTypeIdIndexMap = new HashMap<Integer, Integer>();
}
}
@@ -282,6 +284,39 @@ public class RemoteViewsAdapter extends BaseAdapter {
}
}
+ protected void onNotifyDataSetChanged() {
+ // we mark the data as dirty so that the next call to fetch views will result in
+ // an onDataSetDirty() call from the adapter
+ synchronized (mViewCacheInfo) {
+ mViewCacheInfo.isDataDirty = true;
+ }
+ }
+
+ private void updateNotifyDataSetChanged() {
+ // actually calls through to the factory to notify it to update
+ if (mServiceConnection.isConnected()) {
+ IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
+ try {
+ factory.onDataSetChanged();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ // re-request the new metadata (only after the notification to the factory)
+ requestMetaData();
+
+ // post a new runnable on the main thread to propagate the notification back
+ // to the base adapter
+ mMainQueue.post(new Runnable() {
+ @Override
+ public void run() {
+ completeNotifyDataSetChanged();
+ }
+ });
+ }
+
protected void updateRemoteViewsInfo(int position) {
if (mServiceConnection.isConnected()) {
IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
@@ -499,6 +534,16 @@ public class RemoteViewsAdapter extends BaseAdapter {
@Override
public void run() {
while (mBackgroundLoaderEnabled) {
+ // notify the RemoteViews factory if necessary
+ boolean isDataDirty = false;
+ synchronized (mViewCacheInfo) {
+ isDataDirty = mViewCacheInfo.isDataDirty;
+ mViewCacheInfo.isDataDirty = false;
+ }
+ if (isDataDirty) {
+ updateNotifyDataSetChanged();
+ }
+
int index = -1;
synchronized (mViewCacheLoadIndices) {
if (!mViewCacheLoadIndices.isEmpty()) {
@@ -668,6 +713,12 @@ public class RemoteViewsAdapter extends BaseAdapter {
public void notifyDataSetChanged() {
// flush the cache so that we can reload new items from the service
mViewCache.flushCache();
+
+ // notify the factory that it's data may no longer be valid
+ mViewCache.onNotifyDataSetChanged();
+ }
+
+ public void completeNotifyDataSetChanged() {
super.notifyDataSetChanged();
}
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index 7c9d7ff..584fa25 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -43,9 +43,27 @@ public abstract class RemoteViewsService extends Service {
* An interface for an adapter between a remote collection view (ListView, GridView, etc) and
* the underlying data for that view. The implementor is responsible for making a RemoteView
* for each item in the data set.
+ *
+ * @see android.widget.Adapter
+ * @see android.appwidget.AppWidgetManager
*/
public interface RemoteViewsFactory {
+ /**
+ * Called when your factory is first constructed. The same factory may be shared across
+ * multiple RemoteViewAdapters depending on the intent passed.
+ */
public void onCreate();
+ /**
+ * Called when notifyDataSetChanged() is triggered on the remote adapter. This allows a
+ * RemoteViewsFactory to respond to data changes by updating any internal references.
+ *
+ * @see android.appwidget.AppWidgetManager#notifyAppWidgetViewDataChanged(int[], int)
+ */
+ public void onDataSetChanged();
+ /**
+ * Called when the last RemoteViewsAdapter that is associated with this factory is
+ * unbound.
+ */
public void onDestroy();
public int getCount();
@@ -64,7 +82,9 @@ public abstract class RemoteViewsService extends Service {
public RemoteViewsFactoryAdapter(RemoteViewsFactory factory) {
mFactory = factory;
}
-
+ public void onDataSetChanged() {
+ mFactory.onDataSetChanged();
+ }
public int getCount() {
return mFactory.getCount();
}
diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java
index 3b113ae..93a1179 100644
--- a/core/java/android/widget/ScrollBarDrawable.java
+++ b/core/java/android/widget/ScrollBarDrawable.java
@@ -111,8 +111,7 @@ public class ScrollBarDrawable extends Drawable {
}
Rect r = getBounds();
- if (canvas.quickReject(r.left, r.top, r.right, r.bottom,
- Canvas.EdgeType.AA)) {
+ if (canvas.quickReject(r.left, r.top, r.right, r.bottom, Canvas.EdgeType.AA)) {
return;
}
if (drawTrack) {
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index e3aca6a..5797cbb 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -34,6 +34,7 @@ import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.animation.LinearInterpolator;
import android.widget.RemoteViews.RemoteView;
@RemoteView
@@ -47,22 +48,22 @@ public class StackView extends AdapterViewAnimator {
/**
* Default animation parameters
*/
- private final int DEFAULT_ANIMATION_DURATION = 400;
+ private final int DEFAULT_ANIMATION_DURATION = 500;
private final int MINIMUM_ANIMATION_DURATION = 50;
/**
* These specify the different gesture states
*/
- private final int GESTURE_NONE = 0;
- private final int GESTURE_SLIDE_UP = 1;
- private final int GESTURE_SLIDE_DOWN = 2;
+ private static final int GESTURE_NONE = 0;
+ private static final int GESTURE_SLIDE_UP = 1;
+ private static final int GESTURE_SLIDE_DOWN = 2;
/**
* Specifies how far you need to swipe (up or down) before it
* will be consider a completed gesture when you lift your finger
*/
- private final float SWIPE_THRESHOLD_RATIO = 0.35f;
- private final float SLIDE_UP_RATIO = 0.7f;
+ private static final float SWIPE_THRESHOLD_RATIO = 0.35f;
+ private static final float SLIDE_UP_RATIO = 0.7f;
private final WeakHashMap<View, Float> mRotations = new WeakHashMap<View, Float>();
private final WeakHashMap<View, Integer>
@@ -93,9 +94,6 @@ public class StackView extends AdapterViewAnimator {
private StackSlider mStackSlider;
private boolean mFirstLayoutHappened = false;
- // TODO: temp hack to get this thing started
- int mIndex = 5;
-
public StackView(Context context) {
super(context);
initStackView();
@@ -107,10 +105,10 @@ public class StackView extends AdapterViewAnimator {
}
private void initStackView() {
- configureViewAnimator(4, 2);
+ configureViewAnimator(4, 2, false);
setStaticTransformationsEnabled(true);
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
- mTouchSlop = configuration.getScaledTouchSlop();// + 5;
+ mTouchSlop = configuration.getScaledTouchSlop();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mActivePointerId = INVALID_POINTER;
@@ -120,7 +118,7 @@ public class StackView extends AdapterViewAnimator {
mStackSlider = new StackSlider();
if (!sPaintsInitialized) {
- initializePaints(getContext());
+ initializePaints();
}
}
@@ -133,6 +131,8 @@ public class StackView extends AdapterViewAnimator {
if (view.getAlpha() == 1) {
view.setAlpha(0);
}
+ view.setVisibility(VISIBLE);
+
PropertyAnimator fadeIn = new PropertyAnimator(DEFAULT_ANIMATION_DURATION,
view, "alpha", view.getAlpha(), 1.0f);
fadeIn.start();
@@ -142,7 +142,7 @@ public class StackView extends AdapterViewAnimator {
LayoutParams lp = (LayoutParams) view.getLayoutParams();
- int largestDuration = (int) Math.round(
+ int largestDuration = Math.round(
(lp.verticalOffset*1.0f/-mViewHeight)*DEFAULT_ANIMATION_DURATION);
int duration = largestDuration;
if (mYVelocity != 0) {
@@ -152,18 +152,20 @@ public class StackView extends AdapterViewAnimator {
duration = Math.min(duration, largestDuration);
duration = Math.max(duration, MINIMUM_ANIMATION_DURATION);
- PropertyAnimator slideInY = new PropertyAnimator(duration, mStackSlider,
+ StackSlider animationSlider = new StackSlider(mStackSlider);
+ PropertyAnimator slideInY = new PropertyAnimator(duration, animationSlider,
"YProgress", mStackSlider.getYProgress(), 0);
+ slideInY.setInterpolator(new LinearInterpolator());
slideInY.start();
- PropertyAnimator slideInX = new PropertyAnimator(duration, mStackSlider,
+ PropertyAnimator slideInX = new PropertyAnimator(duration, animationSlider,
"XProgress", mStackSlider.getXProgress(), 0);
+ slideInX.setInterpolator(new LinearInterpolator());
slideInX.start();
-
} else if (fromIndex == mNumActiveViews - 2 && toIndex == mNumActiveViews - 1) {
// Slide item out
LayoutParams lp = (LayoutParams) view.getLayoutParams();
- int largestDuration = (int) Math.round(mStackSlider.getYProgress()*DEFAULT_ANIMATION_DURATION);
+ int largestDuration = Math.round(mStackSlider.getYProgress()*DEFAULT_ANIMATION_DURATION);
int duration = largestDuration;
if (mYVelocity != 0) {
duration = 1000*(lp.verticalOffset + mViewHeight)/Math.abs(mYVelocity);
@@ -172,16 +174,21 @@ public class StackView extends AdapterViewAnimator {
duration = Math.min(duration, largestDuration);
duration = Math.max(duration, MINIMUM_ANIMATION_DURATION);
- PropertyAnimator slideOutY = new PropertyAnimator(duration, mStackSlider,
+ StackSlider animationSlider = new StackSlider(mStackSlider);
+ PropertyAnimator slideOutY = new PropertyAnimator(duration, animationSlider,
"YProgress", mStackSlider.getYProgress(), 1);
+ slideOutY.setInterpolator(new LinearInterpolator());
slideOutY.start();
- PropertyAnimator slideOutX = new PropertyAnimator(duration, mStackSlider,
+ PropertyAnimator slideOutX = new PropertyAnimator(duration, animationSlider,
"XProgress", mStackSlider.getXProgress(), 0);
+ slideOutX.setInterpolator(new LinearInterpolator());
slideOutX.start();
-
} else if (fromIndex == -1 && toIndex == mNumActiveViews - 1) {
// 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(-mViewHeight);
} else if (toIndex == -1) {
// Fade item out
PropertyAnimator fadeOut = new PropertyAnimator(DEFAULT_ANIMATION_DURATION,
@@ -194,13 +201,9 @@ public class StackView extends AdapterViewAnimator {
* Apply any necessary tranforms for the child that is being added.
*/
void applyTransformForChildAtIndex(View child, int relativeIndex) {
- float rotation;
-
if (!mRotations.containsKey(child)) {
- rotation = (float) (Math.random()*26 - 13);
+ float rotation = (float) (Math.random()*26 - 13);
mRotations.put(child, rotation);
- } else {
- rotation = mRotations.get(child);
}
// Child has been removed
@@ -235,8 +238,8 @@ public class StackView extends AdapterViewAnimator {
}
if (!mFirstLayoutHappened) {
- mViewHeight = (int) Math.round(SLIDE_UP_RATIO*getMeasuredHeight());
- mSwipeThreshold = (int) Math.round(SWIPE_THRESHOLD_RATIO*mViewHeight);
+ mViewHeight = Math.round(SLIDE_UP_RATIO*getMeasuredHeight());
+ mSwipeThreshold = Math.round(SWIPE_THRESHOLD_RATIO*mViewHeight);
// TODO: Right now this walks all the way up the view hierarchy and disables
// ClipChildren and ClipToPadding. We're probably going to want to reset
@@ -391,7 +394,7 @@ public class StackView extends AdapterViewAnimator {
float y = ev.getY(index);
touchRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
- if (touchRect.contains((int) Math.round(x), (int) Math.round(y))) {
+ if (touchRect.contains(Math.round(x), Math.round(y))) {
float oldX = ev.getX(activePointerIndex);
float oldY = ev.getY(activePointerIndex);
@@ -437,23 +440,29 @@ public class StackView extends AdapterViewAnimator {
mHighlight.bringToFront();
} else if (mSwipeGestureType == GESTURE_SLIDE_UP) {
// Didn't swipe up far enough, snap back down
- int duration = (int) Math.round(mStackSlider.getYProgress()*DEFAULT_ANIMATION_DURATION);
+ int duration = Math.round(mStackSlider.getYProgress()*DEFAULT_ANIMATION_DURATION);
- PropertyAnimator snapBackY = new PropertyAnimator(duration, mStackSlider,
+ StackSlider animationSlider = new StackSlider(mStackSlider);
+ PropertyAnimator snapBackY = new PropertyAnimator(duration, animationSlider,
"YProgress", mStackSlider.getYProgress(), 0);
+ snapBackY.setInterpolator(new LinearInterpolator());
snapBackY.start();
- PropertyAnimator snapBackX = new PropertyAnimator(duration, mStackSlider,
+ PropertyAnimator snapBackX = new PropertyAnimator(duration, animationSlider,
"XProgress", mStackSlider.getXProgress(), 0);
+ snapBackX.setInterpolator(new LinearInterpolator());
snapBackX.start();
} else if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
// Didn't swipe down far enough, snap back up
- int duration = (int) Math.round((1 -
+ int duration = Math.round((1 -
mStackSlider.getYProgress())*DEFAULT_ANIMATION_DURATION);
- PropertyAnimator snapBackY = new PropertyAnimator(duration, mStackSlider,
+ StackSlider animationSlider = new StackSlider(mStackSlider);
+ PropertyAnimator snapBackY = new PropertyAnimator(duration, animationSlider,
"YProgress", mStackSlider.getYProgress(), 1);
+ snapBackY.setInterpolator(new LinearInterpolator());
snapBackY.start();
- PropertyAnimator snapBackX = new PropertyAnimator(duration, mStackSlider,
+ PropertyAnimator snapBackX = new PropertyAnimator(duration, animationSlider,
"XProgress", mStackSlider.getXProgress(), 0);
+ snapBackX.setInterpolator(new LinearInterpolator());
snapBackX.start();
}
@@ -466,6 +475,15 @@ public class StackView extends AdapterViewAnimator {
float mYProgress;
float mXProgress;
+ public StackSlider() {
+ }
+
+ public StackSlider(StackSlider copy) {
+ mView = copy.mView;
+ mYProgress = copy.mYProgress;
+ mXProgress = copy.mXProgress;
+ }
+
private float cubic(float r) {
return (float) (Math.pow(2*r-1, 3) + 1)/2.0f;
}
@@ -488,6 +506,15 @@ public class StackView extends AdapterViewAnimator {
}
}
+ private float rotationInterpolator(float r) {
+ float pivot = 0.2f;
+ if (r < pivot) {
+ return 0;
+ } else {
+ return (r-pivot)/(1-pivot);
+ }
+ }
+
void setView(View v) {
mView = v;
}
@@ -502,10 +529,23 @@ public class StackView extends AdapterViewAnimator {
final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams();
- viewLp.setVerticalOffset((int) Math.round(-r*mViewHeight));
- highlightLp.setVerticalOffset((int) Math.round(-r*mViewHeight));
+ viewLp.setVerticalOffset(Math.round(-r*mViewHeight));
+ highlightLp.setVerticalOffset(Math.round(-r*mViewHeight));
mHighlight.setAlpha(highlightAlphaInterpolator(r));
+
+ float alpha = viewAlphaInterpolator(1-r);
+
+ // We make sure that views which can't be seen (have 0 alpha) are also invisible
+ // so that they don't interfere with click events.
+ if (mView.getAlpha() == 0 && alpha != 0 && mView.getVisibility() != VISIBLE) {
+ mView.setVisibility(VISIBLE);
+ } else if (alpha == 0 && mView.getAlpha() != 0 && mView.getVisibility() == VISIBLE) {
+ mView.setVisibility(INVISIBLE);
+ }
+
mView.setAlpha(viewAlphaInterpolator(1-r));
+ mView.setRotationX(90.0f*rotationInterpolator(r));
+ mHighlight.setRotationX(90.0f*rotationInterpolator(r));
}
public void setXProgress(float r) {
@@ -518,8 +558,8 @@ public class StackView extends AdapterViewAnimator {
final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams();
- viewLp.setHorizontalOffset((int) Math.round(r*mViewHeight));
- highlightLp.setHorizontalOffset((int) Math.round(r*mViewHeight));
+ viewLp.setHorizontalOffset(Math.round(r*mViewHeight));
+ highlightLp.setHorizontalOffset(Math.round(r*mViewHeight));
}
float getYProgress() {
@@ -534,7 +574,7 @@ public class StackView extends AdapterViewAnimator {
@Override
public void onRemoteAdapterConnected() {
super.onRemoteAdapterConnected();
- setDisplayedChild(mIndex);
+ setDisplayedChild(mWhichChild);
}
private static final Paint sHolographicPaint = new Paint();
@@ -542,7 +582,7 @@ public class StackView extends AdapterViewAnimator {
private static boolean sPaintsInitialized = false;
private static final float STROKE_WIDTH = 3.0f;
- static void initializePaints(Context context) {
+ static void initializePaints() {
sHolographicPaint.setColor(0xff6699ff);
sHolographicPaint.setFilterBitmap(true);
sErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
@@ -551,22 +591,29 @@ public class StackView extends AdapterViewAnimator {
}
static Bitmap createOutline(View v) {
+ if (v.getMeasuredWidth() == 0 || v.getMeasuredHeight() == 0) {
+ return null;
+ }
+
Bitmap bitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
+ float rotationX = v.getRotationX();
+ v.setRotationX(0);
canvas.concat(v.getMatrix());
v.draw(canvas);
+ v.setRotationX(rotationX);
Bitmap outlineBitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(),
Bitmap.Config.ARGB_8888);
Canvas outlineCanvas = new Canvas(outlineBitmap);
- drawOutline(outlineCanvas, v.getMeasuredWidth(), v.getMeasuredHeight(), bitmap);
+ drawOutline(outlineCanvas, bitmap);
bitmap.recycle();
return outlineBitmap;
}
- static void drawOutline(Canvas dest, int destWidth, int destHeight, Bitmap src) {
+ static void drawOutline(Canvas dest, Bitmap src) {
dest.drawColor(0, PorterDuff.Mode.CLEAR);
Bitmap mask = src.extractAlpha();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index b413aee..825de25 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -21,9 +21,9 @@ import com.android.internal.widget.EditableInputConnection;
import org.xmlpull.v1.XmlPullParserException;
+import android.content.ClipboardManager;
import android.content.ClippedData;
import android.content.Context;
-import android.content.ClipboardManager;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -4410,6 +4410,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return super.onKeyUp(keyCode, event);
}
+ hideControllers();
+
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_CENTER:
/*
@@ -6263,6 +6265,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sendOnTextChanged(buffer, start, before, after);
onTextChanged(buffer, start, before, after);
+ hideControllers();
}
/**
@@ -6747,11 +6750,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private void prepareCursorControllers() {
- boolean atLeastOneController = false;
-
// TODO Add an extra android:cursorController flag to disable the controller?
if (mCursorVisible && mLayout != null) {
- atLeastOneController = true;
if (mInsertionPointCursorController == null) {
mInsertionPointCursorController = new InsertionPointCursorController();
}
@@ -6760,7 +6760,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (canSelectText() && mLayout != null) {
- atLeastOneController = true;
if (mSelectionModifierCursorController == null) {
mSelectionModifierCursorController = new SelectionModifierCursorController();
}
@@ -6769,12 +6768,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Stop selection mode if the controller becomes unavailable.
finishSelectionActionMode();
}
-
- if (atLeastOneController) {
- Resources res = mContext.getResources();
- mCursorControllerVerticalOffset = res.getDimensionPixelOffset(
- com.android.internal.R.dimen.cursor_controller_vertical_offset);
- }
}
/**
@@ -7620,6 +7613,75 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public void draw(Canvas canvas);
}
+ private class Handle {
+ Drawable mDrawable;
+ // Vertical extension of the touch region
+ int mTopExtension, mBottomExtension;
+ // Position of the virtual finger position on screen
+ int mHopSpotVertcalPosition;
+
+ Handle(Drawable drawable) {
+ mDrawable = drawable;
+ }
+
+ void positionAtCursor(final int offset, boolean bottom) {
+ final int drawableWidth = mDrawable.getIntrinsicWidth();
+ final int drawableHeight = mDrawable.getIntrinsicHeight();
+ final int line = mLayout.getLineForOffset(offset);
+ final int lineTop = mLayout.getLineTop(line);
+ final int lineBottom = mLayout.getLineBottom(line);
+
+ mHopSpotVertcalPosition = lineTop + (bottom ? (3 * (lineBottom - lineTop)) / 4 :
+ (lineBottom - lineTop) / 4);
+
+ final Rect bounds = sCursorControllerTempRect;
+ bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - drawableWidth / 2.0);
+ bounds.top = (bottom ? lineBottom : lineTop) - drawableHeight / 2;
+
+ mTopExtension = bottom ? 0 : drawableHeight / 2;
+ mBottomExtension = drawableHeight;
+
+ // Extend touch region up when editing the last line of text (or a single line) so that
+ // it is easier to grab.
+ if (line == mLayout.getLineCount() - 1) {
+ mTopExtension = (lineBottom - lineTop) - drawableHeight / 2;
+ }
+
+ bounds.right = bounds.left + drawableWidth;
+ bounds.bottom = bounds.top + drawableHeight;
+
+ int boundTopBefore = bounds.top;
+ convertFromViewportToContentCoordinates(bounds);
+ mHopSpotVertcalPosition += bounds.top - boundTopBefore;
+ mDrawable.setBounds(bounds);
+ postInvalidate();
+ }
+
+ boolean hasFingerOn(float x, float y) {
+ // Simulate a 'fat finger' to ease grabbing of the controller.
+ // Expands according to controller image size instead of using dip distance.
+ // Assumes controller imager has a sensible size, proportionnal to screen density.
+ final int drawableWidth = mDrawable.getIntrinsicWidth();
+ final Rect fingerRect = sCursorControllerTempRect;
+ fingerRect.set((int) (x - drawableWidth / 2.0),
+ (int) (y - mBottomExtension),
+ (int) (x + drawableWidth / 2.0),
+ (int) (y + mTopExtension));
+ return Rect.intersects(mDrawable.getBounds(), fingerRect);
+ }
+
+ void postInvalidate() {
+ final Rect bounds = mDrawable.getBounds();
+ TextView.this.postInvalidate(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ }
+
+ void postInvalidateDelayed(long delay) {
+ final Rect bounds = mDrawable.getBounds();
+ TextView.this.postInvalidateDelayed(delay, bounds.left, bounds.top,
+ bounds.right, bounds.bottom);
+ }
+ }
+
class InsertionPointCursorController implements CursorController {
private static final int DELAY_BEFORE_FADE_OUT = 2100;
@@ -7628,7 +7690,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Starting time of the fade timer
private long mFadeOutTimerStart;
// The cursor controller image
- private final Drawable mDrawable;
+ private final Handle mHandle;
// Used to detect a tap (vs drag) on the controller
private long mOnDownTimerStart;
// Offset between finger hot point on cursor controller and actual cursor
@@ -7636,7 +7698,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
InsertionPointCursorController() {
Resources res = mContext.getResources();
- mDrawable = res.getDrawable(com.android.internal.R.drawable.cursor_controller);
+ mHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle));
}
public void show() {
@@ -7652,7 +7714,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Start fading out, only if not already in progress
if (time - mFadeOutTimerStart < DELAY_BEFORE_FADE_OUT) {
mFadeOutTimerStart = time - DELAY_BEFORE_FADE_OUT;
- postInvalidate(mDrawable);
+ mHandle.postInvalidate();
}
}
}
@@ -7661,19 +7723,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mIsVisible) {
int time = (int) (System.currentTimeMillis() - mFadeOutTimerStart);
if (time <= DELAY_BEFORE_FADE_OUT) {
- postInvalidateDelayed(DELAY_BEFORE_FADE_OUT - time, mDrawable);
+ mHandle.postInvalidateDelayed(DELAY_BEFORE_FADE_OUT - time);
} else {
time -= DELAY_BEFORE_FADE_OUT;
if (time <= FADE_OUT_DURATION) {
- final int alpha = 255 * (FADE_OUT_DURATION - time) / FADE_OUT_DURATION;
- mDrawable.setAlpha(alpha);
- postInvalidateDelayed(30, mDrawable);
+ final int alpha = (int)
+ ((255.0 * (FADE_OUT_DURATION - time)) / FADE_OUT_DURATION);
+ mHandle.mDrawable.setAlpha(alpha);
+ mHandle.postInvalidateDelayed(30);
} else {
- mDrawable.setAlpha(0);
+ mHandle.mDrawable.setAlpha(0);
mIsVisible = false;
}
}
- mDrawable.draw(canvas);
+ mHandle.mDrawable.draw(canvas);
}
}
@@ -7688,7 +7751,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private void updateDrawablePosition() {
if (mIsVisible) {
// Clear previous cursor controller before bounds are updated
- postInvalidate(mDrawable);
+ mHandle.postInvalidate();
}
final int offset = getSelectionStart();
@@ -7700,10 +7763,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return;
}
- positionDrawableUnderCursor(offset, mDrawable);
+ mHandle.positionAtCursor(offset, true);
mFadeOutTimerStart = System.currentTimeMillis();
- mDrawable.setAlpha(255);
+ mHandle.mDrawable.setAlpha(255);
}
public void onTouchEvent(MotionEvent event) {
@@ -7713,7 +7776,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final float x = event.getX();
final float y = event.getY();
- if (fingerIsOnDrawable(x, y, mDrawable)) {
+ if (mHandle.hasFingerOn(x, y)) {
show();
if (mMovement instanceof ArrowKeyMovementMethod) {
@@ -7726,9 +7789,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mParent.requestDisallowInterceptTouchEvent(true);
}
- final Rect bounds = mDrawable.getBounds();
+ final Rect bounds = mHandle.mDrawable.getBounds();
mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
- mOffsetY = bounds.top - mCursorControllerVerticalOffset - y;
+ mOffsetY = mHandle.mHopSpotVertcalPosition - y;
mOnDownTimerStart = event.getEventTime();
}
@@ -7769,7 +7832,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Starting time of the fade timer
private long mFadeOutTimerStart;
// The cursor controller images
- private final Drawable mStartDrawable, mEndDrawable;
+ private final Handle mStartHandle, mEndHandle;
// Offset between finger hot point on active cursor controller and actual cursor
private float mOffsetX, mOffsetY;
// The offsets of that last touch down event. Remembered to start selection there.
@@ -7777,8 +7840,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
SelectionModifierCursorController() {
Resources res = mContext.getResources();
- mStartDrawable = res.getDrawable(com.android.internal.R.drawable.selection_start_handle);
- mEndDrawable = res.getDrawable(com.android.internal.R.drawable.selection_end_handle);
+ mStartHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle));
+ mEndHandle = new Handle(res.getDrawable(com.android.internal.R.drawable.text_select_handle));
}
public void show() {
@@ -7793,15 +7856,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public void hide() {
if (mIsVisible && (mFadeOutTimerStart < 0)) {
mFadeOutTimerStart = System.currentTimeMillis();
- postInvalidate(mStartDrawable);
- postInvalidate(mEndDrawable);
+ mStartHandle.postInvalidate();
+ mEndHandle.postInvalidate();
}
}
public void cancelFadeOutAnimation() {
mIsVisible = false;
- postInvalidate(mStartDrawable);
- postInvalidate(mEndDrawable);
+ mStartHandle.postInvalidate();
+ mEndHandle.postInvalidate();
}
public void draw(Canvas canvas) {
@@ -7810,18 +7873,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
int time = (int) (System.currentTimeMillis() - mFadeOutTimerStart);
if (time <= FADE_OUT_DURATION) {
final int alpha = 255 * (FADE_OUT_DURATION - time) / FADE_OUT_DURATION;
- mStartDrawable.setAlpha(alpha);
- mEndDrawable.setAlpha(alpha);
- postInvalidateDelayed(30, mStartDrawable);
- postInvalidateDelayed(30, mEndDrawable);
+ mStartHandle.mDrawable.setAlpha(alpha);
+ mEndHandle.mDrawable.setAlpha(alpha);
+ mStartHandle.postInvalidateDelayed(30);
+ mEndHandle.postInvalidateDelayed(30);
} else {
- mStartDrawable.setAlpha(0);
- mEndDrawable.setAlpha(0);
+ mStartHandle.mDrawable.setAlpha(0);
+ mEndHandle.mDrawable.setAlpha(0);
mIsVisible = false;
}
}
- mStartDrawable.draw(canvas);
- mEndDrawable.draw(canvas);
+ mStartHandle.mDrawable.draw(canvas);
+ mEndHandle.mDrawable.draw(canvas);
}
}
@@ -7861,8 +7924,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private void updateDrawablesPositions() {
if (mIsVisible) {
// Clear previous cursor controller before bounds are updated
- postInvalidate(mStartDrawable);
- postInvalidate(mEndDrawable);
+ mStartHandle.postInvalidate();
+ mEndHandle.postInvalidate();
}
final int selectionStart = getSelectionStart();
@@ -7875,11 +7938,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return;
}
- positionDrawableUnderCursor(selectionStart, mStartDrawable);
- positionDrawableUnderCursor(selectionEnd, mEndDrawable);
+ boolean oneLineSelection = mLayout.getLineForOffset(selectionStart) == mLayout.getLineForOffset(selectionEnd);
+ mStartHandle.positionAtCursor(selectionStart, oneLineSelection);
+ mEndHandle.positionAtCursor(selectionEnd, true);
- mStartDrawable.setAlpha(255);
- mEndDrawable.setAlpha(255);
+ mStartHandle.mDrawable.setAlpha(255);
+ mEndHandle.mDrawable.setAlpha(255);
}
public void onTouchEvent(MotionEvent event) {
@@ -7889,26 +7953,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int x = (int) event.getX();
final int y = (int) event.getY();
- // Remember finger down position, to be able to start selection on that point
+ // Remember finger down position, to be able to start selection from there
mMinTouchOffset = mMaxTouchOffset = getOffset(x, y);
if (mIsVisible) {
if (mMovement instanceof ArrowKeyMovementMethod) {
- boolean isOnStart = fingerIsOnDrawable(x, y, mStartDrawable);
- boolean isOnEnd = fingerIsOnDrawable(x, y, mEndDrawable);
+ boolean isOnStart = mStartHandle.hasFingerOn(x, y);
+ boolean isOnEnd = mEndHandle.hasFingerOn(x, y);
if (isOnStart || isOnEnd) {
if (mParent != null) {
- // Prevent possible scrollView parent from scrolling, so that
- // we can use auto-scrolling.
+ // Prevent possible scrollView parent from scrolling, so
+ // that we can use auto-scrolling.
mParent.requestDisallowInterceptTouchEvent(true);
}
- // Start handle will be dragged in case BOTH controller are under finger
- mStartIsDragged = isOnStart;
- final Rect bounds =
- (mStartIsDragged ? mStartDrawable : mEndDrawable).getBounds();
+ // In case both controllers are under finger (very small
+ // selection region), arbitrarily pick end controller.
+ mStartIsDragged = !isOnEnd;
+ final Handle draggedHandle = mStartIsDragged ? mStartHandle : mEndHandle;
+ final Rect bounds = draggedHandle.mDrawable.getBounds();
mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
- mOffsetY = bounds.top - mCursorControllerVerticalOffset - y;
+ mOffsetY = draggedHandle.mHopSpotVertcalPosition - y;
((ArrowKeyMovementMethod)mMovement).setCursorController(this);
}
@@ -7967,61 +8032,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- // Helper methods used by CursorController implementations
-
- private void positionDrawableUnderCursor(final int offset, Drawable drawable) {
- final int drawableWidth = drawable.getIntrinsicWidth();
- final int drawableHeight = drawable.getIntrinsicHeight();
- final int line = mLayout.getLineForOffset(offset);
-
- final Rect bounds = sCursorControllerTempRect;
- bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5 - drawableWidth / 2.0);
- bounds.top = mLayout.getLineTop(line + 1);
-
- // Move cursor controller a little bit up when editing the last line of text
- // (or a single line) so that it is visible and easier to grab.
- if (line == mLayout.getLineCount() - 1) {
- bounds.top -= Math.max(0, drawableHeight / 2 - getExtendedPaddingBottom());
- }
-
- bounds.right = bounds.left + drawableWidth;
- bounds.bottom = bounds.top + drawableHeight;
-
- convertFromViewportToContentCoordinates(bounds);
- drawable.setBounds(bounds);
- postInvalidate(bounds.left, bounds.top, bounds.right, bounds.bottom);
- }
-
- private boolean fingerIsOnDrawable(float x, float y, Drawable drawable) {
- // Simulate a 'fat finger' to ease grabbing of the controller.
- // Expands according to controller image size instead of using density.
- // Assumes controller imager has a sensible size, proportionnal to density.
- final int drawableWidth = drawable.getIntrinsicWidth();
- final int drawableHeight = drawable.getIntrinsicHeight();
- final Rect fingerRect = sCursorControllerTempRect;
- fingerRect.set((int) (x - drawableWidth / 2.0),
- (int) (y - drawableHeight),
- (int) (x + drawableWidth / 2.0),
- (int) y);
- return Rect.intersects(drawable.getBounds(), fingerRect);
- }
-
- private void postInvalidate(Drawable drawable) {
- final Rect bounds = drawable.getBounds();
- postInvalidate(bounds.left, bounds.top, bounds.right, bounds.bottom);
- }
-
- private void postInvalidateDelayed(long delay, Drawable drawable) {
- final Rect bounds = drawable.getBounds();
- postInvalidateDelayed(delay, bounds.left, bounds.top, bounds.right, bounds.bottom);
- }
-
private void hideInsertionPointCursorController() {
if (mInsertionPointCursorController != null) {
mInsertionPointCursorController.hide();
}
}
+ private void hideControllers() {
+ hideInsertionPointCursorController();
+ finishSelectionActionMode();
+ }
+
/**
* Get the offset character closest to the specified absolute position.
*
@@ -8083,7 +8104,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private final TextPaint mTextPaint;
private boolean mUserSetTextScaleX;
private final Paint mHighlightPaint;
- private int mHighlightColor = 0xD077A14B;
+ private int mHighlightColor = 0xCC475925;
private Layout mLayout;
private long mShowCursor;
@@ -8093,8 +8114,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Cursor Controllers. Null when disabled.
private CursorController mInsertionPointCursorController;
private CursorController mSelectionModifierCursorController;
- // Stored once and for all.
- private int mCursorControllerVerticalOffset;
private boolean mShouldStartSelectionActionMode = false;
private ActionMode mSelectionActionMode;
// Created once and shared by different CursorController helper methods.
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 75084db..ef0ab41 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -429,7 +429,8 @@ public class ActionBarImpl extends ActionBar {
public ActionModeImpl(ActionMode.Callback callback) {
mCallback = callback;
- mMenu = new MenuBuilder(mActionView.getContext());
+ mMenu = new MenuBuilder(mActionView.getContext())
+ .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
mMenu.setCallback(this);
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index d040d3f..1620778 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -22,8 +22,8 @@ import android.telephony.SignalStrength;
interface IBatteryStats {
byte[] getStatistics();
- void noteStartWakelock(int uid, String name, int type);
- void noteStopWakelock(int uid, String name, int type);
+ void noteStartWakelock(int uid, int pid, String name, int type);
+ void noteStopWakelock(int uid, int pid, String name, int type);
/* DO NOT CHANGE the position of noteStartSensor without updating
SensorService.cpp */
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index ce5959d..e1c5564 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -18,17 +18,31 @@ package com.android.internal.app;
import android.app.Activity;
import android.os.Bundle;
+import android.view.MotionEvent;
import android.widget.ImageView;
+import android.widget.Toast;
public class PlatLogoActivity extends Activity {
+ Toast mToast;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mToast = Toast.makeText(this, "Zombie art by Jack Larson", Toast.LENGTH_SHORT);
+
ImageView content = new ImageView(this);
content.setImageResource(com.android.internal.R.drawable.platlogo);
content.setScaleType(ImageView.ScaleType.FIT_CENTER);
setContentView(content);
}
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_UP) {
+ mToast.show();
+ }
+ return super.dispatchTouchEvent(ev);
+ }
}
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
index f0920d1..216d985 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
@@ -24,6 +24,6 @@ import android.widget.RemoteViews;
oneway interface IAppWidgetHost {
void updateAppWidget(int appWidgetId, in RemoteViews views);
void providerChanged(int appWidgetId, in AppWidgetProviderInfo info);
- void viewDataChanged(int appWidgetId, in RemoteViews views, int viewId);
+ void viewDataChanged(int appWidgetId, int viewId);
}
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index af75d5b..4d56745 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -40,8 +40,9 @@ interface IAppWidgetService {
// for AppWidgetManager
//
void updateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views);
+ void partiallyUpdateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views);
void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views);
- void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, in RemoteViews views, int viewId);
+ void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, int viewId);
List<AppWidgetProviderInfo> getInstalledProviders();
AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId);
void bindAppWidgetId(int appWidgetId, in ComponentName provider);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 13b3033..a70dbf6 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -63,7 +63,7 @@ public final class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 49;
+ private static final int VERSION = 50;
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 1000;
@@ -107,6 +107,7 @@ public final class BatteryStatsImpl extends BatteryStats {
int mNumHistoryItems;
HistoryItem mHistory;
HistoryItem mHistoryEnd;
+ HistoryItem mHistoryLastEnd;
HistoryItem mHistoryCache;
final HistoryItem mHistoryCur = new HistoryItem();
@@ -451,7 +452,7 @@ public final class BatteryStatsImpl extends BatteryStats {
* Clear state of this timer. Returns true if the timer is inactive
* so can be completely dropped.
*/
- boolean reset(boolean detachIfReset) {
+ boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
mTotalTime = mLoadedTime = mLastTime = 0;
mCount = mLoadedCount = mLastCount = 0;
if (detachIfReset) {
@@ -713,8 +714,8 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(mTrackingReportedValues ? 1 : 0);
}
- boolean reset(boolean detachIfReset) {
- super.reset(detachIfReset);
+ boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
+ super.reset(stats, detachIfReset);
setStale();
return true;
}
@@ -749,7 +750,7 @@ public final class BatteryStatsImpl extends BatteryStats {
long mUpdateTime;
/**
- * The total time at which the timer was acquired, to determine if
+ * The total time at which the timer was acquired, to determine if it
* was actually held for an interesting duration.
*/
long mAcquireTime;
@@ -890,9 +891,14 @@ public final class BatteryStatsImpl extends BatteryStats {
return mCount;
}
- boolean reset(boolean detachIfReset) {
+ boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
boolean canDetach = mNesting <= 0;
- super.reset(canDetach && detachIfReset);
+ super.reset(stats, canDetach && detachIfReset);
+ if (mNesting > 0) {
+ mUpdateTime = stats.getBatteryRealtimeLocked(
+ SystemClock.elapsedRealtime() * 1000);
+ }
+ mAcquireTime = mTotalTime;
return canDetach;
}
@@ -1115,6 +1121,26 @@ public final class BatteryStatsImpl extends BatteryStats {
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
}
+
+ // If the current time is basically the same as the last time,
+ // just collapse into one record.
+ if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
+ && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+100)) {
+ // If the current is the same as the one before, then we no
+ // longer need the entry.
+ if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
+ && mHistoryLastEnd.same(mHistoryCur)) {
+ mHistoryLastEnd.next = null;
+ mHistoryEnd.next = mHistoryCache;
+ mHistoryCache = mHistoryEnd;
+ mHistoryEnd = mHistoryLastEnd;
+ mHistoryLastEnd = null;
+ } else {
+ mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, mHistoryCur);
+ }
+ return;
+ }
+
if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
// Once we've reached the maximum number of items, we only
// record changes to the battery level.
@@ -1123,6 +1149,7 @@ public final class BatteryStatsImpl extends BatteryStats {
return;
}
}
+
addHistoryRecordLocked(curTime, HistoryItem.CMD_UPDATE);
}
@@ -1141,6 +1168,7 @@ public final class BatteryStatsImpl extends BatteryStats {
void addHistoryRecordLocked(HistoryItem rec) {
mNumHistoryItems++;
rec.next = null;
+ mHistoryLastEnd = mHistoryEnd;
if (mHistoryEnd != null) {
mHistoryEnd.next = rec;
mHistoryEnd = rec;
@@ -1153,7 +1181,7 @@ public final class BatteryStatsImpl extends BatteryStats {
if (mHistory != null) {
mHistoryEnd.next = mHistoryCache;
mHistoryCache = mHistory;
- mHistory = mHistoryEnd = null;
+ mHistory = mHistoryLastEnd = mHistoryEnd = null;
}
mNumHistoryItems = 0;
mHistoryBaseTime = 0;
@@ -1211,6 +1239,89 @@ public final class BatteryStatsImpl extends BatteryStats {
mBluetoothPingStart = -1;
}
+ int mWakeLockNesting;
+
+ public void noteStartWakeLocked(int uid, int pid, String name, int type) {
+ if (type == WAKE_TYPE_PARTIAL) {
+ // Only care about partial wake locks, since full wake locks
+ // will be canceled when the user puts the screen to sleep.
+ if (mWakeLockNesting == 0) {
+ mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ }
+ mWakeLockNesting++;
+ }
+ if (uid >= 0) {
+ getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type);
+ }
+ }
+
+ public void noteStopWakeLocked(int uid, int pid, String name, int type) {
+ if (type == WAKE_TYPE_PARTIAL) {
+ mWakeLockNesting--;
+ if (mWakeLockNesting == 0) {
+ mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ }
+ }
+ if (uid >= 0) {
+ getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type);
+ }
+ }
+
+ public void noteProcessDiedLocked(int uid, int pid) {
+ Uid u = mUidStats.get(uid);
+ if (u != null) {
+ u.mPids.remove(pid);
+ }
+ }
+
+ public long getProcessWakeTime(int uid, int pid, long realtime) {
+ Uid u = mUidStats.get(uid);
+ if (u != null) {
+ Uid.Pid p = u.mPids.get(pid);
+ if (p != null) {
+ return p.mWakeSum + (p.mWakeStart != 0 ? (realtime - p.mWakeStart) : 0);
+ }
+ }
+ return 0;
+ }
+
+ public void reportExcessiveWakeLocked(int uid, String proc, long overTime, long usedTime) {
+ Uid u = mUidStats.get(uid);
+ if (u != null) {
+ u.reportExcessiveWakeLocked(proc, overTime, usedTime);
+ }
+ }
+
+ int mSensorNesting;
+
+ public void noteStartSensorLocked(int uid, int sensor) {
+ if (mSensorNesting == 0) {
+ mHistoryCur.states |= HistoryItem.STATE_SENSOR_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Start sensor to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ }
+ mSensorNesting++;
+ getUidStatsLocked(uid).noteStartSensor(sensor);
+ }
+
+ public void noteStopSensorLocked(int uid, int sensor) {
+ mSensorNesting--;
+ if (mSensorNesting == 0) {
+ mHistoryCur.states &= ~HistoryItem.STATE_SENSOR_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Stop sensor to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ }
+ getUidStatsLocked(uid).noteStopSensor(sensor);
+ }
+
int mGpsNesting;
public void noteStartGpsLocked(int uid) {
@@ -1246,6 +1357,10 @@ public final class BatteryStatsImpl extends BatteryStats {
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(this);
}
+
+ // Fake a wake lock, so we consider the device waked as long
+ // as the screen is on.
+ noteStartWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL);
}
}
@@ -1260,6 +1375,8 @@ public final class BatteryStatsImpl extends BatteryStats {
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this);
}
+
+ noteStopWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL);
}
}
@@ -1800,6 +1917,11 @@ public final class BatteryStatsImpl extends BatteryStats {
*/
final HashMap<String, Pkg> mPackageStats = new HashMap<String, Pkg>();
+ /**
+ * The transient wake stats we have collected for this uid's pids.
+ */
+ final SparseArray<Pid> mPids = new SparseArray<Pid>();
+
public Uid(int uid) {
mUid = uid;
mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON, null, mUnpluggables);
@@ -2083,27 +2205,27 @@ public final class BatteryStatsImpl extends BatteryStats {
boolean active = false;
if (mWifiTurnedOnTimer != null) {
- active |= !mWifiTurnedOnTimer.reset(false);
+ active |= !mWifiTurnedOnTimer.reset(BatteryStatsImpl.this, false);
active |= mWifiTurnedOn;
}
if (mFullWifiLockTimer != null) {
- active |= !mFullWifiLockTimer.reset(false);
+ active |= !mFullWifiLockTimer.reset(BatteryStatsImpl.this, false);
active |= mFullWifiLockOut;
}
if (mScanWifiLockTimer != null) {
- active |= !mScanWifiLockTimer.reset(false);
+ active |= !mScanWifiLockTimer.reset(BatteryStatsImpl.this, false);
active |= mScanWifiLockOut;
}
if (mWifiMulticastTimer != null) {
- active |= !mWifiMulticastTimer.reset(false);
+ active |= !mWifiMulticastTimer.reset(BatteryStatsImpl.this, false);
active |= mWifiMulticastEnabled;
}
if (mAudioTurnedOnTimer != null) {
- active |= !mAudioTurnedOnTimer.reset(false);
+ active |= !mAudioTurnedOnTimer.reset(BatteryStatsImpl.this, false);
active |= mAudioTurnedOn;
}
if (mVideoTurnedOnTimer != null) {
- active |= !mVideoTurnedOnTimer.reset(false);
+ active |= !mVideoTurnedOnTimer.reset(BatteryStatsImpl.this, false);
active |= mVideoTurnedOn;
}
@@ -2148,6 +2270,14 @@ public final class BatteryStatsImpl extends BatteryStats {
}
mProcessStats.clear();
}
+ if (mPids.size() > 0) {
+ for (int i=0; !active && i<mPids.size(); i++) {
+ Pid pid = mPids.valueAt(i);
+ if (pid.mWakeStart != 0) {
+ active = true;
+ }
+ }
+ }
if (mPackageStats.size() > 0) {
Iterator<Map.Entry<String, Pkg>> it = mPackageStats.entrySet().iterator();
while (it.hasNext()) {
@@ -2166,6 +2296,8 @@ public final class BatteryStatsImpl extends BatteryStats {
mPackageStats.clear();
}
+ mPids.clear();
+
if (!active) {
if (mWifiTurnedOnTimer != null) {
mWifiTurnedOnTimer.detach();
@@ -2414,13 +2546,13 @@ public final class BatteryStatsImpl extends BatteryStats {
boolean reset() {
boolean wlactive = false;
if (mTimerFull != null) {
- wlactive |= !mTimerFull.reset(false);
+ wlactive |= !mTimerFull.reset(BatteryStatsImpl.this, false);
}
if (mTimerPartial != null) {
- wlactive |= !mTimerPartial.reset(false);
+ wlactive |= !mTimerPartial.reset(BatteryStatsImpl.this, false);
}
if (mTimerWindow != null) {
- wlactive |= !mTimerWindow.reset(false);
+ wlactive |= !mTimerWindow.reset(BatteryStatsImpl.this, false);
}
if (!wlactive) {
if (mTimerFull != null) {
@@ -2488,7 +2620,7 @@ public final class BatteryStatsImpl extends BatteryStats {
}
boolean reset() {
- if (mTimer.reset(true)) {
+ if (mTimer.reset(BatteryStatsImpl.this, true)) {
mTimer = null;
return true;
}
@@ -2600,6 +2732,8 @@ public final class BatteryStatsImpl extends BatteryStats {
SamplingCounter[] mSpeedBins;
+ ArrayList<ExcessiveWake> mExcessiveWake;
+
Proc() {
mUnpluggables.add(this);
mSpeedBins = new SamplingCounter[getCpuSpeedSteps()];
@@ -2626,6 +2760,58 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
+ public int countExcessiveWakes() {
+ return mExcessiveWake != null ? mExcessiveWake.size() : 0;
+ }
+
+ public ExcessiveWake getExcessiveWake(int i) {
+ if (mExcessiveWake != null) {
+ return mExcessiveWake.get(i);
+ }
+ return null;
+ }
+
+ public void addExcessiveWake(long overTime, long usedTime) {
+ if (mExcessiveWake == null) {
+ mExcessiveWake = new ArrayList<ExcessiveWake>();
+ }
+ ExcessiveWake ew = new ExcessiveWake();
+ ew.overTime = overTime;
+ ew.usedTime = usedTime;
+ mExcessiveWake.add(ew);
+ }
+
+ void writeExcessiveWakeToParcelLocked(Parcel out) {
+ if (mExcessiveWake == null) {
+ out.writeInt(0);
+ return;
+ }
+
+ final int N = mExcessiveWake.size();
+ out.writeInt(N);
+ for (int i=0; i<N; i++) {
+ ExcessiveWake ew = mExcessiveWake.get(i);
+ out.writeLong(ew.overTime);
+ out.writeLong(ew.usedTime);
+ }
+ }
+
+ void readExcessiveWakeFromParcelLocked(Parcel in) {
+ final int N = in.readInt();
+ if (N == 0) {
+ mExcessiveWake = null;
+ return;
+ }
+
+ mExcessiveWake = new ArrayList<ExcessiveWake>();
+ for (int i=0; i<N; i++) {
+ ExcessiveWake ew = new ExcessiveWake();
+ ew.overTime = in.readLong();
+ ew.usedTime = in.readLong();
+ mExcessiveWake.add(ew);
+ }
+ }
+
void writeToParcelLocked(Parcel out) {
out.writeLong(mUserTime);
out.writeLong(mSystemTime);
@@ -2650,6 +2836,8 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(0);
}
}
+
+ writeExcessiveWakeToParcelLocked(out);
}
void readFromParcelLocked(Parcel in) {
@@ -2678,6 +2866,8 @@ public final class BatteryStatsImpl extends BatteryStats {
mSpeedBins[i] = new SamplingCounter(mUnpluggables, in);
}
}
+
+ readExcessiveWakeFromParcelLocked(in);
}
public BatteryStatsImpl getBatteryStats() {
@@ -3155,6 +3345,11 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
+ public class Pid {
+ long mWakeSum;
+ long mWakeStart;
+ }
+
/**
* Retrieve the statistics object for a particular process, creating
* if needed.
@@ -3169,6 +3364,15 @@ public final class BatteryStatsImpl extends BatteryStats {
return ps;
}
+ public Pid getPidStatsLocked(int pid) {
+ Pid p = mPids.get(pid);
+ if (p == null) {
+ p = new Pid();
+ mPids.put(pid, p);
+ }
+ return p;
+ }
+
/**
* Retrieve the statistics object for a particular service, creating
* if needed.
@@ -3261,18 +3465,36 @@ public final class BatteryStatsImpl extends BatteryStats {
return t;
}
- public void noteStartWakeLocked(String name, int type) {
+ public void noteStartWakeLocked(int pid, String name, int type) {
StopwatchTimer t = getWakeTimerLocked(name, type);
if (t != null) {
t.startRunningLocked(BatteryStatsImpl.this);
}
+ if (pid >= 0 && type == WAKE_TYPE_PARTIAL) {
+ Pid p = getPidStatsLocked(pid);
+ p.mWakeStart = SystemClock.elapsedRealtime();
+ }
}
- public void noteStopWakeLocked(String name, int type) {
+ public void noteStopWakeLocked(int pid, String name, int type) {
StopwatchTimer t = getWakeTimerLocked(name, type);
if (t != null) {
t.stopRunningLocked(BatteryStatsImpl.this);
}
+ if (pid >= 0 && type == WAKE_TYPE_PARTIAL) {
+ Pid p = mPids.get(pid);
+ if (p != null) {
+ p.mWakeSum += SystemClock.elapsedRealtime() - p.mWakeStart;
+ p.mWakeStart = 0;
+ }
+ }
+ }
+
+ public void reportExcessiveWakeLocked(String proc, long overTime, long usedTime) {
+ Proc p = getProcessStatsLocked(proc);
+ if (p != null) {
+ p.addExcessiveWake(overTime, usedTime);
+ }
}
public void noteStartSensor(int sensor) {
@@ -3374,6 +3596,10 @@ public final class BatteryStatsImpl extends BatteryStats {
return mOnBattery;
}
+ public boolean isScreenOn() {
+ return mScreenOn;
+ }
+
void initTimes() {
mBatteryRealtime = mTrackBatteryPastUptime = 0;
mBatteryUptime = mTrackBatteryPastRealtime = 0;
@@ -3386,24 +3612,24 @@ public final class BatteryStatsImpl extends BatteryStats {
public void resetAllStatsLocked() {
mStartCount = 0;
initTimes();
- mScreenOnTimer.reset(false);
+ mScreenOnTimer.reset(this, false);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
- mScreenBrightnessTimer[i].reset(false);
+ mScreenBrightnessTimer[i].reset(this, false);
}
mInputEventCounter.reset(false);
- mPhoneOnTimer.reset(false);
- mAudioOnTimer.reset(false);
- mVideoOnTimer.reset(false);
+ mPhoneOnTimer.reset(this, false);
+ mAudioOnTimer.reset(this, false);
+ mVideoOnTimer.reset(this, false);
for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
- mPhoneSignalStrengthsTimer[i].reset(false);
+ mPhoneSignalStrengthsTimer[i].reset(this, false);
}
- mPhoneSignalScanningTimer.reset(false);
+ mPhoneSignalScanningTimer.reset(this, false);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
- mPhoneDataConnectionsTimer[i].reset(false);
+ mPhoneDataConnectionsTimer[i].reset(this, false);
}
- mWifiOnTimer.reset(false);
- mWifiRunningTimer.reset(false);
- mBluetoothOnTimer.reset(false);
+ mWifiOnTimer.reset(this, false);
+ mWifiRunningTimer.reset(this, false);
+ mBluetoothOnTimer.reset(this, false);
for (int i=0; i<mUidStats.size(); i++) {
if (mUidStats.valueAt(i).reset()) {
@@ -4085,6 +4311,7 @@ public final class BatteryStatsImpl extends BatteryStats {
p.mUserTime = p.mLoadedUserTime = in.readLong();
p.mSystemTime = p.mLoadedSystemTime = in.readLong();
p.mStarts = p.mLoadedStarts = in.readInt();
+ p.readExcessiveWakeFromParcelLocked(in);
}
NP = in.readInt();
@@ -4273,6 +4500,7 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeLong(ps.mUserTime);
out.writeLong(ps.mSystemTime);
out.writeInt(ps.mStarts);
+ ps.writeExcessiveWakeToParcelLocked(out);
}
}
diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java
index ab80c58..d381901 100644
--- a/core/java/com/android/internal/view/StandaloneActionMode.java
+++ b/core/java/com/android/internal/view/StandaloneActionMode.java
@@ -44,7 +44,7 @@ public class StandaloneActionMode extends ActionMode implements MenuBuilder.Call
mContextView = view;
mCallback = callback;
- mMenu = new MenuBuilder(context);
+ mMenu = new MenuBuilder(context).setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
mMenu.setCallback(this);
}
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 215d809..d160fec 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -171,6 +171,11 @@ public class MenuBuilder implements Menu {
private boolean mReserveActionOverflow;
/**
+ * Default value for how added items should show in the action list.
+ */
+ private int mDefaultShowAsAction = MenuItem.SHOW_AS_ACTION_NEVER;
+
+ /**
* Current use case is Context Menus: As Views populate the context menu, each one has
* extra information that should be passed along. This is the current menu info that
* should be set on all items added to this menu.
@@ -328,6 +333,11 @@ public class MenuBuilder implements Menu {
(mResources.getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS);
}
+ public MenuBuilder setDefaultShowAsAction(int defaultShowAsAction) {
+ mDefaultShowAsAction = defaultShowAsAction;
+ return this;
+ }
+
public void setCallback(Callback callback) {
mCallback = callback;
}
@@ -411,7 +421,8 @@ public class MenuBuilder implements Menu {
private MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
final int ordering = getOrdering(categoryOrder);
- final MenuItemImpl item = new MenuItemImpl(this, group, id, categoryOrder, ordering, title);
+ final MenuItemImpl item = new MenuItemImpl(this, group, id, categoryOrder,
+ ordering, title, mDefaultShowAsAction);
if (mCurrentMenuInfo != null) {
// Pass along the current menu info
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index fecbd77..07a2a94 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -108,7 +108,7 @@ public final class MenuItemImpl implements MenuItem {
* @param title The text to display for the item.
*/
MenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering,
- CharSequence title) {
+ CharSequence title, int showAsAction) {
if (sPrependShortcutLabel == null) {
// This is instantiated from the UI thread, so no chance of sync issues
@@ -129,6 +129,7 @@ public final class MenuItemImpl implements MenuItem {
mCategoryOrder = categoryOrder;
mOrdering = ordering;
mTitle = title;
+ mShowAsAction = showAsAction;
}
/**
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 8918a8e..acf75e3 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -78,6 +78,12 @@ public class ActionBarContextView extends ViewGroup {
a.recycle();
}
+ @Override
+ public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
+ // No starting an action mode for an existing action mode UI child! (Where would it go?)
+ return null;
+ }
+
public void setHeight(int height) {
mContentHeight = height;
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index c3c0db2..067d022 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -22,8 +22,8 @@ import com.android.internal.view.menu.ActionMenuView;
import com.android.internal.view.menu.MenuBuilder;
import android.app.ActionBar;
-import android.app.Activity;
import android.app.ActionBar.NavigationCallback;
+import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -34,6 +34,7 @@ import android.graphics.drawable.Drawable;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.view.ActionMode;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -163,7 +164,6 @@ public class ActionBarView extends ViewGroup {
LayoutInflater inflater = LayoutInflater.from(context);
mCustomNavView = (View) inflater.inflate(customNavId, null);
mNavigationMode = ActionBar.NAVIGATION_MODE_CUSTOM;
- addView(mCustomNavView);
}
mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
@@ -188,6 +188,12 @@ public class ActionBarView extends ViewGroup {
}
}
+ @Override
+ public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
+ // No starting an action mode for an action bar child! (Where would it go?)
+ return null;
+ }
+
public void setCallback(NavigationCallback callback) {
mCallback = callback;
}
@@ -553,7 +559,7 @@ public class ActionBarView extends ViewGroup {
if (mSpinner != null) {
mSpinner.measure(
MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
}
break;
case ActionBar.NAVIGATION_MODE_CUSTOM:
@@ -563,8 +569,16 @@ public class ActionBarView extends ViewGroup {
MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
final int customNavWidth = lp.width >= 0 ?
Math.min(lp.width, availableWidth) : availableWidth;
- final int customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
- MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
+
+ // If the action bar is wrapping to its content height, don't allow a custom
+ // view to MATCH_PARENT.
+ int customNavHeightMode;
+ if (mContentHeight <= 0) {
+ customNavHeightMode = MeasureSpec.AT_MOST;
+ } else {
+ customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
+ MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
+ }
final int customNavHeight = lp.height >= 0 ?
Math.min(lp.height, height) : height;
mCustomNavView.measure(
@@ -576,7 +590,7 @@ public class ActionBarView extends ViewGroup {
if (mTabLayout != null) {
mTabLayout.measure(
MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
}
break;
}
diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
index 7851347..ae9900c 100644
--- a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
+++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
@@ -20,6 +20,7 @@ import android.widget.RemoteViews;
/** {@hide} */
interface IRemoteViewsFactory {
+ void onDataSetChanged();
int getCount();
RemoteViews getViewAt(int position);
RemoteViews getLoadingView();
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index 3218ba8..4b56cb4 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -474,10 +474,13 @@ public class SlidingTab extends ViewGroup {
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
- if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
- Log.e("SlidingTab", "SlidingTab cannot have UNSPECIFIED MeasureSpec"
- +"(wspec=" + widthSpecMode + ", hspec=" + heightSpecMode + ")",
- new RuntimeException(LOG_TAG + "stack:"));
+ if (DBG) {
+ if (widthSpecMode == MeasureSpec.UNSPECIFIED
+ || heightSpecMode == MeasureSpec.UNSPECIFIED) {
+ Log.e("SlidingTab", "SlidingTab cannot have UNSPECIFIED MeasureSpec"
+ +"(wspec=" + widthSpecMode + ", hspec=" + heightSpecMode + ")",
+ new RuntimeException(LOG_TAG + "stack:"));
+ }
}
mLeftSlider.measure();
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 860b5b7..cb0bdd3 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -82,6 +82,7 @@ LOCAL_SRC_FILES:= \
android_util_Process.cpp \
android_util_StringBlock.cpp \
android_util_XmlBlock.cpp \
+ android/graphics/AutoDecodeCancel.cpp \
android/graphics/Bitmap.cpp \
android/graphics/BitmapFactory.cpp \
android/graphics/Camera.cpp \
@@ -105,6 +106,7 @@ LOCAL_SRC_FILES:= \
android_graphics_PixelFormat.cpp \
android/graphics/Picture.cpp \
android/graphics/PorterDuff.cpp \
+ android/graphics/LargeBitmap.cpp \
android/graphics/Rasterizer.cpp \
android/graphics/Region.cpp \
android/graphics/Shader.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 2dd17bb..dba1cea 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -53,6 +53,7 @@ extern int register_android_os_Binder(JNIEnv* env);
extern int register_android_os_Process(JNIEnv* env);
extern int register_android_graphics_Bitmap(JNIEnv*);
extern int register_android_graphics_BitmapFactory(JNIEnv*);
+extern int register_android_graphics_LargeBitmap(JNIEnv*);
extern int register_android_graphics_Camera(JNIEnv* env);
extern int register_android_graphics_Graphics(JNIEnv* env);
extern int register_android_graphics_Interpolator(JNIEnv* env);
@@ -1256,6 +1257,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_graphics_Bitmap),
REG_JNI(register_android_graphics_BitmapFactory),
+ REG_JNI(register_android_graphics_LargeBitmap),
REG_JNI(register_android_graphics_Camera),
REG_JNI(register_android_graphics_Canvas),
REG_JNI(register_android_graphics_ColorFilter),
diff --git a/core/jni/android/graphics/AutoDecodeCancel.cpp b/core/jni/android/graphics/AutoDecodeCancel.cpp
new file mode 100644
index 0000000..f0739ea
--- /dev/null
+++ b/core/jni/android/graphics/AutoDecodeCancel.cpp
@@ -0,0 +1,100 @@
+#include "AutoDecodeCancel.h"
+
+static SkMutex gAutoDecoderCancelMutex;
+static AutoDecoderCancel* gAutoDecoderCancel;
+#ifdef SK_DEBUG
+static int gAutoDecoderCancelCount;
+#endif
+
+AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
+ SkImageDecoder* decoder) {
+ fJOptions = joptions;
+ fDecoder = decoder;
+
+ if (NULL != joptions) {
+ SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+ // Add us as the head of the list
+ fPrev = NULL;
+ fNext = gAutoDecoderCancel;
+ if (gAutoDecoderCancel) {
+ gAutoDecoderCancel->fPrev = this;
+ }
+ gAutoDecoderCancel = this;
+
+ SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
+ Validate();
+ }
+}
+
+AutoDecoderCancel::~AutoDecoderCancel() {
+ if (NULL != fJOptions) {
+ SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+ // take us out of the dllist
+ AutoDecoderCancel* prev = fPrev;
+ AutoDecoderCancel* next = fNext;
+
+ if (prev) {
+ SkASSERT(prev->fNext == this);
+ prev->fNext = next;
+ } else {
+ SkASSERT(gAutoDecoderCancel == this);
+ gAutoDecoderCancel = next;
+ }
+ if (next) {
+ SkASSERT(next->fPrev == this);
+ next->fPrev = prev;
+ }
+
+ SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
+ Validate();
+ }
+}
+
+bool AutoDecoderCancel::RequestCancel(jobject joptions) {
+ SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+ Validate();
+
+ AutoDecoderCancel* pair = gAutoDecoderCancel;
+ while (pair != NULL) {
+ if (pair->fJOptions == joptions) {
+ pair->fDecoder->cancelDecode();
+ return true;
+ }
+ pair = pair->fNext;
+ }
+ return false;
+}
+
+#ifdef SK_DEBUG
+// can only call this inside a lock on gAutoDecoderCancelMutex
+void AutoDecoderCancel::Validate() {
+ const int gCount = gAutoDecoderCancelCount;
+
+ if (gCount == 0) {
+ SkASSERT(gAutoDecoderCancel == NULL);
+ } else {
+ SkASSERT(gCount > 0);
+
+ AutoDecoderCancel* curr = gAutoDecoderCancel;
+ SkASSERT(curr);
+ SkASSERT(curr->fPrev == NULL);
+
+ int count = 0;
+ while (curr) {
+ count += 1;
+ SkASSERT(count <= gCount);
+ if (curr->fPrev) {
+ SkASSERT(curr->fPrev->fNext == curr);
+ }
+ if (curr->fNext) {
+ SkASSERT(curr->fNext->fPrev == curr);
+ }
+ curr = curr->fNext;
+ }
+ SkASSERT(count == gCount);
+ }
+}
+#endif
diff --git a/core/jni/android/graphics/AutoDecodeCancel.h b/core/jni/android/graphics/AutoDecodeCancel.h
new file mode 100644
index 0000000..37b86f9
--- /dev/null
+++ b/core/jni/android/graphics/AutoDecodeCancel.h
@@ -0,0 +1,27 @@
+#ifndef AutoDecodeCancel_DEFINED
+#define AutoDecodeCancel_DEFINED
+
+#include <jni.h>
+#include "SkImageDecoder.h"
+
+class AutoDecoderCancel {
+public:
+ AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
+ ~AutoDecoderCancel();
+
+ static bool RequestCancel(jobject options);
+
+private:
+ AutoDecoderCancel* fNext;
+ AutoDecoderCancel* fPrev;
+ jobject fJOptions; // java options object
+ SkImageDecoder* fDecoder;
+
+#ifdef SK_DEBUG
+ static void Validate();
+#else
+ static void Validate() {}
+#endif
+};
+
+#endif
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index b41bad0..21b2e3b 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -1,14 +1,15 @@
#define LOG_TAG "BitmapFactory"
+#include "BitmapFactory.h"
#include "SkImageDecoder.h"
#include "SkImageRef_ashmem.h"
#include "SkImageRef_GlobalPool.h"
#include "SkPixelRef.h"
#include "SkStream.h"
-#include "GraphicsJNI.h"
#include "SkTemplates.h"
#include "SkUtils.h"
#include "CreateJavaOutputStreamAdaptor.h"
+#include "AutoDecodeCancel.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/Asset.h>
@@ -16,18 +17,18 @@
#include <netinet/in.h>
#include <sys/mman.h>
-static jclass gOptions_class;
-static jfieldID gOptions_justBoundsFieldID;
-static jfieldID gOptions_sampleSizeFieldID;
-static jfieldID gOptions_configFieldID;
-static jfieldID gOptions_ditherFieldID;
-static jfieldID gOptions_purgeableFieldID;
-static jfieldID gOptions_shareableFieldID;
-static jfieldID gOptions_nativeAllocFieldID;
-static jfieldID gOptions_widthFieldID;
-static jfieldID gOptions_heightFieldID;
-static jfieldID gOptions_mimeFieldID;
-static jfieldID gOptions_mCancelID;
+jclass gOptions_class;
+jfieldID gOptions_justBoundsFieldID;
+jfieldID gOptions_sampleSizeFieldID;
+jfieldID gOptions_configFieldID;
+jfieldID gOptions_ditherFieldID;
+jfieldID gOptions_purgeableFieldID;
+jfieldID gOptions_shareableFieldID;
+jfieldID gOptions_nativeAllocFieldID;
+jfieldID gOptions_widthFieldID;
+jfieldID gOptions_heightFieldID;
+jfieldID gOptions_mimeFieldID;
+jfieldID gOptions_mCancelID;
static jclass gFileDescriptor_class;
static jfieldID gFileDescriptor_descriptor;
@@ -38,129 +39,6 @@ static jfieldID gFileDescriptor_descriptor;
#define TRACE_BITMAP(code)
#endif
-///////////////////////////////////////////////////////////////////////////////
-
-class AutoDecoderCancel {
-public:
- AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
- ~AutoDecoderCancel();
-
- static bool RequestCancel(jobject options);
-
-private:
- AutoDecoderCancel* fNext;
- AutoDecoderCancel* fPrev;
- jobject fJOptions; // java options object
- SkImageDecoder* fDecoder;
-
-#ifdef SK_DEBUG
- static void Validate();
-#else
- static void Validate() {}
-#endif
-};
-
-static SkMutex gAutoDecoderCancelMutex;
-static AutoDecoderCancel* gAutoDecoderCancel;
-#ifdef SK_DEBUG
- static int gAutoDecoderCancelCount;
-#endif
-
-AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
- SkImageDecoder* decoder) {
- fJOptions = joptions;
- fDecoder = decoder;
-
- if (NULL != joptions) {
- SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-
- // Add us as the head of the list
- fPrev = NULL;
- fNext = gAutoDecoderCancel;
- if (gAutoDecoderCancel) {
- gAutoDecoderCancel->fPrev = this;
- }
- gAutoDecoderCancel = this;
-
- SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
- Validate();
- }
-}
-
-AutoDecoderCancel::~AutoDecoderCancel() {
- if (NULL != fJOptions) {
- SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-
- // take us out of the dllist
- AutoDecoderCancel* prev = fPrev;
- AutoDecoderCancel* next = fNext;
-
- if (prev) {
- SkASSERT(prev->fNext == this);
- prev->fNext = next;
- } else {
- SkASSERT(gAutoDecoderCancel == this);
- gAutoDecoderCancel = next;
- }
- if (next) {
- SkASSERT(next->fPrev == this);
- next->fPrev = prev;
- }
-
- SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
- Validate();
- }
-}
-
-bool AutoDecoderCancel::RequestCancel(jobject joptions) {
- SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-
- Validate();
-
- AutoDecoderCancel* pair = gAutoDecoderCancel;
- while (pair != NULL) {
- if (pair->fJOptions == joptions) {
- pair->fDecoder->cancelDecode();
- return true;
- }
- pair = pair->fNext;
- }
- return false;
-}
-
-#ifdef SK_DEBUG
-// can only call this inside a lock on gAutoDecoderCancelMutex
-void AutoDecoderCancel::Validate() {
- const int gCount = gAutoDecoderCancelCount;
-
- if (gCount == 0) {
- SkASSERT(gAutoDecoderCancel == NULL);
- } else {
- SkASSERT(gCount > 0);
-
- AutoDecoderCancel* curr = gAutoDecoderCancel;
- SkASSERT(curr);
- SkASSERT(curr->fPrev == NULL);
-
- int count = 0;
- while (curr) {
- count += 1;
- SkASSERT(count <= gCount);
- if (curr->fPrev) {
- SkASSERT(curr->fPrev->fNext == curr);
- }
- if (curr->fNext) {
- SkASSERT(curr->fNext->fPrev == curr);
- }
- curr = curr->fNext;
- }
- SkASSERT(count == gCount);
- }
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
using namespace android;
class NinePatchPeeker : public SkImageDecoder::Peeker {
@@ -279,7 +157,7 @@ static inline int32_t validOrNeg1(bool isValid, int32_t value) {
return ((int32_t)isValid - 1) | value;
}
-static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
+jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
static const struct {
SkImageDecoder::Format fFormat;
const char* fMimeType;
@@ -477,7 +355,7 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
jobject padding,
jobject options) { // BitmapFactory$Options
jobject bitmap = NULL;
- SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage);
+ SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
if (stream) {
// for now we don't allow purgeable with java inputstreams
@@ -682,6 +560,107 @@ static void nativeSetDefaultConfig(JNIEnv* env, jobject, int nativeConfig) {
}
}
+static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream, bool isShareable) {
+ SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
+ int width, height;
+ if (NULL == decoder) {
+ doThrowIOE(env, "Image format not supported");
+ return nullObjectReturn("SkImageDecoder::Factory returned null");
+ }
+
+ JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
+ decoder->setAllocator(javaAllocator);
+ JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
+ decoder->setReporter(javaMemoryReporter);
+ javaAllocator->unref();
+ javaMemoryReporter->unref();
+
+ if (!decoder->buildTileIndex(stream, &width, &height, isShareable)) {
+ char msg[1024];
+ snprintf(msg, 1023, "Image failed to decode using %s decoder", decoder->getFormatName());
+ doThrowIOE(env, msg);
+ return nullObjectReturn("decoder->buildTileIndex returned false");
+ }
+
+ SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height);
+
+ return GraphicsJNI::createLargeBitmap(env, bm);
+}
+
+static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
+ int offset, int length, jboolean isShareable) {
+ AutoJavaByteArray ar(env, byteArray);
+ SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false);
+ SkAutoUnref aur(stream);
+ if (isShareable) {
+ aur.detach();
+ }
+ return doBuildTileIndex(env, stream, isShareable);
+}
+
+static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz,
+ jobject fileDescriptor, jboolean isShareable) {
+ NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
+
+ jint descriptor = env->GetIntField(fileDescriptor,
+ gFileDescriptor_descriptor);
+ bool weOwnTheFD = false;
+
+ if (isShareable) {
+ int newFD = ::dup(descriptor);
+ if (-1 != newFD) {
+ weOwnTheFD = true;
+ descriptor = newFD;
+ }
+ }
+
+ SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
+ SkAutoUnref aur(stream);
+ if (!stream->isValid()) {
+ return NULL;
+ }
+
+ if (isShareable) {
+ aur.detach();
+ }
+
+ /* Restore our offset when we leave, so we can be called more than once
+ with the same descriptor. This is only required if we didn't dup the
+ file descriptor, but it is OK to do it all the time.
+ */
+ AutoFDSeek as(descriptor);
+
+ return doBuildTileIndex(env, stream, isShareable);
+}
+
+static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz,
+ jobject is, // InputStream
+ jbyteArray storage, // byte[]
+ jboolean isShareable) {
+ jobject largeBitmap = NULL;
+ SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
+
+ if (stream) {
+ // for now we don't allow shareable with java inputstreams
+ largeBitmap = doBuildTileIndex(env, stream, false);
+ stream->unref();
+ }
+ return largeBitmap;
+}
+
+static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz,
+ jint native_asset, // Asset
+ jboolean isShareable) {
+ SkStream* stream;
+ Asset* asset = reinterpret_cast<Asset*>(native_asset);
+ stream = new AssetStreamAdaptor(asset);
+ SkAutoUnref aur(stream);
+ if (isShareable) {
+ aur.detach();
+ }
+ return doBuildTileIndex(env, stream, isShareable);
+}
+
///////////////////////////////////////////////////////////////////////////////
static JNINativeMethod gMethods[] = {
@@ -711,6 +690,26 @@ static JNINativeMethod gMethods[] = {
},
{ "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
+
+ { "nativeCreateLargeBitmap",
+ "([BIIZ)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromByteArray
+ },
+
+ { "nativeCreateLargeBitmap",
+ "(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromStream
+ },
+
+ { "nativeCreateLargeBitmap",
+ "(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromFileDescriptor
+ },
+
+ { "nativeCreateLargeBitmap",
+ "(IZ)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromAsset
+ },
};
static JNINativeMethod gOptionsMethods[] = {
diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h
new file mode 100644
index 0000000..f868434
--- /dev/null
+++ b/core/jni/android/graphics/BitmapFactory.h
@@ -0,0 +1,21 @@
+#ifndef BitmapFactory_DEFINE
+#define BitmapFactory_DEFINE
+
+#include "GraphicsJNI.h"
+
+extern jclass gOptions_class;
+extern jfieldID gOptions_justBoundsFieldID;
+extern jfieldID gOptions_sampleSizeFieldID;
+extern jfieldID gOptions_configFieldID;
+extern jfieldID gOptions_ditherFieldID;
+extern jfieldID gOptions_purgeableFieldID;
+extern jfieldID gOptions_shareableFieldID;
+extern jfieldID gOptions_nativeAllocFieldID;
+extern jfieldID gOptions_widthFieldID;
+extern jfieldID gOptions_heightFieldID;
+extern jfieldID gOptions_mimeFieldID;
+extern jfieldID gOptions_mCancelID;
+
+jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format);
+
+#endif
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
index 007757f..137acc6 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
@@ -5,6 +5,7 @@
static jclass gInputStream_Clazz;
static jmethodID gInputStream_resetMethodID;
+static jmethodID gInputStream_markMethodID;
static jmethodID gInputStream_availableMethodID;
static jmethodID gInputStream_readMethodID;
static jmethodID gInputStream_skipMethodID;
@@ -143,7 +144,7 @@ private:
};
SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
- jbyteArray storage) {
+ jbyteArray storage, int markSize) {
static bool gInited;
if (!gInited) {
@@ -153,6 +154,8 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
gInputStream_resetMethodID = env->GetMethodID(gInputStream_Clazz,
"reset", "()V");
+ gInputStream_markMethodID = env->GetMethodID(gInputStream_Clazz,
+ "mark", "(I)V");
gInputStream_availableMethodID = env->GetMethodID(gInputStream_Clazz,
"available", "()I");
gInputStream_readMethodID = env->GetMethodID(gInputStream_Clazz,
@@ -161,6 +164,7 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
"skip", "(J)J");
RETURN_NULL_IF_NULL(gInputStream_resetMethodID);
+ RETURN_NULL_IF_NULL(gInputStream_markMethodID);
RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
RETURN_NULL_IF_NULL(gInputStream_skipMethodID);
@@ -168,6 +172,10 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
gInited = true;
}
+ if (markSize) {
+ env->CallVoidMethod(stream, gInputStream_markMethodID, markSize);
+ }
+
return new JavaInputStreamAdaptor(env, stream, storage);
}
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
index cf21dde..c34c96a 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
@@ -6,7 +6,7 @@
#include "SkStream.h"
SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
- jbyteArray storage);
+ jbyteArray storage, int markSize = 0);
SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
jbyteArray storage);
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 5659ba2..204bb74 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -46,6 +46,10 @@ void doThrowOOME(JNIEnv* env, const char* msg) {
doThrow(env, "java/lang/OutOfMemoryError", msg);
}
+void doThrowIOE(JNIEnv* env, const char* msg) {
+ doThrow(env, "java/lang/IOException", msg);
+}
+
bool GraphicsJNI::hasException(JNIEnv *env) {
if (env->ExceptionCheck() != 0) {
LOGE("*** Uncaught exception returned from Java call!\n");
@@ -165,6 +169,9 @@ static jmethodID gBitmap_allocBufferMethodID;
static jclass gBitmapConfig_class;
static jfieldID gBitmapConfig_nativeInstanceID;
+static jclass gLargeBitmap_class;
+static jmethodID gLargeBitmap_constructorMethodID;
+
static jclass gCanvas_class;
static jfieldID gCanvas_nativeInstanceID;
@@ -370,6 +377,23 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
}
return obj;
}
+jobject GraphicsJNI::createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap)
+{
+ SkASSERT(bitmap != NULL);
+
+ jobject obj = env->AllocObject(gLargeBitmap_class);
+ if (hasException(env)) {
+ obj = NULL;
+ return obj;
+ }
+ if (obj) {
+ env->CallVoidMethod(obj, gLargeBitmap_constructorMethodID, (jint)bitmap);
+ if (hasException(env)) {
+ obj = NULL;
+ }
+ }
+ return obj;
+}
jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
{
@@ -502,6 +526,35 @@ bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
////////////////////////////////////////////////////////////////////////////////
+JavaMemoryUsageReporter::JavaMemoryUsageReporter(JNIEnv* env)
+ : fEnv(env), fTotalSize(0) {}
+
+JavaMemoryUsageReporter::~JavaMemoryUsageReporter() {
+ jlong jtotalSize = fTotalSize;
+ fEnv->CallVoidMethod(gVMRuntime_singleton,
+ gVMRuntime_trackExternalFreeMethodID,
+ jtotalSize);
+}
+
+bool JavaMemoryUsageReporter::reportMemory(size_t memorySize) {
+ jlong jsize = memorySize; // the VM wants longs for the size
+ bool r = fEnv->CallBooleanMethod(gVMRuntime_singleton,
+ gVMRuntime_trackExternalAllocationMethodID,
+ jsize);
+ if (GraphicsJNI::hasException(fEnv)) {
+ return false;
+ }
+ if (!r) {
+ LOGE("VM won't let us allocate %zd bytes\n", memorySize);
+ doThrowOOME(fEnv, "bitmap size exceeds VM budget");
+ return false;
+ }
+ fTotalSize += memorySize;
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
static jclass make_globalref(JNIEnv* env, const char classname[])
{
jclass c = env->FindClass(classname);
@@ -547,6 +600,9 @@ int register_android_graphics_Graphics(JNIEnv* env)
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
"(IZ[BI)V");
+ gLargeBitmap_class = make_globalref(env, "android/graphics/LargeBitmap");
+ gLargeBitmap_constructorMethodID = env->GetMethodID(gLargeBitmap_class, "<init>", "(I)V");
+
gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
"nativeInt", "I");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index fe24b05..8d6528b 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -4,6 +4,8 @@
#include "SkPoint.h"
#include "SkRect.h"
#include "SkBitmap.h"
+#include "../images/SkLargeBitmap.h"
+#include "../images/SkImageDecoder.h"
#include <jni.h>
class SkCanvas;
@@ -54,6 +56,8 @@ public:
static jobject createRegion(JNIEnv* env, SkRegion* region);
+ static jobject createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap);
+
/** Set a pixelref for the bitmap (needs setConfig to already be called)
Returns true on success. If it returns false, then it failed, and the
appropriate exception will have been raised.
@@ -80,6 +84,18 @@ private:
bool fReportSizeToVM;
};
+class JavaMemoryUsageReporter : public SkVMMemoryReporter {
+public:
+ JavaMemoryUsageReporter(JNIEnv* env);
+ virtual ~JavaMemoryUsageReporter();
+ // overrides
+ virtual bool reportMemory(size_t memorySize);
+
+private:
+ JNIEnv* fEnv;
+ size_t fTotalSize;
+};
+
enum JNIAccess {
kRO_JNIAccess,
kRW_JNIAccess
@@ -156,6 +172,7 @@ void doThrowIAE(JNIEnv* env, const char* msg = NULL); // Illegal Argument
void doThrowRE(JNIEnv* env, const char* msg = NULL); // Runtime
void doThrowISE(JNIEnv* env, const char* msg = NULL); // Illegal State
void doThrowOOME(JNIEnv* env, const char* msg = NULL); // Out of memory
+void doThrowIOE(JNIEnv* env, const char* msg = NULL); // IO Exception
#define NPE_CHECK_RETURN_ZERO(env, object) \
do { if (NULL == (object)) { doThrowNPE(env); return 0; } } while (0)
diff --git a/core/jni/android/graphics/LargeBitmap.cpp b/core/jni/android/graphics/LargeBitmap.cpp
new file mode 100644
index 0000000..4cf5dfa
--- /dev/null
+++ b/core/jni/android/graphics/LargeBitmap.cpp
@@ -0,0 +1,138 @@
+#define LOG_TAG "LargeBitmap"
+
+#include "SkBitmap.h"
+#include "SkImageEncoder.h"
+#include "SkColorPriv.h"
+#include "GraphicsJNI.h"
+#include "SkDither.h"
+#include "SkUnPreMultiply.h"
+#include "SkUtils.h"
+#include "SkTemplates.h"
+#include "SkPixelRef.h"
+#include "BitmapFactory.h"
+#include "AutoDecodeCancel.h"
+#include "SkLargeBitmap.h"
+
+#include <binder/Parcel.h>
+#include "android_util_Binder.h"
+#include "android_nio_utils.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+#include <jni.h>
+
+#if 0
+ #define TRACE_BITMAP(code) code
+#else
+ #define TRACE_BITMAP(code)
+#endif
+
+static jobject nullObjectReturn(const char msg[]) {
+ if (msg) {
+ SkDebugf("--- %s\n", msg);
+ }
+ return NULL;
+}
+
+/*
+ * nine patch not supported
+ *
+ * purgeable not supported
+ * reportSizeToVM not supported
+ */
+static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkLargeBitmap *bm,
+ int start_x, int start_y, int width, int height, jobject options) {
+ SkImageDecoder *decoder = bm->getDecoder();
+ int sampleSize = 1;
+ SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
+ bool doDither = true;
+
+ if (NULL != options) {
+ sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
+ // initialize these, in case we fail later on
+ env->SetIntField(options, gOptions_widthFieldID, -1);
+ env->SetIntField(options, gOptions_heightFieldID, -1);
+ env->SetObjectField(options, gOptions_mimeFieldID, 0);
+
+ jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
+ prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
+ doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
+ }
+
+ decoder->setDitherImage(doDither);
+ SkBitmap* bitmap = new SkBitmap;
+ SkAutoTDelete<SkBitmap> adb(bitmap);
+ AutoDecoderCancel adc(options, decoder);
+
+ // To fix the race condition in case "requestCancelDecode"
+ // happens earlier than AutoDecoderCancel object is added
+ // to the gAutoDecoderCancelMutex linked list.
+ if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
+ return nullObjectReturn("gOptions_mCancelID");;
+ }
+
+ SkIRect region;
+ region.fLeft = start_x;
+ region.fTop = start_y;
+ region.fRight = start_x + width;
+ region.fBottom = start_y + height;
+
+ if (!bm->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
+ return nullObjectReturn("decoder->decodeRegion returned false");
+ }
+
+ // update options (if any)
+ if (NULL != options) {
+ env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
+ env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
+ // TODO: set the mimeType field with the data from the codec.
+ // but how to reuse a set of strings, rather than allocating new one
+ // each time?
+ env->SetObjectField(options, gOptions_mimeFieldID,
+ getMimeTypeString(env, decoder->getFormat()));
+ }
+
+ // detach bitmap from its autotdeleter, since we want to own it now
+ adb.detach();
+
+ SkPixelRef* pr;
+ pr = bitmap->pixelRef();
+ // promise we will never change our pixels (great for sharing and pictures)
+ pr->setImmutable();
+ // now create the java bitmap
+ return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
+}
+
+static int nativeGetHeight(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+ return bm->getHeight();
+}
+
+static int nativeGetWidth(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+ return bm->getWidth();
+}
+
+static void nativeClean(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+ delete bm;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+static JNINativeMethod gLargeBitmapMethods[] = {
+ { "nativeDecodeRegion",
+ "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+ (void*)nativeDecodeRegion},
+ { "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
+ { "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
+ { "nativeClean", "(I)V", (void*)nativeClean},
+};
+
+#define kClassPathName "android/graphics/LargeBitmap"
+
+int register_android_graphics_LargeBitmap(JNIEnv* env);
+int register_android_graphics_LargeBitmap(JNIEnv* env)
+{
+ return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+ gLargeBitmapMethods, SK_ARRAY_COUNT(gLargeBitmapMethods));
+}
+
diff --git a/core/res/res/drawable-hdpi/cursor_controller.png b/core/res/res/drawable-hdpi/cursor_controller.png
deleted file mode 100644
index 720aded..0000000
--- a/core/res/res/drawable-hdpi/cursor_controller.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/selection_end_handle.png b/core/res/res/drawable-hdpi/selection_end_handle.png
deleted file mode 100644
index 624ab58..0000000
--- a/core/res/res/drawable-hdpi/selection_end_handle.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/selection_start_handle.png b/core/res/res/drawable-hdpi/selection_start_handle.png
deleted file mode 100644
index 7d6f24c..0000000
--- a/core/res/res/drawable-hdpi/selection_start_handle.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle.png b/core/res/res/drawable-hdpi/text_select_handle.png
new file mode 100644
index 0000000..93a5a15
--- /dev/null
+++ b/core/res/res/drawable-hdpi/text_select_handle.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cursor_controller.png b/core/res/res/drawable-mdpi/cursor_controller.png
deleted file mode 100644
index 1a8a459..0000000
--- a/core/res/res/drawable-mdpi/cursor_controller.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/selection_end_handle.png b/core/res/res/drawable-mdpi/selection_end_handle.png
deleted file mode 100644
index 7e075eb..0000000
--- a/core/res/res/drawable-mdpi/selection_end_handle.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/selection_start_handle.png b/core/res/res/drawable-mdpi/selection_start_handle.png
deleted file mode 100644
index d8022f7..0000000
--- a/core/res/res/drawable-mdpi/selection_start_handle.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle.png b/core/res/res/drawable-mdpi/text_select_handle.png
new file mode 100644
index 0000000..93a5a15
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_select_handle.png
Binary files differ
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 2e7bfdf..4d42a4e 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -869,8 +869,8 @@
<string name="car_mode_disable_notification_message" msgid="668663626721675614">"選取結束車用模式。"</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"數據連線或無線基地台已啟用"</string>
<string name="tethered_notification_message" msgid="3067108323903048927">"輕觸以設定"</string>
- <string name="back_button_label" msgid="2300470004503343439">"上一頁"</string>
- <string name="next_button_label" msgid="1080555104677992408">"下一頁"</string>
+ <string name="back_button_label" msgid="2300470004503343439">"返回"</string>
+ <string name="next_button_label" msgid="1080555104677992408">"繼續"</string>
<!-- no translation found for skip_button_label (1275362299471631819) -->
<skip />
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"高行動資料用量"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 67327b2..4362c04 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -46,6 +46,7 @@
theme does not set this value, meaning it is based on whether the
window is floating. -->
<attr name="backgroundDimEnabled" format="boolean" />
+
<!-- =========== -->
<!-- Text styles -->
@@ -95,6 +96,12 @@
<!-- Text color for urls in search suggestions, used by things like global search and the browser. @hide -->
<attr name="textColorSearchUrl" format="reference|color" />
+
+ <!-- Color of highlighted text, when used in a light theme. @hide -->
+ <attr name="textColorHighlightInverse" format="reference|color" />
+ <!-- Color of link text (URLs), when used in a light theme. @hide -->
+ <attr name="textColorLinkInverse" format="reference|color" />
+
<!-- Search widget more corpus result item background. -->
<attr name="searchWidgetCorpusItemBackground" format="reference|color" />
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index a2316e0..3a0c651 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -54,6 +54,12 @@
<color name="dim_foreground_light_inverse">#bebebe</color>
<color name="dim_foreground_light_inverse_disabled">#80bebebe</color>
<color name="hint_foreground_light">#808080</color>
+ <color name="highlight_background">#cc475925</color>
+ <color name="highlight_background_inverse">#ccd2e461</color>
+ <color name="highlighted_text_dark">#cc475925</color>
+ <color name="highlighted_text_light">#ccd2e461</color>
+ <color name="link_text_dark">#5c5cff</color>
+ <color name="link_text_light">#0000ee</color>
<drawable name="stat_notify_sync_noanim">@drawable/stat_notify_sync_anim0</drawable>
<drawable name="stat_sys_download_done">@drawable/stat_sys_download_anim0</drawable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 91e9a92..05cae9f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -269,6 +269,21 @@
<!-- Default LED off time for notification LED in milliseconds. -->
<integer name="config_defaultNotificationLedOff">2000</integer>
+ <!-- Default value for led color when battery is low on charge -->
+ <integer name="config_notificationsBatteryLowARGB">0xFFFF0000</integer>
+
+ <!-- Default value for led color when battery is medium charged -->
+ <integer name="config_notificationsBatteryMediumARGB">0xFFFFFF00</integer>
+
+ <!-- Default value for led color when battery is fully charged -->
+ <integer name="config_notificationsBatteryFullARGB">0xFF00FF00</integer>
+
+ <!-- Default value for LED on time when the battery is low on charge in miliseconds -->
+ <integer name="config_notificationsBatteryLedOn">125</integer>
+
+ <!-- Default value for LED off time when the battery is low on charge in miliseconds -->
+ <integer name="config_notificationsBatteryLedOff">2875</integer>
+
<!-- Allow the menu hard key to be disabled in LockScreen on some devices -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index fdafaf1..3b38bd5 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -47,6 +47,4 @@
<dimen name="password_keyboard_key_height_numeric">56dip</dimen>
<!-- Default correction for the space key in the password keyboard -->
<dimen name="password_keyboard_spacebar_vertical_correction">4dip</dimen>
- <!-- Distance between the text base line and virtual finger position used to position cursor -->
- <dimen name="cursor_controller_vertical_offset">12dp</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b84a613..f413a12 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1329,7 +1329,7 @@
<!-- Context menu ID for the "Select text..." menu item to switch to text
selection context mode in text views. -->
<public type="id" name="selectTextMode" />
-
+
<public type="style" name="Theme.WithActionBar" />
<public type="style" name="Widget.Spinner.DropDown" />
<public type="style" name="Widget.ActionButton" />
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 2ae3ccd..4294c22 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -640,9 +640,9 @@
<style name="TextAppearance">
<item name="android:textColor">?textColorPrimary</item>
- <item name="android:textColorHighlight">#D077A14B</item>
+ <item name="android:textColorHighlight">?textColorHighlight</item>
<item name="android:textColorHint">?textColorHint</item>
- <item name="android:textColorLink">#5C5CFF</item>
+ <item name="android:textColorLink">?textColorLink</item>
<item name="android:textSize">16sp</item>
<item name="android:textStyle">normal</item>
</style>
@@ -650,7 +650,8 @@
<style name="TextAppearance.Inverse">
<item name="textColor">?textColorPrimaryInverse</item>
<item name="android:textColorHint">?textColorHintInverse</item>
- <item name="android:textColorLink">#0000EE</item>
+ <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+ <item name="android:textColorLink">?textColorLinkInverse</item>
</style>
<style name="TextAppearance.Theme">
@@ -671,6 +672,8 @@
<style name="TextAppearance.Large.Inverse">
<item name="android:textColor">?textColorPrimaryInverse</item>
<item name="android:textColorHint">?textColorHintInverse</item>
+ <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+ <item name="android:textColorLink">?textColorLinkInverse</item>
</style>
<style name="TextAppearance.Medium">
@@ -682,6 +685,8 @@
<style name="TextAppearance.Medium.Inverse">
<item name="android:textColor">?textColorPrimaryInverse</item>
<item name="android:textColorHint">?textColorHintInverse</item>
+ <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+ <item name="android:textColorLink">?textColorLinkInverse</item>
</style>
<style name="TextAppearance.Small">
@@ -693,6 +698,8 @@
<style name="TextAppearance.Small.Inverse">
<item name="android:textColor">?textColorSecondaryInverse</item>
<item name="android:textColorHint">?textColorHintInverse</item>
+ <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+ <item name="android:textColorLink">?textColorLinkInverse</item>
</style>
<style name="TextAppearance.Theme.Dialog" parent="TextAppearance.Theme">
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index f9e20f7..348a7de 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -53,6 +53,10 @@
<item name="textColorHint">@android:color/hint_foreground_dark</item>
<item name="textColorHintInverse">@android:color/hint_foreground_light</item>
<item name="textColorSearchUrl">@android:color/search_url_text</item>
+ <item name="textColorHighlight">@android:color/highlighted_text_dark</item>
+ <item name="textColorHighlightInverse">@android:color/highlighted_text_light</item>
+ <item name="textColorLink">@android:color/link_text_dark</item>
+ <item name="textColorLinkInverse">@android:color/link_text_light</item>
<item name="textAppearanceLarge">@android:style/TextAppearance.Large</item>
<item name="textAppearanceMedium">@android:style/TextAppearance.Medium</item>
@@ -233,7 +237,7 @@
<item name="colorBackground">@android:color/background_light</item>
<item name="colorForeground">@color/bright_foreground_light</item>
<item name="colorForegroundInverse">@android:color/bright_foreground_light_inverse</item>
-
+
<item name="textColorPrimary">@android:color/primary_text_light</item>
<item name="textColorSecondary">@android:color/secondary_text_light</item>
<item name="textColorTertiary">@android:color/tertiary_text_light</item>
@@ -247,7 +251,11 @@
<item name="textColorPrimaryInverseNoDisable">@android:color/primary_text_dark_nodisable</item>
<item name="textColorSecondaryInverseNoDisable">@android:color/secondary_text_dark_nodisable</item>
<item name="textColorHint">@android:color/hint_foreground_light</item>
- <item name="textColorHintInverse">@android:color/hint_foreground_dark</item>
+ <item name="textColorHintInverse">@android:color/hint_foreground_dark</item>
+ <item name="textColorHighlight">@android:color/highlighted_text_light</item>
+ <item name="textColorHighlightInverse">@android:color/highlighted_text_dark</item>
+ <item name="textColorLink">@android:color/link_text_light</item>
+ <item name="textColorLinkInverse">@android:color/link_text_dark</item>
<item name="popupWindowStyle">@android:style/Widget.PopupWindow</item>
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 320fc4d..5b8d62c 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -27,6 +27,7 @@ import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.FileNotFoundException;
/**
* Creates Bitmap objects from various sources, including files, streams,
@@ -582,6 +583,139 @@ public class BitmapFactory {
nativeSetDefaultConfig(config.nativeInt);
}
+ /**
+ * Create a LargeBitmap from the specified byte array.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param data byte array of compressed image data.
+ * @param offset offset into data for where the decoder should begin
+ * parsing.
+ * @param length the number of bytes, beginning at offset, to parse
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(byte[] data,
+ int offset, int length, boolean isShareable) throws IOException {
+ if ((offset | length) < 0 || data.length < offset + length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ return nativeCreateLargeBitmap(data, offset, length, isShareable);
+ }
+
+ /**
+ * Create a LargeBitmap from the file descriptor.
+ * The position within the descriptor will not be changed when
+ * this returns, so the descriptor can be used again as is.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param fd The file descriptor containing the data to decode
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(
+ FileDescriptor fd, boolean isShareable) throws IOException {
+ if (MemoryFile.isMemoryFile(fd)) {
+ int mappedlength = MemoryFile.getSize(fd);
+ MemoryFile file = new MemoryFile(fd, mappedlength, "r");
+ InputStream is = file.getInputStream();
+ return createLargeBitmap(is, isShareable);
+ }
+ return nativeCreateLargeBitmap(fd, isShareable);
+ }
+
+ /**
+ * Create a LargeBitmap from an input stream.
+ * The stream's position will be where ever it was after the encoded data
+ * was read.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param is The input stream that holds the raw data to be decoded into a
+ * LargeBitmap.
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(InputStream is,
+ boolean isShareable) throws IOException {
+ // we need mark/reset to work properly in JNI
+
+ if (!is.markSupported()) {
+ is = new BufferedInputStream(is, 16 * 1024);
+ }
+
+ if (is instanceof AssetManager.AssetInputStream) {
+ return nativeCreateLargeBitmap(
+ ((AssetManager.AssetInputStream) is).getAssetInt(),
+ isShareable);
+ } else {
+ // pass some temp storage down to the native code. 1024 is made up,
+ // but should be large enough to avoid too many small calls back
+ // into is.read(...).
+ byte [] tempStorage = null;
+ tempStorage = new byte[16 * 1024];
+ return nativeCreateLargeBitmap(is, tempStorage, isShareable);
+ }
+ }
+
+ /**
+ * Create a LargeBitmap from a file path.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param pathName complete path name for the file to be decoded.
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(String pathName,
+ boolean isShareable) throws FileNotFoundException, IOException {
+ LargeBitmap bm = null;
+ InputStream stream = null;
+
+ try {
+ stream = new FileInputStream(pathName);
+ bm = createLargeBitmap(stream, isShareable);
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // do nothing here
+ }
+ }
+ }
+ return bm;
+ }
+
private static native void nativeSetDefaultConfig(int nativeConfig);
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
Rect padding, Options opts);
@@ -591,5 +725,14 @@ public class BitmapFactory {
private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
int length, Options opts);
private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad);
+
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ byte[] data, int offset, int length, boolean isShareable);
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ FileDescriptor fd, boolean isShareable);
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ InputStream is, byte[] storage, boolean isShareable);
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ int asset, boolean isShareable);
}
diff --git a/graphics/java/android/graphics/LargeBitmap.java b/graphics/java/android/graphics/LargeBitmap.java
new file mode 100644
index 0000000..6656b17
--- /dev/null
+++ b/graphics/java/android/graphics/LargeBitmap.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.DisplayMetrics;
+
+import java.io.OutputStream;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+
+/**
+ * LargeBitmap can be used to decode a rectangle region from an image.
+ * LargeBimap is particularly useful when an original image is large and
+ * you only need parts of the image.
+ *
+ * To create a LargeBitmap, call BitmapFactory.createLargeBitmap().
+ * Given a LargeBitmap, users can call decodeRegion() repeatedly
+ * to get a decoded Bitmap of the specified region.
+ * @hide
+ */
+public final class LargeBitmap {
+ private int mNativeLargeBitmap;
+ private boolean mRecycled;
+
+ /* Private constructor that must received an already allocated native
+ large bitmap int (pointer).
+
+ This can be called from JNI code.
+ */
+ private LargeBitmap(int lbm) {
+ mNativeLargeBitmap = lbm;
+ mRecycled = false;
+ }
+
+ /**
+ * Decodes a rectangle region in the image specified by rect.
+ *
+ * @param rect The rectangle that specified the region to be decode.
+ * @param opts null-ok; Options that control downsampling.
+ * inPurgeable is not supported.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded.
+ */
+ public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
+ checkRecycled("decodeRegion called on recycled large bitmap");
+ if (rect.left < 0 || rect.top < 0 || rect.right > getWidth() || rect.bottom > getHeight())
+ throw new IllegalArgumentException("rectangle is not inside the image");
+ return nativeDecodeRegion(mNativeLargeBitmap, rect.left, rect.top,
+ rect.right - rect.left, rect.bottom - rect.top, options);
+ }
+
+ /** Returns the original image's width */
+ public int getWidth() {
+ checkRecycled("getWidth called on recycled large bitmap");
+ return nativeGetWidth(mNativeLargeBitmap);
+ }
+
+ /** Returns the original image's height */
+ public int getHeight() {
+ checkRecycled("getHeight called on recycled large bitmap");
+ return nativeGetHeight(mNativeLargeBitmap);
+ }
+
+ /**
+ * Frees up the memory associated with this large bitmap, and mark the
+ * large bitmap as "dead", meaning it will throw an exception if decodeRegion(),
+ * getWidth() or getHeight() is called.
+ * This operation cannot be reversed, so it should only be called if you are
+ * sure there are no further uses for the large bitmap. This is an advanced call,
+ * and normally need not be called, since the normal GC process will free up this
+ * memory when there are no more references to this bitmap.
+ */
+ public void recycle() {
+ if (!mRecycled) {
+ nativeClean(mNativeLargeBitmap);
+ mRecycled = true;
+ }
+ }
+
+ /**
+ * Returns true if this large bitmap has been recycled.
+ * If so, then it is an error to try use its method.
+ *
+ * @return true if the large bitmap has been recycled
+ */
+ public final boolean isRecycled() {
+ return mRecycled;
+ }
+
+ /**
+ * Called by methods that want to throw an exception if the bitmap
+ * has already been recycled.
+ */
+ private void checkRecycled(String errorMessage) {
+ if (mRecycled) {
+ throw new IllegalStateException(errorMessage);
+ }
+ }
+
+ protected void finalize() {
+ recycle();
+ }
+
+ private static native Bitmap nativeDecodeRegion(int lbm,
+ int start_x, int start_y, int width, int height,
+ BitmapFactory.Options options);
+ private static native int nativeGetWidth(int lbm);
+ private static native int nativeGetHeight(int lbm);
+ private static native void nativeClean(int lbm);
+}
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 2e1e8d8..be96935 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -128,6 +128,12 @@ private:
// Write the first chunk from the given ChunkInfo.
void writeFirstChunk(ChunkInfo* info);
+ // Adjust other track media clock (presumably wall clock)
+ // based on audio track media clock with the drift time.
+ int64_t mDriftTimeUs;
+ void addDriftTimeUs(int64_t driftTimeUs);
+ int64_t getDriftTimeUs();
+
void lock();
void unlock();
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index ab1fa4f..43354c2 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -86,10 +86,10 @@ enum {
// Track authoring progress status
// kKeyTrackTimeStatus is used to track progress in elapsed time
- // kKeyTrackFrameStatus is used to track progress in authored frames
- kKeyTrackFrameStatus = 'tkfm', // int32_t
kKeyTrackTimeStatus = 'tktm', // int64_t
+ kKeyNotRealTime = 'ntrt', // bool (int32_t)
+
};
enum {
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index dab35b3..05cd96d 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -224,6 +224,7 @@ private:
uint8_t* keyBitmask;
KeyLayoutMap* layoutMap;
String8 keylayoutFilename;
+ int fd;
device_t* next;
device_t(int32_t _id, const char* _path, const char* name);
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 74d50e4..264ad3d 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -260,10 +260,10 @@ void Matrix4::mapRect(Rect& r) const {
}
float vertices[] = {
- r.left, r.top,
- r.right, r.top,
- r.right, r.bottom,
- r.left, r.bottom
+ r.left, r.top,
+ r.right, r.top,
+ r.right, r.bottom,
+ r.left, r.bottom
};
float x, y, z;
@@ -297,8 +297,8 @@ void Matrix4::mapRect(Rect& r) const {
void Matrix4::dump() const {
LOGD("Matrix4[simple=%d", mSimpleMatrix);
- LOGD(" %f %f %f %f", data[kScaleX], data[kSkewX], data[ 8], data[kTranslateX]);
- LOGD(" %f %f %f %f", data[kSkewY], data[kScaleY], data[ 9], data[kTranslateY]);
+ LOGD(" %f %f %f %f", data[kScaleX], data[kSkewX], data[8], data[kTranslateX]);
+ LOGD(" %f %f %f %f", data[kSkewY], data[kScaleY], data[9], data[kTranslateY]);
LOGD(" %f %f %f %f", data[2], data[6], data[kScaleZ], data[kTranslateZ]);
LOGD(" %f %f %f %f", data[kPerspective0], data[kPerspective1], data[11], data[kPerspective2]);
LOGD("]");
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index da5b9dd..35e17bf 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -183,17 +183,17 @@ OpenGLRenderer::~OpenGLRenderer() {
void OpenGLRenderer::setViewport(int width, int height) {
glViewport(0, 0, width, height);
-
mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
mWidth = width;
mHeight = height;
mFirstSnapshot->height = height;
+ mFirstSnapshot->viewport.set(0, 0, width, height);
}
void OpenGLRenderer::prepare() {
mSnapshot = new Snapshot(mFirstSnapshot);
- mSaveCount = 0;
+ mSaveCount = 1;
glDisable(GL_SCISSOR_TEST);
@@ -219,19 +219,17 @@ int OpenGLRenderer::save(int flags) {
}
void OpenGLRenderer::restore() {
- if (mSaveCount == 0) return;
-
- if (restoreSnapshot()) {
+ if (mSaveCount > 1 && restoreSnapshot()) {
setScissorFromClip();
}
}
void OpenGLRenderer::restoreToCount(int saveCount) {
- if (saveCount <= 0 || saveCount > mSaveCount) return;
+ if (saveCount < 1) saveCount = 1;
bool restoreClip = false;
- while (mSaveCount != saveCount - 1) {
+ while (mSaveCount > saveCount) {
restoreClip |= restoreSnapshot();
}
@@ -242,7 +240,7 @@ void OpenGLRenderer::restoreToCount(int saveCount) {
int OpenGLRenderer::saveSnapshot() {
mSnapshot = new Snapshot(mSnapshot);
- return ++mSaveCount;
+ return mSaveCount++;
}
bool OpenGLRenderer::restoreSnapshot() {
@@ -254,6 +252,8 @@ bool OpenGLRenderer::restoreSnapshot() {
sp<Snapshot> previous = mSnapshot->previous;
if (restoreOrtho) {
+ Rect& r = previous->viewport;
+ glViewport(r.left, r.top, r.right, r.bottom);
mOrthoMatrix.load(current->orthoMatrix);
}
@@ -261,42 +261,17 @@ bool OpenGLRenderer::restoreSnapshot() {
composeLayer(current, previous);
}
- mSnapshot = previous;
- mSaveCount--;
-
- return restoreClip;
-}
-
-void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
- if (!current->layer) {
- LOGE("Attempting to compose a layer that does not exist");
- return;
+ bool skip = mSnapshot->skip;
+ if (!skip) {
+ mSaveCount--;
}
+ mSnapshot = previous;
- // Unbind current FBO and restore previous one
- // Most of the time, previous->fbo will be 0 to bind the default buffer
- glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
-
- // Restore the clip from the previous snapshot
- const Rect& clip = previous->clipRect;
- glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight());
-
- Layer* layer = current->layer;
- const Rect& rect = layer->layer;
-
- drawTextureRect(rect.left, rect.top, rect.right, rect.bottom,
- layer->texture, layer->alpha, layer->mode, layer->blend);
-
- LayerSize size(rect.getWidth(), rect.getHeight());
- // Failing to add the layer to the cache should happen only if the
- // layer is too large
- if (!mLayerCache.put(size, layer)) {
- LAYER_LOGD("Deleting layer");
-
- glDeleteFramebuffers(1, &layer->fbo);
- glDeleteTextures(1, &layer->texture);
-
- delete layer;
+ if (!skip) {
+ return restoreClip;
+ } else {
+ bool restorePreviousClip = restoreSnapshot();
+ return restoreClip || restorePreviousClip;
}
}
@@ -355,32 +330,71 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
- // Save the layer in the snapshot
- snapshot->flags |= Snapshot::kFlagIsLayer;
layer->mode = mode;
layer->alpha = alpha / 255.0f;
layer->layer.set(left, top, right, bottom);
+ // Save the layer in the snapshot
+ snapshot->flags |= Snapshot::kFlagIsLayer;
snapshot->layer = layer;
snapshot->fbo = layer->fbo;
// Creates a new snapshot to draw into the FBO
saveSnapshot();
- // TODO: This doesn't preserve other transformations (check Skia first)
+ mSaveCount--;
+
+ mSnapshot->skip = true;
mSnapshot->transform.loadTranslate(-left, -top, 0.0f);
mSnapshot->setClip(0.0f, 0.0f, right - left, bottom - top);
+ mSnapshot->viewport.set(0.0f, 0.0f, right - left, bottom - top);
mSnapshot->height = bottom - top;
+
setScissorFromClip();
mSnapshot->flags = Snapshot::kFlagDirtyOrtho | Snapshot::kFlagClipSet;
mSnapshot->orthoMatrix.load(mOrthoMatrix);
// Change the ortho projection
- mOrthoMatrix.loadOrtho(0.0f, right - left, bottom - top, 0.0f, 0.0f, 1.0f);
+ glViewport(0, 0, right - left, bottom - top);
+ // Don't flip the FBO, it will get flipped when drawing back to the framebuffer
+ mOrthoMatrix.loadOrtho(0.0f, right - left, 0.0f, bottom - top, -1.0f, 1.0f);
return true;
}
+void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
+ if (!current->layer) {
+ LOGE("Attempting to compose a layer that does not exist");
+ return;
+ }
+
+ // Unbind current FBO and restore previous one
+ // Most of the time, previous->fbo will be 0 to bind the default buffer
+ glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
+
+ // Restore the clip from the previous snapshot
+ const Rect& clip = previous->clipRect;
+ glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight());
+
+ Layer* layer = current->layer;
+ const Rect& rect = layer->layer;
+
+ drawTextureRect(rect.left, rect.top, rect.right, rect.bottom,
+ layer->texture, layer->alpha, layer->mode, layer->blend);
+
+ LayerSize size(rect.getWidth(), rect.getHeight());
+ // Failing to add the layer to the cache should happen only if the
+ // layer is too large
+ if (!mLayerCache.put(size, layer)) {
+ LAYER_LOGD("Deleting layer");
+
+ glDeleteFramebuffers(1, &layer->fbo);
+ glDeleteTextures(1, &layer->texture);
+
+ delete layer;
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Transforms
///////////////////////////////////////////////////////////////////////////////
@@ -424,14 +438,8 @@ const Rect& OpenGLRenderer::getClipBounds() {
}
bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) {
- SkRect sr;
- sr.set(left, top, right, bottom);
-
- SkMatrix m;
- mSnapshot->transform.copyTo(m);
- m.mapRect(&sr);
-
- Rect r(sr.fLeft, sr.fTop, sr.fRight, sr.fBottom);
+ Rect r(left, top, right, bottom);
+ mSnapshot->transform.mapRect(r);
return !mSnapshot->clipRect.intersects(r);
}
@@ -562,82 +570,6 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
drawColorRect(left, top, right, bottom, color, mode);
}
-void OpenGLRenderer::renderShadow(const ShadowTexture* texture, float x, float y,
- SkXfermode::Mode mode) {
- const float sx = x - texture->left + mShadowDx;
- const float sy = y - texture->top + mShadowDy;
-
- const GLfloat a = ((mShadowColor >> 24) & 0xFF) / 255.0f;
- const GLfloat r = a * ((mShadowColor >> 16) & 0xFF) / 255.0f;
- const GLfloat g = a * ((mShadowColor >> 8) & 0xFF) / 255.0f;
- const GLfloat b = a * ((mShadowColor ) & 0xFF) / 255.0f;
-
- GLuint textureUnit = 0;
- renderTextureAlpha8(texture, textureUnit, sx, sy, r, g, b, a, mode, false);
-}
-
-void OpenGLRenderer::renderTextureAlpha8(const Texture* texture, GLuint& textureUnit,
- float x, float y, float r, float g, float b, float a, SkXfermode::Mode mode,
- bool applyFilters) {
- // Describe the required shaders
- ProgramDescription description;
- description.hasTexture = true;
- description.hasAlpha8Texture = true;
-
- if (applyFilters) {
- if (mShader) {
- mShader->describe(description, mExtensions);
- }
- if (mColorFilter) {
- mColorFilter->describe(description, mExtensions);
- }
- }
-
- // Build and use the appropriate shader
- useProgram(mProgramCache.get(description));
-
- // Setup the blending mode
- chooseBlending(true, mode);
- bindTexture(texture->id, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit);
- glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit);
-
- int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
- glEnableVertexAttribArray(texCoordsSlot);
-
- // Setup attributes
- glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
- gMeshStride, &mMeshVertices[0].position[0]);
- glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE,
- gMeshStride, &mMeshVertices[0].texture[0]);
-
- // Setup uniforms
- mModelView.loadTranslate(x, y, 0.0f);
- mModelView.scale(texture->width, texture->height, 1.0f);
- mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
-
- glUniform4f(mCurrentProgram->color, r, g, b, a);
-
- textureUnit++;
- if (applyFilters) {
- // Setup attributes and uniforms required by the shaders
- if (mShader) {
- mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
- }
- if (mColorFilter) {
- mColorFilter->setupProgram(mCurrentProgram);
- }
- }
-
- // Draw the mesh
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
-
- glDisableVertexAttribArray(texCoordsSlot);
-}
-
-#define kStdStrikeThru_Offset (-6.0f / 21.0f)
-#define kStdUnderline_Offset (1.0f / 9.0f)
-#define kStdUnderline_Thickness (1.0f / 18.0f)
-
void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
float x, float y, SkPaint* paint) {
if (text == NULL || count == 0 || (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
@@ -668,7 +600,12 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
const ShadowTexture* shadow = mDropShadowCache.get(paint, text, bytesCount,
count, mShadowRadius);
const AutoTexture autoCleanup(shadow);
- renderShadow(shadow, x, y, mode);
+
+ setupShadow(shadow, x, y, mode);
+
+ // Draw the mesh
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+ glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords"));
}
uint32_t color = paint->getColor();
@@ -677,94 +614,19 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
const GLfloat g = a * ((color >> 8) & 0xFF) / 255.0f;
const GLfloat b = a * ((color ) & 0xFF) / 255.0f;
- mModelView.loadIdentity();
-
GLuint textureUnit = 0;
- // Needs to be set prior to calling FontRenderer::getTexture()
glActiveTexture(gTextureUnits[textureUnit]);
- ProgramDescription description;
- description.hasTexture = true;
- description.hasAlpha8Texture = true;
- if (mShader) {
- mShader->describe(description, mExtensions);
- }
- if (mColorFilter) {
- mColorFilter->describe(description, mExtensions);
- }
-
- useProgram(mProgramCache.get(description));
- mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
-
- // Text is always blended, no need to check the shader
- chooseBlending(true, mode);
- bindTexture(mFontRenderer.getTexture(), GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit);
- glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit);
-
- int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
- glEnableVertexAttribArray(texCoordsSlot);
-
- // Always premultiplied
- glUniform4f(mCurrentProgram->color, r, g, b, a);
-
- textureUnit++;
- // Setup attributes and uniforms required by the shaders
- if (mShader) {
- mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
- }
- if (mColorFilter) {
- mColorFilter->setupProgram(mCurrentProgram);
- }
+ setupTextureAlpha8(mFontRenderer.getTexture(), 0, 0, textureUnit, x, y, r, g, b, a,
+ mode, false, true);
const Rect& clip = mSnapshot->getLocalClip();
mFontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDisableVertexAttribArray(texCoordsSlot);
-
- // Handle underline and strike-through
- uint32_t flags = paint->getFlags();
- if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
- float underlineWidth = length;
- // If length is > 0.0f, we already measured the text for the text alignment
- if (length <= 0.0f) {
- underlineWidth = paint->measureText(text, bytesCount);
- }
-
- float offsetX = 0;
- switch (paint->getTextAlign()) {
- case SkPaint::kCenter_Align:
- offsetX = underlineWidth * 0.5f;
- break;
- case SkPaint::kRight_Align:
- offsetX = underlineWidth;
- break;
- default:
- break;
- }
-
- if (underlineWidth > 0.0f) {
- float textSize = paint->getTextSize();
- float height = textSize * kStdUnderline_Thickness;
-
- float left = x - offsetX;
- float top = 0.0f;
- float right = left + underlineWidth;
- float bottom = 0.0f;
-
- if (flags & SkPaint::kUnderlineText_Flag) {
- top = y + textSize * kStdUnderline_Offset;
- bottom = top + height;
- drawRect(left, top, right, bottom, paint);
- }
+ glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords"));
- if (flags & SkPaint::kStrikeThruText_Flag) {
- top = y + textSize * kStdStrikeThru_Offset;
- bottom = top + height;
- drawRect(left, top, right, bottom, paint);
- }
- }
- }
+ drawTextDecorations(text, bytesCount, length, x, y, paint);
}
void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
@@ -787,7 +649,12 @@ void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
const float x = texture->left - texture->offset;
const float y = texture->top - texture->offset;
- renderTextureAlpha8(texture, textureUnit, x, y, r, g, b, a, mode, true);
+
+ setupTextureAlpha8(texture, textureUnit, x, y, r, g, b, a, mode, true, true);
+
+ // Draw the mesh
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+ glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords"));
}
///////////////////////////////////////////////////////////////////////////////
@@ -837,6 +704,134 @@ void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) {
// Drawing implementation
///////////////////////////////////////////////////////////////////////////////
+void OpenGLRenderer::setupShadow(const ShadowTexture* texture, float x, float y,
+ SkXfermode::Mode mode) {
+ const float sx = x - texture->left + mShadowDx;
+ const float sy = y - texture->top + mShadowDy;
+
+ const GLfloat a = ((mShadowColor >> 24) & 0xFF) / 255.0f;
+ const GLfloat r = a * ((mShadowColor >> 16) & 0xFF) / 255.0f;
+ const GLfloat g = a * ((mShadowColor >> 8) & 0xFF) / 255.0f;
+ const GLfloat b = a * ((mShadowColor ) & 0xFF) / 255.0f;
+
+ GLuint textureUnit = 0;
+ setupTextureAlpha8(texture, textureUnit, sx, sy, r, g, b, a, mode, true, false);
+}
+
+void OpenGLRenderer::setupTextureAlpha8(const Texture* texture, GLuint& textureUnit,
+ float x, float y, float r, float g, float b, float a, SkXfermode::Mode mode,
+ bool transforms, bool applyFilters) {
+ setupTextureAlpha8(texture->id, texture->width, texture->height, textureUnit,
+ x, y, r, g, b, a, mode, transforms, applyFilters);
+}
+
+void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height,
+ GLuint& textureUnit, float x, float y, float r, float g, float b, float a,
+ SkXfermode::Mode mode, bool transforms, bool applyFilters) {
+ // Describe the required shaders
+ ProgramDescription description;
+ description.hasTexture = true;
+ description.hasAlpha8Texture = true;
+
+ if (applyFilters) {
+ if (mShader) {
+ mShader->describe(description, mExtensions);
+ }
+ if (mColorFilter) {
+ mColorFilter->describe(description, mExtensions);
+ }
+ }
+
+ // Build and use the appropriate shader
+ useProgram(mProgramCache.get(description));
+
+ // Setup the blending mode
+ chooseBlending(true, mode);
+ bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit);
+ glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit);
+
+ int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
+ glEnableVertexAttribArray(texCoordsSlot);
+
+ // Setup attributes
+ glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
+ gMeshStride, &mMeshVertices[0].position[0]);
+ glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE,
+ gMeshStride, &mMeshVertices[0].texture[0]);
+
+ // Setup uniforms
+ if (transforms) {
+ mModelView.loadTranslate(x, y, 0.0f);
+ mModelView.scale(width, height, 1.0f);
+ } else {
+ mModelView.loadIdentity();
+ }
+ mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+ glUniform4f(mCurrentProgram->color, r, g, b, a);
+
+ textureUnit++;
+ if (applyFilters) {
+ // Setup attributes and uniforms required by the shaders
+ if (mShader) {
+ mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
+ }
+ if (mColorFilter) {
+ mColorFilter->setupProgram(mCurrentProgram);
+ }
+ }
+}
+
+#define kStdStrikeThru_Offset (-6.0f / 21.0f)
+#define kStdUnderline_Offset (1.0f / 9.0f)
+#define kStdUnderline_Thickness (1.0f / 18.0f)
+
+void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float length,
+ float x, float y, SkPaint* paint) {
+ // Handle underline and strike-through
+ uint32_t flags = paint->getFlags();
+ if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+ float underlineWidth = length;
+ // If length is > 0.0f, we already measured the text for the text alignment
+ if (length <= 0.0f) {
+ underlineWidth = paint->measureText(text, bytesCount);
+ }
+
+ float offsetX = 0;
+ switch (paint->getTextAlign()) {
+ case SkPaint::kCenter_Align:
+ offsetX = underlineWidth * 0.5f;
+ break;
+ case SkPaint::kRight_Align:
+ offsetX = underlineWidth;
+ break;
+ default:
+ break;
+ }
+
+ if (underlineWidth > 0.0f) {
+ float textSize = paint->getTextSize();
+ float height = textSize * kStdUnderline_Thickness;
+
+ float left = x - offsetX;
+ float top = 0.0f;
+ float right = left + underlineWidth;
+ float bottom = 0.0f;
+
+ if (flags & SkPaint::kUnderlineText_Flag) {
+ top = y + textSize * kStdUnderline_Offset;
+ bottom = top + height;
+ drawRect(left, top, right, bottom, paint);
+ }
+
+ if (flags & SkPaint::kStrikeThruText_Flag) {
+ top = y + textSize * kStdStrikeThru_Offset;
+ bottom = top + height;
+ drawRect(left, top, right, bottom, paint);
+ }
+ }
+ }
+}
+
void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
int color, SkXfermode::Mode mode, bool ignoreTransform) {
// If a shader is set, preserve only the alpha
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 948ff13..49143a5 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -226,17 +226,17 @@ private:
GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount = 0);
/**
- * Renders the specified shadow.
+ * Prepares the renderer to draw the specified shadow.
*
* @param texture The shadow texture
* @param x The x coordinate of the shadow
* @param y The y coordinate of the shadow
* @param mode The blending mode
*/
- void renderShadow(const ShadowTexture* texture, float x, float y, SkXfermode::Mode mode);
+ void setupShadow(const ShadowTexture* texture, float x, float y, SkXfermode::Mode mode);
/**
- * Renders the specified Alpha8 texture as a rectangle.
+ * Prepares the renderer to draw the specified Alpha8 texture as a rectangle.
*
* @param texture The texture to render with
* @param textureUnit The texture unit to use, may be modified
@@ -247,11 +247,50 @@ private:
* @param b The blue component of the color
* @param a The alpha component of the color
* @param mode The blending mode
+ * @param transforms True if the matrix passed to the shader should be multiplied
+ * by the model-view matrix
* @param applyFilters Whether or not to take color filters and
* shaders into account
*/
- void renderTextureAlpha8(const Texture* texture, GLuint& textureUnit, float x, float y,
- float r, float g, float b, float a, SkXfermode::Mode mode, bool applyFilters);
+ void setupTextureAlpha8(const Texture* texture, GLuint& textureUnit, float x, float y,
+ float r, float g, float b, float a, SkXfermode::Mode mode, bool transforms,
+ bool applyFilters);
+
+ /**
+ * Prepares the renderer to draw the specified Alpha8 texture as a rectangle.
+ *
+ * @param texture The texture to render with
+ * @param width The width of the texture
+ * @param height The height of the texture
+ * @param textureUnit The texture unit to use, may be modified
+ * @param x The x coordinate of the rectangle to draw
+ * @param y The y coordinate of the rectangle to draw
+ * @param r The red component of the color
+ * @param g The green component of the color
+ * @param b The blue component of the color
+ * @param a The alpha component of the color
+ * @param mode The blending mode
+ * @param transforms True if the matrix passed to the shader should be multiplied
+ * by the model-view matrix
+ * @param applyFilters Whether or not to take color filters and
+ * shaders into account
+ */
+ void setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height,
+ GLuint& textureUnit, float x, float y, float r, float g, float b, float a,
+ SkXfermode::Mode mode, bool transforms, bool applyFilters);
+
+ /**
+ * Draws text underline and strike-through if needed.
+ *
+ * @param text The text to decor
+ * @param bytesCount The number of bytes in the text
+ * @param length The length in pixels of the text, can be <= 0.0f to force a measurement
+ * @param x The x coordinate where the text will be drawn
+ * @param y The y coordinate where the text will be drawn
+ * @param paint The paint to draw the text with
+ */
+ void drawTextDecorations(const char* text, int bytesCount, float length,
+ float x, float y, SkPaint* paint);
/**
* Resets the texture coordinates stored in mMeshVertices. Setting the values
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 9495bee..77650a3 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -42,7 +42,7 @@ namespace uirenderer {
*/
class Snapshot: public LightRefBase<Snapshot> {
public:
- Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0) { }
+ Snapshot(): skip(false), flags(0), previous(NULL), layer(NULL), fbo(0) { }
/**
* Copies the specified snapshot. Only the transform and clip rectangle
@@ -54,10 +54,12 @@ public:
height(s->height),
transform(s->transform),
clipRect(s->clipRect),
+ skip(false),
flags(0),
previous(s),
layer(NULL),
- fbo(s->fbo) {
+ fbo(s->fbo),
+ viewport(s->viewport) {
if ((s->flags & Snapshot::kFlagClipSet) &&
!(s->flags & Snapshot::kFlagDirtyLocalClip)) {
localClip.set(s->localClip);
@@ -164,6 +166,12 @@ public:
Rect clipRect;
/**
+ * This snapshot should be skipped. Snapshots marked as skipped are
+ * created by the renderer and should be hidden from the user.
+ */
+ bool skip;
+
+ /**
* Dirty flags.
*/
int flags;
@@ -180,6 +188,11 @@ public:
GLuint fbo;
/**
+ * Current viewport.
+ */
+ Rect viewport;
+
+ /**
* Contains the previous ortho matrix.
*/
mat4 orthoMatrix;
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index 9c86187..5fbe9e5 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -47,7 +47,7 @@ struct ShadowText {
ShadowText(const ShadowText& shadow):
paint(shadow.paint), radius(shadow.radius), len(shadow.len), hash(shadow.hash) {
- text = new char[len];
+ text = new char[shadow.len];
memcpy(text, shadow.text, shadow.len);
}
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
index 833bee0..1ef9c93 100644
--- a/libs/rs/rsFont.cpp
+++ b/libs/rs/rsFont.cpp
@@ -130,15 +130,7 @@ void Font::renderUTF(const char *text, uint32_t len, uint32_t start, int numGlyp
// Move to the next character in the array
index = nextIndex;
- CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar);
-
- if(cachedGlyph == NULL) {
- cachedGlyph = cacheGlyph((uint32_t)utfChar);
- }
- // Is the glyph still in texture cache?
- if(!cachedGlyph->mIsValid) {
- updateGlyphCache(cachedGlyph);
- }
+ CachedGlyphInfo *cachedGlyph = getCachedUTFChar(utfChar);
// If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
if(cachedGlyph->mIsValid) {
@@ -154,6 +146,20 @@ void Font::renderUTF(const char *text, uint32_t len, uint32_t start, int numGlyp
}
}
+Font::CachedGlyphInfo* Font::getCachedUTFChar(int32_t utfChar) {
+
+ CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar);
+ if(cachedGlyph == NULL) {
+ cachedGlyph = cacheGlyph((uint32_t)utfChar);
+ }
+ // Is the glyph still in texture cache?
+ if(!cachedGlyph->mIsValid) {
+ updateGlyphCache(cachedGlyph);
+ }
+
+ return cachedGlyph;
+}
+
void Font::updateGlyphCache(CachedGlyphInfo *glyph)
{
FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER );
@@ -225,6 +231,7 @@ Font * Font::create(Context *rsc, const char *name, uint32_t fontSize, uint32_t
bool isInitialized = newFont->init(name, fontSize, dpi);
if(isInitialized) {
activeFonts.push(newFont);
+ rsc->mStateFont.precacheLatin(newFont);
return newFont;
}
@@ -422,6 +429,8 @@ void FontState::initTextTexture()
nextLine += mCacheLines.top()->mMaxHeight;
mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
nextLine += mCacheLines.top()->mMaxHeight;
+ mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
+ nextLine += mCacheLines.top()->mMaxHeight;
mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
nextLine += mCacheLines.top()->mMaxHeight;
mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
@@ -611,12 +620,33 @@ void FontState::appendMeshQuad(float x1, float y1, float z1,
}
}
+uint32_t FontState::getRemainingCacheCapacity() {
+ uint32_t remainingCapacity = 0;
+ float totalPixels = 0;
+ for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
+ remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
+ totalPixels += mCacheLines[i]->mMaxWidth;
+ }
+ remainingCapacity = (remainingCapacity * 100) / totalPixels;
+ return remainingCapacity;
+}
+
+void FontState::precacheLatin(Font *font) {
+ // Remaining capacity is measured in %
+ uint32_t remainingCapacity = getRemainingCacheCapacity();
+ uint32_t precacheIdx = 0;
+ while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
+ font->getCachedUTFChar((int32_t)mLatinPrecache[precacheIdx]);
+ remainingCapacity = getRemainingCacheCapacity();
+ precacheIdx ++;
+ }
+}
+
+
void FontState::renderText(const char *text, uint32_t len, uint32_t startIndex, int numGlyphs, int x, int y)
{
checkInit();
- //String8 text8(text);
-
// Render code here
Font *currentFont = mRSC->getFont();
if(!currentFont) {
@@ -636,6 +666,12 @@ void FontState::renderText(const char *text, uint32_t len, uint32_t startIndex,
issueDrawCommand();
mCurrentQuadIndex = 0;
}
+
+ // We store a string with letters in a rough frequency of occurrence
+ mLatinPrecache = String8(" eisarntolcdugpmhbyfvkwzxjq");
+ mLatinPrecache += String8("EISARNTOLCDUGPMHBYFVKWZXJQ");
+ mLatinPrecache += String8(",.?!()-+@;:`'");
+ mLatinPrecache += String8("0123456789");
}
void FontState::renderText(const char *text, int x, int y)
diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h
index ab229be..defe38b 100644
--- a/libs/rs/rsFont.h
+++ b/libs/rs/rsFont.h
@@ -93,6 +93,7 @@ protected:
bool mHasKerning;
DefaultKeyedVector<uint32_t, CachedGlyphInfo* > mCachedGlyphs;
+ CachedGlyphInfo* getCachedUTFChar(int32_t utfChar);
CachedGlyphInfo *cacheGlyph(uint32_t glyph);
void updateGlyphCache(CachedGlyphInfo *glyph);
@@ -129,9 +130,11 @@ protected:
uint32_t mMaxWidth;
uint32_t mCurrentRow;
uint32_t mCurrentCol;
+ bool mDirty;
CacheTextureLine(uint32_t maxHeight, uint32_t maxWidth, uint32_t currentRow, uint32_t currentCol) :
- mMaxHeight(maxHeight), mMaxWidth(maxWidth), mCurrentRow(currentRow), mCurrentCol(currentCol) {
+ mMaxHeight(maxHeight), mMaxWidth(maxWidth), mCurrentRow(currentRow), mCurrentCol(currentCol),
+ mDirty(false) {
}
bool fitBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) {
@@ -143,6 +146,7 @@ protected:
*retOriginX = mCurrentCol;
*retOriginY = mCurrentRow;
mCurrentCol += bitmap->width;
+ mDirty = true;
return true;
}
@@ -151,6 +155,10 @@ protected:
};
Vector<CacheTextureLine*> mCacheLines;
+ uint32_t getRemainingCacheCapacity();
+
+ void precacheLatin(Font *font);
+ String8 mLatinPrecache;
Context *mRSC;
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index 60de04a..aee4133 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -318,6 +318,12 @@ void ProgramVertex::setTextureMatrix(const rsc_Matrix *m) const
mDirty = true;
}
+void ProgramVertex::getProjectionMatrix(rsc_Matrix *m) const
+{
+ float *f = static_cast<float *>(mConstants[0]->getPtr());
+ memcpy(m, &f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], sizeof(rsc_Matrix));
+}
+
void ProgramVertex::transformToScreen(const Context *rsc, float *v4out, const float *v3in) const
{
float *f = static_cast<float *>(mConstants[0]->getPtr());
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index 1c8b9c8..a17c9ea 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -43,6 +43,7 @@ public:
void addLight(const Light *);
void setProjectionMatrix(const rsc_Matrix *) const;
+ void getProjectionMatrix(rsc_Matrix *) const;
void setModelviewMatrix(const rsc_Matrix *) const;
void setTextureMatrix(const rsc_Matrix *) const;
diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp
index 4b8de76..a7877cd 100644
--- a/libs/rs/rsScriptC_LibGL.cpp
+++ b/libs/rs/rsScriptC_LibGL.cpp
@@ -117,6 +117,12 @@ static void SC_pfConstantColor(RsProgramFragment vpf, float r, float g, float b,
pf->setConstantColor(r, g, b, a);
}
+static void SC_vpGetProjectionMatrix(rsc_Matrix *m)
+{
+ GET_TLS();
+ rsc->getVertex()->getProjectionMatrix(m);
+}
+
//////////////////////////////////////////////////////////////////////////////
// Drawing
@@ -387,6 +393,8 @@ static ScriptCState::SymbolTable_t gSyms[] = {
{ "_Z31rsgProgramVertexLoadModelMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadModelMatrix },
{ "_Z33rsgProgramVertexLoadTextureMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadTextureMatrix },
+ { "_Z35rsgProgramVertexGetProjectionMatrixP12rs_matrix4x4", (void *)&SC_vpGetProjectionMatrix },
+
{ "_Z31rsgProgramFragmentConstantColor19rs_program_fragmentffff", (void *)&SC_pfConstantColor },
{ "_Z11rsgGetWidthv", (void *)&SC_getWidth },
diff --git a/libs/rs/scriptc/rs_core.rsh b/libs/rs/scriptc/rs_core.rsh
index aa9aebc..0bfb3b9 100644
--- a/libs/rs/scriptc/rs_core.rsh
+++ b/libs/rs/scriptc/rs_core.rsh
@@ -767,6 +767,92 @@ static void rsQuaternionGetMatrixUnit(rs_matrix4x4 *m, const rs_quaternion *q) {
m->m[15] = 1.0f;
}
+/////////////////////////////////////////////////////
+// utility funcs
+/////////////////////////////////////////////////////
+void __attribute__((overloadable))
+rsExtractFrustumPlanes(const rs_matrix4x4 *modelViewProj,
+ float4 *left, float4 *right,
+ float4 *top, float4 *bottom,
+ float4 *near, float4 *far) {
+ // x y z w = a b c d in the plane equation
+ left->x = modelViewProj->m[3] + modelViewProj->m[0];
+ left->y = modelViewProj->m[7] + modelViewProj->m[4];
+ left->z = modelViewProj->m[11] + modelViewProj->m[8];
+ left->w = modelViewProj->m[15] + modelViewProj->m[12];
+
+ right->x = modelViewProj->m[3] - modelViewProj->m[0];
+ right->y = modelViewProj->m[7] - modelViewProj->m[4];
+ right->z = modelViewProj->m[11] - modelViewProj->m[8];
+ right->w = modelViewProj->m[15] - modelViewProj->m[12];
+
+ top->x = modelViewProj->m[3] - modelViewProj->m[1];
+ top->y = modelViewProj->m[7] - modelViewProj->m[5];
+ top->z = modelViewProj->m[11] - modelViewProj->m[9];
+ top->w = modelViewProj->m[15] - modelViewProj->m[13];
+
+ bottom->x = modelViewProj->m[3] + modelViewProj->m[1];
+ bottom->y = modelViewProj->m[7] + modelViewProj->m[5];
+ bottom->z = modelViewProj->m[11] + modelViewProj->m[9];
+ bottom->w = modelViewProj->m[15] + modelViewProj->m[13];
+
+ near->x = modelViewProj->m[3] + modelViewProj->m[2];
+ near->y = modelViewProj->m[7] + modelViewProj->m[6];
+ near->z = modelViewProj->m[11] + modelViewProj->m[10];
+ near->w = modelViewProj->m[15] + modelViewProj->m[14];
+
+ far->x = modelViewProj->m[3] - modelViewProj->m[2];
+ far->y = modelViewProj->m[7] - modelViewProj->m[6];
+ far->z = modelViewProj->m[11] - modelViewProj->m[10];
+ far->w = modelViewProj->m[15] - modelViewProj->m[14];
+
+ float len = length(left->xyz);
+ *left /= len;
+ len = length(right->xyz);
+ *right /= len;
+ len = length(top->xyz);
+ *top /= len;
+ len = length(bottom->xyz);
+ *bottom /= len;
+ len = length(near->xyz);
+ *near /= len;
+ len = length(far->xyz);
+ *far /= len;
+}
+
+bool __attribute__((overloadable))
+rsIsSphereInFrustum(float4 *sphere,
+ float4 *left, float4 *right,
+ float4 *top, float4 *bottom,
+ float4 *near, float4 *far) {
+
+ float distToCenter = dot(left->xyz, sphere->xyz) + left->w;
+ if(distToCenter < -sphere->w) {
+ return false;
+ }
+ distToCenter = dot(right->xyz, sphere->xyz) + right->w;
+ if(distToCenter < -sphere->w) {
+ return false;
+ }
+ distToCenter = dot(top->xyz, sphere->xyz) + top->w;
+ if(distToCenter < -sphere->w) {
+ return false;
+ }
+ distToCenter = dot(bottom->xyz, sphere->xyz) + bottom->w;
+ if(distToCenter < -sphere->w) {
+ return false;
+ }
+ distToCenter = dot(near->xyz, sphere->xyz) + near->w;
+ if(distToCenter < -sphere->w) {
+ return false;
+ }
+ distToCenter = dot(far->xyz, sphere->xyz) + far->w;
+ if(distToCenter < -sphere->w) {
+ return false;
+ }
+ return true;
+}
+
/////////////////////////////////////////////////////
// int ops
diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh
index 63bd9d7..c0b2d2d 100644
--- a/libs/rs/scriptc/rs_graphics.rsh
+++ b/libs/rs/scriptc/rs_graphics.rsh
@@ -27,6 +27,9 @@ extern void __attribute__((overloadable))
rsgProgramVertexLoadTextureMatrix(const rs_matrix4x4 *);
extern void __attribute__((overloadable))
+ rsgProgramVertexGetProjectionMatrix(rs_matrix4x4 *);
+
+extern void __attribute__((overloadable))
rsgProgramFragmentConstantColor(rs_program_fragment, float, float, float, float);
extern uint __attribute__((overloadable))
@@ -92,8 +95,6 @@ rsgMeshComputeBoundingBox(rs_mesh mesh, float3 *bBoxMin, float3 *bBoxMax) {
bBoxMax->z = z2;
}
-
-
///////////////////////////////////////////////////////
// misc
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 124f7b3..b1284fe 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -60,7 +60,6 @@
#define ID_MASK 0x0000ffff
#define SEQ_MASK 0x7fff0000
#define SEQ_SHIFT 16
-#define id_to_index(id) ((id&ID_MASK)+1)
#ifndef ABS_MT_TOUCH_MAJOR
#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
@@ -87,7 +86,7 @@ static inline int max(int v1, int v2)
EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
: id(_id), path(_path), name(name), classes(0)
- , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), next(NULL) {
+ , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), fd(-1), next(NULL) {
}
EventHub::device_t::~device_t() {
@@ -151,9 +150,9 @@ status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
struct input_absinfo info;
- if(ioctl(mFDs[id_to_index(device->id)].fd, EVIOCGABS(axis), &info)) {
+ if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
LOGW("Error reading absolute controller %d for device %s fd %d\n",
- axis, device->name.string(), mFDs[id_to_index(device->id)].fd);
+ axis, device->name.string(), device->fd);
return -errno;
}
@@ -182,7 +181,7 @@ int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const {
uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
+ if (ioctl(device->fd,
EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
return test_bit(scanCode, key_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
@@ -205,8 +204,7 @@ int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const
uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
- EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
+ if (ioctl(device->fd, EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
#if 0
for (size_t i=0; i<=KEY_MAX; i++) {
LOGI("(Scan code %d: down=%d)", i, test_bit(i, key_bitmask));
@@ -242,7 +240,7 @@ int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const {
uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
memset(sw_bitmask, 0, sizeof(sw_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
+ if (ioctl(device->fd,
EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
return test_bit(sw, sw_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
@@ -638,6 +636,7 @@ int EventHub::open_device(const char *deviceName)
return -1;
}
+ device->fd = fd;
mFDs[mFDCount].fd = fd;
mFDs[mFDCount].events = POLLIN;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index daa20a8..a616aae 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -731,7 +731,9 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() {
encMeta->setInt32(kKeyChannelCount, mAudioChannels);
encMeta->setInt32(kKeySampleRate, mSampleRate);
encMeta->setInt32(kKeyBitRate, mAudioBitRate);
- encMeta->setInt32(kKeyTimeScale, mAudioTimeScale);
+ if (mAudioTimeScale > 0) {
+ encMeta->setInt32(kKeyTimeScale, mAudioTimeScale);
+ }
OMXClient client;
CHECK_EQ(client.connect(), OK);
@@ -1032,7 +1034,9 @@ status_t StagefrightRecorder::setupVideoEncoder(sp<MediaSource> *source) {
enc_meta->setInt32(kKeyStride, stride);
enc_meta->setInt32(kKeySliceHeight, sliceHeight);
enc_meta->setInt32(kKeyColorFormat, colorFormat);
- enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
+ if (mVideoTimeScale > 0) {
+ enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
+ }
if (mVideoEncoderProfile != -1) {
enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile);
}
@@ -1115,7 +1119,9 @@ status_t StagefrightRecorder::startMPEG4Recording() {
meta->setInt32(kKeyFileType, mOutputFormat);
meta->setInt32(kKeyBitRate, totalBitRate);
meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
- meta->setInt32(kKeyTimeScale, mMovieTimeScale);
+ if (mMovieTimeScale > 0) {
+ meta->setInt32(kKeyTimeScale, mMovieTimeScale);
+ }
if (mTrackEveryTimeDurationUs > 0) {
meta->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
}
@@ -1191,9 +1197,9 @@ status_t StagefrightRecorder::reset() {
mIFramesIntervalSec = 1;
mAudioSourceNode = 0;
mUse64BitFileOffset = false;
- mMovieTimeScale = 1000;
- mAudioTimeScale = 1000;
- mVideoTimeScale = 1000;
+ mMovieTimeScale = -1;
+ mAudioTimeScale = -1;
+ mVideoTimeScale = -1;
mCameraId = 0;
mVideoEncoderProfile = -1;
mVideoEncoderLevel = -1;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 1460f37..f52ec1a 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -72,6 +72,11 @@ private:
bool mIsAudio;
bool mIsMPEG4;
int64_t mTrackDurationUs;
+
+ // For realtime applications, we need to adjust the media clock
+ // for video track based on the audio media clock
+ bool mIsRealTimeRecording;
+ int64_t mMaxTimeStampUs;
int64_t mEstimatedTrackSizeBytes;
int64_t mMaxWriteTimeUs;
int32_t mTimeScale;
@@ -163,6 +168,12 @@ private:
void getCodecSpecificDataFromInputFormatIfPossible();
+ // Determine the track time scale
+ // If it is an audio track, try to use the sampling rate as
+ // the time scale; however, if user chooses the overwrite
+ // value, the user-supplied time scale will be used.
+ void setTimeScale();
+
Track(const Track &);
Track &operator=(const Track &);
};
@@ -429,7 +440,7 @@ void MPEG4Writer::stop() {
mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
mMoovBoxBufferOffset = 0;
CHECK(mMoovBoxBuffer != NULL);
- int32_t duration = (maxDurationUs * mTimeScale) / 1E6;
+ int32_t duration = (maxDurationUs * mTimeScale + 5E5) / 1E6;
beginBox("moov");
@@ -744,10 +755,6 @@ MPEG4Writer::Track::Track(
mReachedEOS(false) {
getCodecSpecificDataFromInputFormatIfPossible();
- if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) {
- mTimeScale = 1000;
- }
-
const char *mime;
mMeta->findCString(kKeyMIMEType, &mime);
mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
@@ -755,6 +762,28 @@ MPEG4Writer::Track::Track(
mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
+ setTimeScale();
+}
+
+void MPEG4Writer::Track::setTimeScale() {
+ LOGV("setTimeScale");
+ // Default time scale
+ mTimeScale = 90000;
+
+ if (mIsAudio) {
+ // Use the sampling rate as the default time scale for audio track.
+ int32_t sampleRate;
+ bool success = mMeta->findInt32(kKeySampleRate, &sampleRate);
+ CHECK(success);
+ mTimeScale = sampleRate;
+ }
+
+ // If someone would like to overwrite the timescale, use user-supplied value.
+ int32_t timeScale;
+ if (mMeta->findInt32(kKeyTimeScale, &timeScale)) {
+ mTimeScale = timeScale;
+ }
+
CHECK(mTimeScale > 0);
}
@@ -940,6 +969,7 @@ status_t MPEG4Writer::startWriterThread() {
mDone = false;
mIsFirstChunk = true;
+ mDriftTimeUs = 0;
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
ChunkInfo info;
@@ -967,6 +997,14 @@ status_t MPEG4Writer::Track::start(MetaData *params) {
startTimeUs = 0;
}
+ mIsRealTimeRecording = true;
+ {
+ int32_t isNotRealTime;
+ if (params && params->findInt32(kKeyNotRealTime, &isNotRealTime)) {
+ mIsRealTimeRecording = (isNotRealTime == 0);
+ }
+ }
+
initTrackingProgressStatus(params);
sp<MetaData> meta = new MetaData;
@@ -1322,10 +1360,16 @@ void MPEG4Writer::Track::threadEntry() {
int32_t nZeroLengthFrames = 0;
int64_t lastTimestampUs = 0; // Previous sample time stamp in ms
int64_t lastDurationUs = 0; // Between the previous two samples in ms
+ int64_t currDurationTicks = 0; // Timescale based ticks
+ int64_t lastDurationTicks = 0; // Timescale based ticks
int32_t sampleCount = 1; // Sample count in the current stts table entry
uint32_t previousSampleSize = 0; // Size of the previous sample
int64_t previousPausedDurationUs = 0;
int64_t timestampUs;
+
+ int64_t wallClockTimeUs = 0;
+ int64_t lastWallClockTimeUs = 0;
+
sp<MetaData> meta_data;
bool collectStats = collectStatisticalData();
@@ -1429,6 +1473,33 @@ void MPEG4Writer::Track::threadEntry() {
}
timestampUs -= previousPausedDurationUs;
+ if (mIsRealTimeRecording && !mIsAudio) {
+ // The minor adjustment on the timestamp is heuristic/experimental
+ // We are adjusting the timestamp to reduce the fluctuation of the duration
+ // of neighboring samples. This in turn helps reduce the track header size,
+ // especially, the number of entries in the "stts" box.
+ if (mNumSamples > 1) {
+ int64_t durationUs = timestampUs + mOwner->getDriftTimeUs() - lastTimestampUs;
+ int64_t diffUs = (durationUs > lastDurationUs)
+ ? durationUs - lastDurationUs
+ : lastDurationUs - durationUs;
+ if (diffUs <= 5000) { // XXX: Magic number 5ms
+ timestampUs = lastTimestampUs + lastDurationUs;
+ } else {
+ timestampUs += mOwner->getDriftTimeUs();
+ }
+ }
+ }
+ CHECK(timestampUs >= 0);
+ if (mNumSamples > 1) {
+ if (timestampUs <= lastTimestampUs) {
+ LOGW("Drop a frame, since it arrives too late!");
+ copy->release();
+ copy = NULL;
+ continue;
+ }
+ }
+
LOGV("time stamp: %lld and previous paused duration %lld",
timestampUs, previousPausedDurationUs);
if (timestampUs > mTrackDurationUs) {
@@ -1438,7 +1509,16 @@ void MPEG4Writer::Track::threadEntry() {
mSampleSizes.push_back(sampleSize);
++mNumSamples;
if (mNumSamples > 2) {
- if (lastDurationUs != timestampUs - lastTimestampUs) {
+ // We need to use the time scale based ticks, rather than the
+ // timestamp itself to determine whether we have to use a new
+ // stts entry, since we may have rounding errors.
+ // The calculation is intended to reduce the accumulated
+ // rounding errors.
+ currDurationTicks =
+ ((timestampUs * mTimeScale + 500000LL) / 1000000LL -
+ (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
+
+ if (currDurationTicks != lastDurationTicks) {
SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
mSttsTableEntries.push_back(sttsEntry);
sampleCount = 1;
@@ -1453,7 +1533,16 @@ void MPEG4Writer::Track::threadEntry() {
previousSampleSize = sampleSize;
}
lastDurationUs = timestampUs - lastTimestampUs;
+ lastDurationTicks = currDurationTicks;
lastTimestampUs = timestampUs;
+ if (mIsRealTimeRecording && mIsAudio) {
+ wallClockTimeUs = systemTime() / 1000;
+ int64_t wallClockDurationUs = wallClockTimeUs - lastWallClockTimeUs;
+ if (mNumSamples > 2) {
+ mOwner->addDriftTimeUs(lastDurationUs - wallClockDurationUs);
+ }
+ lastWallClockTimeUs = wallClockTimeUs;
+ }
if (isSync != 0) {
mStssTableEntries.push_back(mNumSamples);
@@ -1679,6 +1768,18 @@ void MPEG4Writer::Track::logStatisticalData(bool isAudio) {
}
}
+void MPEG4Writer::addDriftTimeUs(int64_t driftTimeUs) {
+ LOGV("addDriftTimeUs: %lld us", driftTimeUs);
+ Mutex::Autolock autolock(mLock);
+ mDriftTimeUs += driftTimeUs;
+}
+
+int64_t MPEG4Writer::getDriftTimeUs() {
+ LOGV("getDriftTimeUs: %lld us", mDriftTimeUs);
+ Mutex::Autolock autolock(mLock);
+ return mDriftTimeUs;
+}
+
void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
LOGV("bufferChunk");
@@ -1709,7 +1810,6 @@ void MPEG4Writer::Track::writeTrackHeader(
LOGV("%s track time scale: %d",
mIsAudio? "Audio": "Video", mTimeScale);
-
time_t now = time(NULL);
int32_t mvhdTimeScale = mOwner->getTimeScale();
int64_t trakDurationUs = getDurationUs();
@@ -2024,10 +2124,18 @@ void MPEG4Writer::Track::writeTrackHeader(
mOwner->beginBox("stts");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(mSttsTableEntries.size());
+ int64_t prevTimestampUs = 0;
for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
it != mSttsTableEntries.end(); ++it) {
mOwner->writeInt32(it->sampleCount);
- int32_t dur = (it->sampleDurationUs * mTimeScale + 5E5) / 1E6;
+
+ // Make sure that we are calculating the sample duration the exactly
+ // same way as we made decision on how to create stts entries.
+ int64_t currTimestampUs = prevTimestampUs + it->sampleDurationUs;
+ int32_t dur = ((currTimestampUs * mTimeScale + 500000LL) / 1000000LL -
+ (prevTimestampUs * mTimeScale + 500000LL) / 1000000LL);
+ prevTimestampUs += (it->sampleCount * it->sampleDurationUs);
+
mOwner->writeInt32(dur);
}
mOwner->endBox(); // stts
diff --git a/packages/DefaultContainerService/res/values-zh-rCN/strings.xml b/packages/DefaultContainerService/res/values-zh-rCN/strings.xml
index 982015c..65928b1 100644
--- a/packages/DefaultContainerService/res/values-zh-rCN/strings.xml
+++ b/packages/DefaultContainerService/res/values-zh-rCN/strings.xml
@@ -20,5 +20,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="service_name" msgid="4841491635055379553">"包裹访问帮助程序"</string>
+ <string name="service_name" msgid="4841491635055379553">"软件包访问帮助程序"</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
index 0aaa370..e7b0509 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
@@ -185,11 +185,11 @@ public abstract class Ticker {
final Segment newSegment = new Segment(n, icon, n.notification.tickerText);
// If there's already a notification schedule for this package and id, remove it.
- for (int i=0; i<initialCount; i++) {
+ for (int i=0; i<mSegments.size(); i++) {
Segment seg = mSegments.get(i);
if (n.id == seg.notification.id && n.pkg.equals(seg.notification.pkg)) {
// just update that one to use this new data instead
- mSegments.remove(i);
+ mSegments.remove(i--); // restart iteration here
}
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index a410fa4..6be5546 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -643,17 +643,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
boolean playSoundEffect = false;
final PanelFeatureState st = getPanelState(featureId, true);
- if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null) {
- if (mActionBar.isOverflowReserved()) {
- if (!mActionBar.isOverflowMenuShowing()) {
- final Callback cb = getCallback();
- if (cb != null &&
- cb.onPreparePanel(featureId, st.createdPanelView, st.menu)) {
- playSoundEffect = mActionBar.showOverflowMenu();
- }
- } else {
- playSoundEffect = mActionBar.hideOverflowMenu();
+ if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
+ mActionBar.isOverflowReserved()) {
+ if (!mActionBar.isOverflowMenuShowing()) {
+ final Callback cb = getCallback();
+ if (cb != null &&
+ cb.onPreparePanel(featureId, st.createdPanelView, st.menu)) {
+ playSoundEffect = mActionBar.showOverflowMenu();
}
+ } else {
+ playSoundEffect = mActionBar.hideOverflowMenu();
}
} else {
if (st.isOpen || st.isHandled) {
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 3059732..f825df9 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -425,7 +425,7 @@ class AppWidgetService extends IAppWidgetService.Stub
}
}
- public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, RemoteViews views, int viewId) {
+ public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
if (appWidgetIds == null) {
return;
}
@@ -437,7 +437,24 @@ class AppWidgetService extends IAppWidgetService.Stub
synchronized (mAppWidgetIds) {
for (int i=0; i<N; i++) {
AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
- notifyAppWidgetViewDataChangedInstanceLocked(id, views, viewId);
+ updateAppWidgetInstanceLocked(id, views, true);
+ }
+ }
+ }
+
+ public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
+ if (appWidgetIds == null) {
+ return;
+ }
+ if (appWidgetIds.length == 0) {
+ return;
+ }
+ final int N = appWidgetIds.length;
+
+ synchronized (mAppWidgetIds) {
+ for (int i=0; i<N; i++) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+ notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
}
}
}
@@ -459,11 +476,17 @@ class AppWidgetService extends IAppWidgetService.Stub
}
void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
+ updateAppWidgetInstanceLocked(id, views, false);
+ }
+
+ void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
// allow for stale appWidgetIds and other badness
// lookup also checks that the calling process can access the appWidgetId
// drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
- id.views = views;
+
+ // We do not want to save this RemoteViews
+ if (!isPartialUpdate) id.views = views;
// is anyone listening?
if (id.host.callbacks != null) {
@@ -479,18 +502,16 @@ class AppWidgetService extends IAppWidgetService.Stub
}
}
- void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, RemoteViews views, int viewId) {
+ void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
// allow for stale appWidgetIds and other badness
// lookup also checks that the calling process can access the appWidgetId
// drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
- id.views = views;
-
// is anyone listening?
if (id.host.callbacks != null) {
try {
// the lock is held, but this is a oneway call
- id.host.callbacks.viewDataChanged(id.appWidgetId, views, viewId);
+ id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
} catch (RemoteException e) {
// It failed; remove the callback. No need to prune because
// we know that this host is still referenced by this instance.
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 67796c6..9d262b6 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -130,11 +130,11 @@ public class NotificationManagerService extends INotificationManager.Stub
private boolean mBatteryFull;
private NotificationRecord mLedNotification;
- private static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on
- private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00; // Charging - orange solid on
- private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on
- private static final int BATTERY_BLINK_ON = 125;
- private static final int BATTERY_BLINK_OFF = 2875;
+ private static int mBatteryLowARGB;
+ private static int mBatteryMediumARGB;
+ private static int mBatteryFullARGB;
+ private static int mBatteryLedOn;
+ private static int mBatteryLedOff;
private static String idDebugString(Context baseContext, String packageName, int id) {
Context c = null;
@@ -453,6 +453,17 @@ public class NotificationManagerService extends INotificationManager.Stub
mDefaultNotificationLedOff = resources.getInteger(
com.android.internal.R.integer.config_defaultNotificationLedOff);
+ mBatteryLowARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLowARGB);
+ mBatteryMediumARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
+ mBatteryFullARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryFullARGB);
+ mBatteryLedOn = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLedOn);
+ mBatteryLedOff = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLedOff);
+
// Don't start allowing notifications until the setup wizard has run once.
// After that, including subsequent boots, init with notifications turned on.
// This works on the first boot because the setup wizard will toggle this
@@ -1071,17 +1082,17 @@ public class NotificationManagerService extends INotificationManager.Stub
// Battery low always shows, other states only show if charging.
if (mBatteryLow) {
if (mBatteryCharging) {
- mBatteryLight.setColor(BATTERY_LOW_ARGB);
+ mBatteryLight.setColor(mBatteryLowARGB);
} else {
// Flash when battery is low and not charging
- mBatteryLight.setFlashing(BATTERY_LOW_ARGB, LightsService.LIGHT_FLASH_TIMED,
- BATTERY_BLINK_ON, BATTERY_BLINK_OFF);
+ mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
+ mBatteryLedOn, mBatteryLedOff);
}
} else if (mBatteryCharging) {
if (mBatteryFull) {
- mBatteryLight.setColor(BATTERY_FULL_ARGB);
+ mBatteryLight.setColor(mBatteryFullARGB);
} else {
- mBatteryLight.setColor(BATTERY_MEDIUM_ARGB);
+ mBatteryLight.setColor(mBatteryMediumARGB);
}
} else {
mBatteryLight.turnOff();
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 2fb481c..4ee89cc 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -151,6 +151,7 @@ class PowerManagerService extends IPowerManager.Stub
static final int INITIAL_KEYBOARD_BRIGHTNESS = Power.BRIGHTNESS_OFF;
private final int MY_UID;
+ private final int MY_PID;
private boolean mDoneBooting = false;
private boolean mBootCompleted = false;
@@ -309,7 +310,7 @@ class PowerManagerService extends IPowerManager.Stub
long ident = Binder.clearCallingIdentity();
try {
PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken,
- MY_UID, mTag);
+ MY_UID, MY_PID, mTag);
mHeld = true;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -434,11 +435,11 @@ class PowerManagerService extends IPowerManager.Stub
}
}
- PowerManagerService()
- {
+ PowerManagerService() {
// Hack to get our uid... should have a func for this.
long token = Binder.clearCallingIdentity();
- MY_UID = Binder.getCallingUid();
+ MY_UID = Process.myUid();
+ MY_PID = Process.myPid();
Binder.restoreCallingIdentity(token);
// XXX remove this when the kernel doesn't timeout wake locks
@@ -573,13 +574,13 @@ class PowerManagerService extends IPowerManager.Stub
private class WakeLock implements IBinder.DeathRecipient
{
- WakeLock(int f, IBinder b, String t, int u) {
+ WakeLock(int f, IBinder b, String t, int u, int p) {
super();
flags = f;
binder = b;
tag = t;
uid = u == MY_UID ? Process.SYSTEM_UID : u;
- pid = Binder.getCallingPid();
+ pid = p;
if (u != MY_UID || (
!"KEEP_SCREEN_ON_FLAG".equals(tag)
&& !"KeyInputQueue".equals(tag))) {
@@ -631,21 +632,23 @@ class PowerManagerService extends IPowerManager.Stub
public void acquireWakeLock(int flags, IBinder lock, String tag) {
int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
if (uid != Process.myUid()) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
}
long ident = Binder.clearCallingIdentity();
try {
synchronized (mLocks) {
- acquireWakeLockLocked(flags, lock, uid, tag);
+ acquireWakeLockLocked(flags, lock, uid, pid, tag);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- public void acquireWakeLockLocked(int flags, IBinder lock, int uid, String tag) {
+ public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag) {
int acquireUid = -1;
+ int acquirePid = -1;
String acquireName = null;
int acquireType = -1;
@@ -657,7 +660,7 @@ class PowerManagerService extends IPowerManager.Stub
WakeLock wl;
boolean newlock;
if (index < 0) {
- wl = new WakeLock(flags, lock, tag, uid);
+ wl = new WakeLock(flags, lock, tag, uid, pid);
switch (wl.flags & LOCK_MASK)
{
case PowerManager.FULL_WAKE_LOCK:
@@ -730,13 +733,14 @@ class PowerManagerService extends IPowerManager.Stub
}
if (newlock) {
acquireUid = wl.uid;
+ acquirePid = wl.pid;
acquireName = wl.tag;
acquireType = wl.monitorType;
}
if (acquireType >= 0) {
try {
- mBatteryStats.noteStartWakelock(acquireUid, acquireName, acquireType);
+ mBatteryStats.noteStartWakelock(acquireUid, acquirePid, acquireName, acquireType);
} catch (RemoteException e) {
// Ignore
}
@@ -756,6 +760,7 @@ class PowerManagerService extends IPowerManager.Stub
private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) {
int releaseUid;
+ int releasePid;
String releaseName;
int releaseType;
@@ -800,13 +805,14 @@ class PowerManagerService extends IPowerManager.Stub
// Unlink the lock from the binder.
wl.binder.unlinkToDeath(wl, 0);
releaseUid = wl.uid;
+ releasePid = wl.pid;
releaseName = wl.tag;
releaseType = wl.monitorType;
if (releaseType >= 0) {
long origId = Binder.clearCallingIdentity();
try {
- mBatteryStats.noteStopWakelock(releaseUid, releaseName, releaseType);
+ mBatteryStats.noteStopWakelock(releaseUid, releasePid, releaseName, releaseType);
} catch (RemoteException e) {
// Ignore
} finally {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 3f5888b..57f93c4 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -8085,12 +8085,12 @@ public class WindowManagerService extends IWindowManager.Stub
if (oldHold != newHold) {
try {
if (oldHold != null) {
- mBatteryStats.noteStopWakelock(oldHold.mUid,
+ mBatteryStats.noteStopWakelock(oldHold.mUid, -1,
"window",
BatteryStats.WAKE_TYPE_WINDOW);
}
if (newHold != null) {
- mBatteryStats.noteStartWakelock(newHold.mUid,
+ mBatteryStats.noteStartWakelock(newHold.mUid, -1,
"window",
BatteryStats.WAKE_TYPE_WINDOW);
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index df930ad..55ec6aa 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import com.android.internal.R;
-import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.AttributeCache;
import com.android.server.IntentResolver;
@@ -39,7 +38,6 @@ import android.app.AppGlobals;
import android.app.ApplicationErrorReport;
import android.app.Dialog;
import android.app.IActivityController;
-import android.app.IActivityManager;
import android.app.IActivityWatcher;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
@@ -50,7 +48,6 @@ import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.ResultInfo;
import android.app.Service;
import android.app.backup.IBackupManager;
import android.content.ActivityNotFoundException;
@@ -95,7 +92,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -110,6 +106,7 @@ import android.util.Slog;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.SparseArray;
+import android.util.TimeUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -200,6 +197,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// The minimum amount of time between successive GC requests for a process.
static final int GC_MIN_INTERVAL = 60*1000;
+ // The rate at which we check for apps using excessive wake locks -- 15 mins.
+ static final int WAKE_LOCK_CHECK_DELAY = 15*60*1000;
+
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_TIMEOUT = 10*1000;
@@ -771,6 +771,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
boolean mDidAppSwitch;
/**
+ * Last time (in realtime) at which we checked for wake lock usage.
+ */
+ long mLastWakeLockCheckTime;
+
+ /**
* Set while we are wanting to sleep, to prevent any
* activities from being started/resumed.
*/
@@ -915,6 +920,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
static final int POST_HEAVY_NOTIFICATION_MSG = 24;
static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26;
+ static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
AlertDialog mUidAlert;
@@ -1174,6 +1180,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} catch (RemoteException e) {
}
} break;
+ case CHECK_EXCESSIVE_WAKE_LOCKS_MSG: {
+ synchronized (ActivityManagerService.this) {
+ checkExcessiveWakeLocksLocked(true);
+ removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ if (mSleeping) {
+ Message nmsg = obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
+ }
+ }
+ } break;
}
}
};
@@ -2560,6 +2576,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mProcDeaths[0]++;
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ stats.noteProcessDiedLocked(app.info.uid, pid);
+ }
+
// Clean up already done if the process has been re-started.
if (app.pid == pid && app.thread != null &&
app.thread.asBinder() == thread.asBinder()) {
@@ -3577,6 +3598,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+ // Start looking for apps that are abusing wake locks.
+ Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
broadcastIntentLocked(null, null,
@@ -5382,6 +5406,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} else {
Slog.w(TAG, "goingToSleep with no resumed activity!");
}
+
+ // Initialize the wake times of all processes.
+ checkExcessiveWakeLocksLocked(false);
+ mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
}
}
@@ -5431,6 +5461,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mWindowManager.setEventDispatching(true);
mSleeping = false;
mMainStack.resumeTopActivityLocked(null);
+ mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
}
}
@@ -11287,6 +11318,62 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ final void checkExcessiveWakeLocksLocked(boolean doKills) {
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ if (mLastWakeLockCheckTime == 0) {
+ doKills = false;
+ }
+ if (stats.isScreenOn()) {
+ doKills = false;
+ }
+ final long curRealtime = SystemClock.elapsedRealtime();
+ final long timeSince = curRealtime - mLastWakeLockCheckTime;
+ mLastWakeLockCheckTime = curRealtime;
+ if (timeSince < (WAKE_LOCK_CHECK_DELAY/3)) {
+ doKills = false;
+ }
+ int i = mLruProcesses.size();
+ while (i > 0) {
+ i--;
+ ProcessRecord app = mLruProcesses.get(i);
+ if (app.curAdj >= HIDDEN_APP_MIN_ADJ) {
+ long wtime;
+ synchronized (stats) {
+ wtime = stats.getProcessWakeTime(app.info.uid,
+ app.pid, curRealtime);
+ }
+ long timeUsed = wtime - app.lastWakeTime;
+ if (false) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Wake for ");
+ app.toShortString(sb);
+ sb.append(": over ");
+ TimeUtils.formatDuration(timeSince, sb);
+ sb.append(" used ");
+ TimeUtils.formatDuration(timeUsed, sb);
+ sb.append(" (");
+ sb.append((timeUsed*100)/timeSince);
+ sb.append("%)");
+ Slog.i(TAG, sb.toString());
+ }
+ // If a process has held a wake lock for more
+ // than 50% of the time during this period,
+ // that sounds pad. Kill!
+ if (doKills && timeSince > 0
+ && ((timeUsed*100)/timeSince) >= 50) {
+ Slog.i(TAG, "Excessive wake lock in " + app.processName
+ + " (pid " + app.pid + "): held " + timeUsed
+ + " during " + timeSince);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "excessive wake lock");
+ Process.killProcessQuiet(app.pid);
+ } else {
+ app.lastWakeTime = wtime;
+ }
+ }
+ }
+ }
+
private final boolean updateOomAdjLocked(
ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) {
app.hiddenAdj = hiddenAdj;
@@ -11309,6 +11396,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Likewise do a gc when an app is moving in to the
// background (such as a service stopping).
scheduleAppGcLocked(app);
+ // And note its current wake lock time.
+ BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ app.lastWakeTime = stats.getProcessWakeTime(app.info.uid,
+ app.pid, SystemClock.elapsedRealtime());
+ }
}
app.setRawAdj = app.curRawAdj;
}
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 37da6f7..7314e04 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -93,31 +93,31 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
return data;
}
- public void noteStartWakelock(int uid, String name, int type) {
+ public void noteStartWakelock(int uid, int pid, String name, int type) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStartWakeLocked(name, type);
+ mStats.noteStartWakeLocked(uid, pid, name, type);
}
}
- public void noteStopWakelock(int uid, String name, int type) {
+ public void noteStopWakelock(int uid, int pid, String name, int type) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStopWakeLocked(name, type);
+ mStats.noteStopWakeLocked(uid, pid, name, type);
}
}
public void noteStartSensor(int uid, int sensor) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStartSensor(sensor);
+ mStats.noteStartSensorLocked(uid, sensor);
}
}
public void noteStopSensor(int uid, int sensor) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.getUidStatsLocked(uid).noteStopSensor(sensor);
+ mStats.noteStopSensorLocked(uid, sensor);
}
}
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 18b1acb..6d1fbab 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.Watchdog;
import android.app.ActivityManager;
import android.app.Dialog;
@@ -27,8 +26,9 @@ import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.PrintWriterPrinter;
+import android.util.TimeUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -74,6 +74,7 @@ class ProcessRecord {
Bundle instrumentationArguments;// as given to us
ComponentName instrumentationResultClass;// copy of instrumentationClass
BroadcastRecord curReceiver;// receiver currently running in the app
+ long lastWakeTime; // How long proc held wake lock at last check
long lastRequestedGc; // When we last asked the app to do a gc
long lastLowMemory; // When we last told the app that memory is low
boolean reportLowMemory; // Set to true when waiting to report low mem
@@ -128,6 +129,8 @@ class ProcessRecord {
ComponentName errorReportReceiver;
void dump(PrintWriter pw, String prefix) {
+ final long now = SystemClock.uptimeMillis();
+
if (info.className != null) {
pw.print(prefix); pw.print("class="); pw.println(info.className);
}
@@ -157,8 +160,9 @@ class ProcessRecord {
pw.print(" curReceiver="); pw.println(curReceiver);
pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
pw.print(starting); pw.print(" lastPss="); pw.println(lastPss);
- pw.print(prefix); pw.print("lastActivityTime="); pw.print(lastActivityTime);
- pw.print(" lruWeight="); pw.println(lruWeight);
+ pw.print(prefix); pw.print("lastActivityTime=");
+ TimeUtils.formatDuration(lastActivityTime, now, pw);
+ pw.print(" lruWeight="); pw.print(lruWeight);
pw.print(" hidden="); pw.print(hidden);
pw.print(" empty="); pw.println(empty);
pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
@@ -177,6 +181,12 @@ class ProcessRecord {
pw.print(" persistentActivities="); pw.println(persistentActivities);
pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
pw.print(" lruSeq="); pw.println(lruSeq);
+ pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime);
+ pw.print(" lastRequestedGc=");
+ TimeUtils.formatDuration(lastRequestedGc, now, pw);
+ pw.print(" lastLowMemory=");
+ TimeUtils.formatDuration(lastLowMemory, now, pw);
+ pw.print(" reportLowMemory="); pw.println(reportLowMemory);
if (killedBackground) {
pw.print(prefix); pw.print("killedBackground="); pw.println(killedBackground);
}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 75365ad..ab5a78d 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -31,6 +31,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Slog;
+import android.util.TimeUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -117,7 +118,10 @@ class ServiceRecord extends Binder {
StartItem si = list.get(i);
pw.print(prefix); pw.print("#"); pw.print(i);
pw.print(" id="); pw.print(si.id);
- if (now != 0) pw.print(" dur="); pw.print(now-si.deliveredTime);
+ if (now != 0) {
+ pw.print(" dur=");
+ TimeUtils.formatDuration(si.deliveredTime, now, pw);
+ }
if (si.deliveryCount != 0) {
pw.print(" dc="); pw.print(si.deliveryCount);
}
@@ -140,18 +144,26 @@ class ServiceRecord extends Binder {
pw.print(prefix); pw.print("permission="); pw.println(permission);
}
long now = SystemClock.uptimeMillis();
- pw.print(prefix); pw.print("baseDir="); pw.print(baseDir);
- if (!resDir.equals(baseDir)) pw.print(" resDir="); pw.print(resDir);
- pw.print(" dataDir="); pw.println(dataDir);
+ long nowReal = SystemClock.elapsedRealtime();
+ pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
+ if (!resDir.equals(baseDir)) pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+ pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
pw.print(prefix); pw.print("app="); pw.println(app);
if (isForeground || foregroundId != 0) {
pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
pw.print(" foregroundId="); pw.print(foregroundId);
pw.print(" foregroundNoti="); pw.println(foregroundNoti);
}
- pw.print(prefix); pw.print("lastActivity="); pw.print(lastActivity-now);
- pw.print(" executingStart="); pw.print(executingStart-now);
- pw.print(" restartTime="); pw.println(restartTime);
+ pw.print(prefix); pw.print("createTime=");
+ TimeUtils.formatDuration(createTime, nowReal, pw);
+ pw.print(" lastActivity=");
+ TimeUtils.formatDuration(lastActivity, now, pw);
+ pw.println("");
+ pw.print(prefix); pw.print(" executingStart=");
+ TimeUtils.formatDuration(executingStart, now, pw);
+ pw.print(" restartTime=");
+ TimeUtils.formatDuration(restartTime, now, pw);
+ pw.println("");
if (startRequested || lastStartId != 0) {
pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
pw.print(" stopIfKilled="); pw.print(stopIfKilled);
@@ -162,13 +174,15 @@ class ServiceRecord extends Binder {
|| restartDelay != 0 || nextRestartTime != 0) {
pw.print(prefix); pw.print("executeNesting="); pw.print(executeNesting);
pw.print(" restartCount="); pw.print(restartCount);
- pw.print(" restartDelay="); pw.print(restartDelay-now);
- pw.print(" nextRestartTime="); pw.print(nextRestartTime-now);
+ pw.print(" restartDelay=");
+ TimeUtils.formatDuration(restartDelay, now, pw);
+ pw.print(" nextRestartTime=");
+ TimeUtils.formatDuration(nextRestartTime, now, pw);
pw.print(" crashCount="); pw.println(crashCount);
}
if (deliveredStarts.size() > 0) {
pw.print(prefix); pw.println("Delivered Starts:");
- dumpStartList(pw, prefix, deliveredStarts, SystemClock.uptimeMillis());
+ dumpStartList(pw, prefix, deliveredStarts, now);
}
if (pendingStarts.size() > 0) {
pw.print(prefix); pw.println("Pending Starts:");
@@ -213,7 +227,8 @@ class ServiceRecord extends Binder {
dataDir = sInfo.applicationInfo.dataDir;
exported = sInfo.exported;
this.restarter = restarter;
- createTime = lastActivity = SystemClock.uptimeMillis();
+ createTime = SystemClock.elapsedRealtime();
+ lastActivity = SystemClock.uptimeMillis();
}
public AppBindRecord retrieveAppBindingLocked(Intent intent,
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index ea6aa94..c1165c7 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -197,6 +197,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
// capabilities of the GPS engine
private int mEngineCapabilities;
+ // true if XTRA is supported
+ private boolean mSupportsXtra;
+
// for calculating time to first fix
private long mFixRequestTime = 0;
// time to first fix for most recent session
@@ -635,6 +638,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
mEnabled = native_init();
if (mEnabled) {
+ mSupportsXtra = native_supports_xtra();
if (mSuplServerHost != null) {
native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
}
@@ -839,7 +843,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
sendMessage(INJECT_NTP_TIME, 0, null);
result = true;
} else if ("force_xtra_injection".equals(command)) {
- if (native_supports_xtra()) {
+ if (mSupportsXtra) {
xtraDownloadRequest();
result = true;
}
@@ -1372,7 +1376,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
handleInjectNtpTime();
break;
case DOWNLOAD_XTRA_DATA:
- if (native_supports_xtra()) {
+ if (mSupportsXtra) {
handleDownloadXtraData();
}
break;
diff --git a/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp
index b80dbc5..146c177 100644
--- a/services/jni/com_android_server_PowerManagerService.cpp
+++ b/services/jni/com_android_server_PowerManagerService.cpp
@@ -22,6 +22,7 @@
#include "jni.h"
#include <limits.h>
#include <android_runtime/AndroidRuntime.h>
+#include <utils/Timers.h>
#include "com_android_server_PowerManagerService.h"
namespace android {
@@ -43,6 +44,11 @@ static Mutex gPowerManagerLock;
static bool gScreenOn;
static bool gScreenBright;
+static nsecs_t gLastEventTime[POWER_MANAGER_LAST_EVENT + 1];
+
+// Throttling interval for user activity calls.
+static const nsecs_t MIN_TIME_BETWEEN_USERACTIVITIES = 500 * 1000000L; // 500ms
+
// ----------------------------------------------------------------------------
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
@@ -67,6 +73,21 @@ bool android_server_PowerManagerService_isScreenBright() {
void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
if (gPowerManagerServiceObj) {
+ // Throttle calls into user activity by event type.
+ // We're a little conservative about argument checking here in case the caller
+ // passes in bad data which could corrupt system state.
+ if (eventType >= 0 && eventType <= POWER_MANAGER_LAST_EVENT) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (eventTime > now) {
+ eventTime = now;
+ }
+
+ if (gLastEventTime[eventType] + MIN_TIME_BETWEEN_USERACTIVITIES > eventTime) {
+ return;
+ }
+ gLastEventTime[eventType] = eventTime;
+ }
+
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.userActivity,
@@ -136,6 +157,12 @@ int register_android_server_PowerManagerService(JNIEnv* env) {
GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, gPowerManagerServiceClassInfo.clazz,
"userActivity", "(JZIZ)V");
+ // Initialize
+ for (int i = 0; i < POWER_MANAGER_LAST_EVENT; i++) {
+ gLastEventTime[i] = LLONG_MIN;
+ }
+ gScreenOn = true;
+ gScreenBright = true;
return 0;
}
diff --git a/services/jni/com_android_server_PowerManagerService.h b/services/jni/com_android_server_PowerManagerService.h
index 9b05f38..7c329b2 100644
--- a/services/jni/com_android_server_PowerManagerService.h
+++ b/services/jni/com_android_server_PowerManagerService.h
@@ -30,6 +30,8 @@ enum {
POWER_MANAGER_LONG_TOUCH_EVENT = 3,
POWER_MANAGER_TOUCH_UP_EVENT = 4,
POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
+
+ POWER_MANAGER_LAST_EVENT = POWER_MANAGER_BUTTON_EVENT, // Last valid event code.
};
extern bool android_server_PowerManagerService_isScreenOn();
diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp
index 3b326df..0f448e0 100644
--- a/services/surfaceflinger/TextureManager.cpp
+++ b/services/surfaceflinger/TextureManager.cpp
@@ -190,7 +190,7 @@ status_t TextureManager::loadTexture(Texture* texture,
return err;
}
- if (texture->target != GL_TEXTURE_2D)
+ if (texture->target != Texture::TEXTURE_2D)
return INVALID_OPERATION;
glBindTexture(GL_TEXTURE_2D, texture->name);
diff --git a/telephony/java/android/telephony/gsm/SmsMessage.java b/telephony/java/android/telephony/gsm/SmsMessage.java
index 37ef912..0c63c37 100644
--- a/telephony/java/android/telephony/gsm/SmsMessage.java
+++ b/telephony/java/android/telephony/gsm/SmsMessage.java
@@ -304,9 +304,9 @@ public class SmsMessage {
int septets = GsmAlphabet.countGsmSeptets(messageBody, !use7bitOnly);
ret[1] = septets;
if (septets > MAX_USER_DATA_SEPTETS) {
- ret[0] = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1;
- ret[2] = MAX_USER_DATA_SEPTETS_WITH_HEADER
- - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER);
+ ret[0] = (septets + (MAX_USER_DATA_SEPTETS_WITH_HEADER - 1)) /
+ MAX_USER_DATA_SEPTETS_WITH_HEADER;
+ ret[2] = (ret[0] * MAX_USER_DATA_SEPTETS_WITH_HEADER) - septets;
} else {
ret[0] = 1;
ret[2] = MAX_USER_DATA_SEPTETS - septets;
@@ -318,9 +318,9 @@ public class SmsMessage {
ret[1] = messageBody.length();
if (octets > MAX_USER_DATA_BYTES) {
// 6 is the size of the user data header
- ret[0] = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
- ret[2] = (MAX_USER_DATA_BYTES_WITH_HEADER
- - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
+ ret[0] = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
+ MAX_USER_DATA_BYTES_WITH_HEADER;
+ ret[2] = ((ret[0] * MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
} else {
ret[0] = 1;
ret[2] = (MAX_USER_DATA_BYTES - octets)/2;
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 65d87f5..e95b2f9 100755
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -442,7 +442,7 @@ public class SmsMessage extends SmsMessageBase {
*/
public static TextEncodingDetails calculateLength(CharSequence messageBody,
boolean use7bitOnly) {
- return BearerData.calcTextEncodingDetails(messageBody.toString(), use7bitOnly);
+ return BearerData.calcTextEncodingDetails(messageBody, use7bitOnly);
}
/**
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index e9fea55..cf06dab 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -402,6 +402,7 @@ public final class BearerData {
/**
* Calculate the message text encoding length, fragmentation, and other details.
*
+ * @param msg message text
* @param force7BitEncoding ignore (but still count) illegal characters if true
* @return septet count, or -1 on failure
*/
@@ -424,9 +425,10 @@ public final class BearerData {
ted.codeUnitCount = msg.length();
int octets = ted.codeUnitCount * 2;
if (octets > MAX_USER_DATA_BYTES) {
- ted.msgCount = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
- ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES_WITH_HEADER
- - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
+ ted.msgCount = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
+ MAX_USER_DATA_BYTES_WITH_HEADER;
+ ted.codeUnitsRemaining = ((ted.msgCount *
+ MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
} else {
ted.msgCount = 1;
ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
@@ -801,7 +803,7 @@ public final class BearerData {
*
* @param bData an instance of BearerData.
*
- * @return data byte array of raw encoded SMS bearer data.
+ * @return byte array of raw encoded SMS bearer data.
*/
public static byte[] encode(BearerData bData) {
bData.hasUserDataHeader = ((bData.userData != null) &&
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 278e1ba..a77484a 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -795,9 +795,10 @@ public class SmsMessage extends SmsMessageBase {
int septets = GsmAlphabet.countGsmSeptets(msgBody, !use7bitOnly);
ted.codeUnitCount = septets;
if (septets > MAX_USER_DATA_SEPTETS) {
- ted.msgCount = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1;
- ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS_WITH_HEADER
- - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER);
+ ted.msgCount = (septets + (MAX_USER_DATA_SEPTETS_WITH_HEADER - 1)) /
+ MAX_USER_DATA_SEPTETS_WITH_HEADER;
+ ted.codeUnitsRemaining = (ted.msgCount *
+ MAX_USER_DATA_SEPTETS_WITH_HEADER) - septets;
} else {
ted.msgCount = 1;
ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS - septets;
@@ -807,9 +808,10 @@ public class SmsMessage extends SmsMessageBase {
int octets = msgBody.length() * 2;
ted.codeUnitCount = msgBody.length();
if (octets > MAX_USER_DATA_BYTES) {
- ted.msgCount = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
- ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES_WITH_HEADER
- - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
+ ted.msgCount = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
+ MAX_USER_DATA_BYTES_WITH_HEADER;
+ ted.codeUnitsRemaining = ((ted.msgCount *
+ MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
} else {
ted.msgCount = 1;
ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java
new file mode 100644
index 0000000..b214887
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.telephony.SmsMessage;
+import android.telephony.TelephonyManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
+
+public class SmsMessageBodyTest extends AndroidTestCase {
+
+ private static final String sAsciiChars = "@$_ !\"#%&'()*+,-./0123456789" +
+ ":;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n\r";
+ private static final String sGsmBasicChars = "\u00a3\u00a5\u00e8\u00e9" +
+ "\u00f9\u00ec\u00f2\u00c7\u00d8\u00f8\u00c5\u00e5\u0394\u03a6" +
+ "\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u00c6\u00e6" +
+ "\u00df\u00c9\u00a4\u00a1\u00c4\u00d6\u00d1\u00dc\u00a7\u00bf" +
+ "\u00e4\u00f6\u00f1\u00fc\u00e0";
+ private static final String sGsmExtendedAsciiChars = "{|}\\[~]^\f";
+ private static final String sGsmExtendedEuroSymbol = "\u20ac";
+ private static final String sUnicodeChars = "\u4e00\u4e01\u4e02\u4e03" +
+ "\u4e04\u4e05\u4e06\u4e07\u4e08\u4e09\u4e0a\u4e0b\u4e0c\u4e0d" +
+ "\u4e0e\u4e0f\u3041\u3042\u3043\u3044\u3045\u3046\u3047\u3048" +
+ "\u30a1\u30a2\u30a3\u30a4\u30a5\u30a6\u30a7\u30a8" +
+ "\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18" +
+ "\uff70\uff71\uff72\uff73\uff74\uff75\uff76\uff77\uff78" +
+ "\u0400\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408" +
+ "\u00a2\u00a9\u00ae\u2122";
+
+ private static final int sTestLengthCount = 12;
+
+ private static final int[] sSeptetTestLengths =
+ { 0, 1, 2, 80, 159, 160, 161, 240, 305, 306, 307, 320};
+
+ private static final int[] sUnicodeTestLengths =
+ { 0, 1, 2, 35, 69, 70, 71, 100, 133, 134, 135, 160};
+
+ private static final int[] sTestMsgCounts =
+ { 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3};
+
+ private static final int[] sSeptetUnitsRemaining =
+ {160, 159, 158, 80, 1, 0, 145, 66, 1, 0, 152, 139};
+
+ private static final int[] sUnicodeUnitsRemaining =
+ { 70, 69, 68, 35, 1, 0, 63, 34, 1, 0, 66, 41};
+
+
+ @SmallTest
+ public void testCalcLengthAscii() throws Exception {
+ StringBuilder sb = new StringBuilder(320);
+ int[] values = {0, 0, 0, SmsMessage.ENCODING_7BIT};
+ int startPos = 0;
+ int asciiCharsLen = sAsciiChars.length();
+
+ for (int i = 0; i < sTestLengthCount; i++) {
+ int len = sSeptetTestLengths[i];
+ assertTrue(sb.length() <= len);
+
+ while (sb.length() < len) {
+ int addCount = len - sb.length();
+ int endPos = (asciiCharsLen - startPos > addCount) ?
+ (startPos + addCount) : asciiCharsLen;
+ sb.append(sAsciiChars, startPos, endPos);
+ startPos = (endPos == asciiCharsLen) ? 0 : endPos;
+ }
+ assertEquals(len, sb.length());
+
+ String testStr = sb.toString();
+ values[0] = sTestMsgCounts[i];
+ values[1] = len;
+ values[2] = sSeptetUnitsRemaining[i];
+
+ callGsmLengthMethods(testStr, false, values);
+ callGsmLengthMethods(testStr, true, values);
+ callCdmaLengthMethods(testStr, false, values);
+ callCdmaLengthMethods(testStr, true, values);
+ }
+ }
+
+ @SmallTest
+ public void testCalcLength7bitGsm() throws Exception {
+ // TODO
+ }
+
+ @SmallTest
+ public void testCalcLength7bitGsmExtended() throws Exception {
+ // TODO
+ }
+
+ @SmallTest
+ public void testCalcLengthUnicode() throws Exception {
+ StringBuilder sb = new StringBuilder(160);
+ int[] values = {0, 0, 0, SmsMessage.ENCODING_16BIT};
+ int[] values7bit = {1, 0, 0, SmsMessage.ENCODING_7BIT};
+ int startPos = 0;
+ int unicodeCharsLen = sUnicodeChars.length();
+
+ // start with length 1: empty string uses ENCODING_7BIT
+ for (int i = 1; i < sTestLengthCount; i++) {
+ int len = sUnicodeTestLengths[i];
+ assertTrue(sb.length() <= len);
+
+ while (sb.length() < len) {
+ int addCount = len - sb.length();
+ int endPos = (unicodeCharsLen - startPos > addCount) ?
+ (startPos + addCount) : unicodeCharsLen;
+ sb.append(sUnicodeChars, startPos, endPos);
+ startPos = (endPos == unicodeCharsLen) ? 0 : endPos;
+ }
+ assertEquals(len, sb.length());
+
+ String testStr = sb.toString();
+ values[0] = sTestMsgCounts[i];
+ values[1] = len;
+ values[2] = sUnicodeUnitsRemaining[i];
+ values7bit[1] = len;
+ values7bit[2] = MAX_USER_DATA_SEPTETS - len;
+
+ callGsmLengthMethods(testStr, false, values);
+ callCdmaLengthMethods(testStr, false, values);
+ callGsmLengthMethods(testStr, true, values7bit);
+ callCdmaLengthMethods(testStr, true, values7bit);
+ }
+ }
+
+ private void callGsmLengthMethods(CharSequence msgBody, boolean use7bitOnly,
+ int[] expectedValues)
+ {
+ // deprecated GSM-specific method
+ int[] values = android.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], values[0]);
+ assertEquals("codeUnitCount", expectedValues[1], values[1]);
+ assertEquals("codeUnitsRemaining", expectedValues[2], values[2]);
+ assertEquals("codeUnitSize", expectedValues[3], values[3]);
+
+ int activePhone = TelephonyManager.getDefault().getPhoneType();
+ if (TelephonyManager.PHONE_TYPE_GSM == activePhone) {
+ values = android.telephony.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], values[0]);
+ assertEquals("codeUnitCount", expectedValues[1], values[1]);
+ assertEquals("codeUnitsRemaining", expectedValues[2], values[2]);
+ assertEquals("codeUnitSize", expectedValues[3], values[3]);
+ }
+
+ SmsMessageBase.TextEncodingDetails ted =
+ com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], ted.msgCount);
+ assertEquals("codeUnitCount", expectedValues[1], ted.codeUnitCount);
+ assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining);
+ assertEquals("codeUnitSize", expectedValues[3], ted.codeUnitSize);
+ }
+
+ private void callCdmaLengthMethods(CharSequence msgBody, boolean use7bitOnly,
+ int[] expectedValues)
+ {
+ int activePhone = TelephonyManager.getDefault().getPhoneType();
+ if (TelephonyManager.PHONE_TYPE_CDMA == activePhone) {
+ int[] values = android.telephony.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], values[0]);
+ assertEquals("codeUnitCount", expectedValues[1], values[1]);
+ assertEquals("codeUnitsRemaining", expectedValues[2], values[2]);
+ assertEquals("codeUnitSize", expectedValues[3], values[3]);
+ }
+
+ SmsMessageBase.TextEncodingDetails ted =
+ com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], ted.msgCount);
+ assertEquals("codeUnitCount", expectedValues[1], ted.codeUnitCount);
+ assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining);
+ assertEquals("codeUnitSize", expectedValues[3], ted.codeUnitSize);
+
+ ted = com.android.internal.telephony.cdma.sms.BearerData.calcTextEncodingDetails(msgBody, use7bitOnly);
+ assertEquals("msgCount", expectedValues[0], ted.msgCount);
+ assertEquals("codeUnitCount", expectedValues[1], ted.codeUnitCount);
+ assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining);
+ assertEquals("codeUnitSize", expectedValues[3], ted.codeUnitSize);
+ }
+}
diff --git a/tests/BatteryWaster/res/layout/battery_waster.xml b/tests/BatteryWaster/res/layout/battery_waster.xml
index e1cb6bf..57a5b55 100644
--- a/tests/BatteryWaster/res/layout/battery_waster.xml
+++ b/tests/BatteryWaster/res/layout/battery_waster.xml
@@ -25,11 +25,23 @@
android:layout_height="wrap_content"
android:layout_marginLeft="25dp"
android:layout_marginTop="25dp"
+ android:saveEnabled="false"
android:textSize="18sp"
android:textColor="#ffffffff"
android:text="@string/waste_away"
/>
+ <CheckBox android:id="@+id/checkbox_wake"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="25dp"
+ android:layout_marginTop="25dp"
+ android:saveEnabled="false"
+ android:textSize="18sp"
+ android:textColor="#ffffffff"
+ android:text="@string/wake_away"
+ />
+
<ScrollView android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="0px"
diff --git a/tests/BatteryWaster/res/values/strings.xml b/tests/BatteryWaster/res/values/strings.xml
index 46c5fa1..a3b849a 100644
--- a/tests/BatteryWaster/res/values/strings.xml
+++ b/tests/BatteryWaster/res/values/strings.xml
@@ -18,5 +18,7 @@
<string name="waste_away">Discharge my battery!</string>
+ <string name="wake_away">Keep my device awake!</string>
+
</resources>
diff --git a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
index 8ea7e00..48c4520 100644
--- a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
+++ b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
@@ -39,8 +39,11 @@ public class BatteryWaster extends Activity {
DateFormat mDateFormat;
IntentFilter mFilter;
PowerManager.WakeLock mWakeLock;
+ PowerManager.WakeLock mPartialWakeLock;
SpinThread mThread;
+ boolean mWasting, mWaking;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -50,6 +53,7 @@ public class BatteryWaster extends Activity {
setContentView(R.layout.battery_waster);
findViewById(R.id.checkbox).setOnClickListener(mClickListener);
+ findViewById(R.id.checkbox_wake).setOnClickListener(mWakeClickListener);
mLog = (TextView)findViewById(R.id.log);
mDateFormat = DateFormat.getInstance();
@@ -63,13 +67,27 @@ public class BatteryWaster extends Activity {
PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "BatteryWaster");
mWakeLock.setReferenceCounted(false);
+ mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BatteryWaster");
+ mPartialWakeLock.setReferenceCounted(false);
}
@Override
public void onPause() {
+ super.onPause();
stopRunning();
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ if (mPartialWakeLock.isHeld()) {
+ mPartialWakeLock.release();
+ }
+ }
+
View.OnClickListener mClickListener = new View.OnClickListener() {
public void onClick(View v) {
CheckBox checkbox = (CheckBox)v;
@@ -81,23 +99,63 @@ public class BatteryWaster extends Activity {
}
};
+ View.OnClickListener mWakeClickListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ CheckBox checkbox = (CheckBox)v;
+ if (checkbox.isChecked()) {
+ mWaking = true;
+ updateWakeLock();
+ } else {
+ mWaking = false;
+ updateWakeLock();
+ }
+ }
+ };
+
void startRunning() {
- log("Start");
- registerReceiver(mReceiver, mFilter);
- mWakeLock.acquire();
- if (mThread == null) {
- mThread = new SpinThread();
- mThread.start();
+ if (!mWasting) {
+ log("Start");
+ registerReceiver(mReceiver, mFilter);
+ mWasting = true;
+ updateWakeLock();
+ if (mThread == null) {
+ mThread = new SpinThread();
+ mThread.start();
+ }
}
}
void stopRunning() {
- log("Stop");
- unregisterReceiver(mReceiver);
- mWakeLock.release();
- if (mThread != null) {
- mThread.quit();
- mThread = null;
+ if (mWasting) {
+ log("Stop");
+ unregisterReceiver(mReceiver);
+ mWasting = false;
+ updateWakeLock();
+ if (mThread != null) {
+ mThread.quit();
+ mThread = null;
+ }
+ }
+ }
+
+ void updateWakeLock() {
+ if (mWasting) {
+ if (!mWakeLock.isHeld()) {
+ mWakeLock.acquire();
+ }
+ } else {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+ if (mWaking) {
+ if (!mPartialWakeLock.isHeld()) {
+ mPartialWakeLock.acquire();
+ }
+ } else {
+ if (mPartialWakeLock.isHeld()) {
+ mPartialWakeLock.release();
+ }
}
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 181b4c8..775dc24 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -189,5 +189,14 @@
</intent-filter>
</activity>
+ <activity
+ android:name="StackActivity"
+ android:label="_Stacks">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
</application>
</manifest>
diff --git a/tests/HwAccelerationTest/res/layout/list_activity.xml b/tests/HwAccelerationTest/res/layout/list_activity.xml
index f548f53..6bba370 100644
--- a/tests/HwAccelerationTest/res/layout/list_activity.xml
+++ b/tests/HwAccelerationTest/res/layout/list_activity.xml
@@ -19,6 +19,31 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="3dip"
+
+ android:text="Add" />
+
+ <Button
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="3dip"
+ android:layout_marginRight="10dip"
+
+ android:text="Remove" />
+
+ </LinearLayout>
+
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
diff --git a/tests/HwAccelerationTest/res/layout/stack.xml b/tests/HwAccelerationTest/res/layout/stack.xml
new file mode 100644
index 0000000..b4d2d73a
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/stack.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:paddingTop="0dp"
+ android:paddingBottom="0dp"
+ android:paddingLeft="12dp"
+ android:paddingRight="12dp"
+ android:focusable="true">
+ <StackView
+ android:id="@+id/stack_view"
+ android:layout_width="348px"
+ android:layout_height="374px"
+ android:layout_gravity="center"
+ android:background="#00000000"
+ android:cacheColorHint="#00000000"
+ android:autoStart="true" />
+</FrameLayout>
diff --git a/tests/HwAccelerationTest/res/layout/stack_item.xml b/tests/HwAccelerationTest/res/layout/stack_item.xml
new file mode 100644
index 0000000..3504018
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/stack_item.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/stack_item"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <FrameLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <ImageView android:id="@+id/textview_icon"
+ android:layout_height="250dip"
+ android:layout_width="250dip"
+ android:layout_gravity="center" />
+ <TextView android:id="@+id/mini_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center" />
+ </FrameLayout>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layout_gravity="center" />
+</FrameLayout> \ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java
index fd7a1e6..2ba249a 100644
--- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java
@@ -23,6 +23,7 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Bundle;
+import android.util.Log;
import android.view.View;
@SuppressWarnings({"UnusedDeclaration"})
@@ -51,6 +52,32 @@ public class QuickRejectActivity extends Activity {
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
+ int count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "count=" + count);
+ count = canvas.save();
+ Log.d("OpenGLRenderer", "count after save=" + count);
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "getSaveCount after save=" + count);
+ canvas.restore();
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "count after restore=" + count);
+ canvas.save();
+ Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+ canvas.save();
+ Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+ canvas.save();
+ Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+ canvas.restoreToCount(count);
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "count after restoreToCount=" + count);
+ count = canvas.saveLayer(0, 0, 10, 10, mBitmapPaint, Canvas.ALL_SAVE_FLAG);
+ Log.d("OpenGLRenderer", "count after saveLayer=" + count);
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "getSaveCount after saveLayer=" + count);
+ canvas.restore();
+ count = canvas.getSaveCount();
+ Log.d("OpenGLRenderer", "count after restore=" + count);
+
canvas.save();
canvas.clipRect(0.0f, 0.0f, 40.0f, 40.0f);
canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/StackActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/StackActivity.java
new file mode 100644
index 0000000..5c8db6e
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/StackActivity.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.StackView;
+import android.widget.TextView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class StackActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.stack);
+
+ StackView stack = (StackView) findViewById(R.id.stack_view);
+ stack.setAdapter(new ArrayAdapter<Drawable>(this, android.R.layout.simple_list_item_1,
+ android.R.id.text1, new Drawable[] {
+ getResources().getDrawable(R.drawable.sunset1),
+ getResources().getDrawable(R.drawable.sunset2),
+ }) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View item = convertView;
+ if (item == null) {
+ item = LayoutInflater.from(getContext()).inflate(
+ R.layout.stack_item, null, false);
+ }
+ ((ImageView) item.findViewById(R.id.textview_icon)).setImageDrawable(
+ getItem(position % getCount()));
+ ((TextView) item.findViewById(R.id.mini_text)).setText("" + position);
+ return item;
+ }
+ });
+ stack.setDisplayedChild(0);
+ }
+}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 5855b56..31c1722 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1880,7 +1880,7 @@ addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName,
className.append(inClassName);
}
}
-
+
String8 rule("-keep class ");
rule += className;
rule += " { <init>(...); }";
@@ -1955,7 +1955,7 @@ writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& ass
if (tag == "application") {
inApplication = true;
keepTag = true;
-
+
String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
"backupAgent", &error);
if (agent.length() > 0) {
@@ -1988,9 +1988,17 @@ writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& ass
return NO_ERROR;
}
+struct NamespaceAttributePair {
+ const char* ns;
+ const char* attr;
+
+ NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {}
+ NamespaceAttributePair() : ns(NULL), attr(NULL) {}
+};
+
status_t
writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
- const char* startTag, const char* altTag)
+ const char* startTag, const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs)
{
status_t err;
ResXMLTree tree;
@@ -2020,7 +2028,7 @@ writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
return NO_ERROR;
}
}
-
+
while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code != ResXMLTree::START_TAG) {
continue;
@@ -2031,16 +2039,21 @@ writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
if (strchr(tag.string(), '.')) {
addProguardKeepRule(keep, tag, NULL,
layoutFile->getPrintableSource(), tree.getLineNumber());
- } else if (altTag != NULL && tag == altTag) {
- ssize_t classIndex = tree.indexOfAttribute(NULL, "class");
- if (classIndex < 0) {
- fprintf(stderr, "%s:%d: <view> does not have class attribute.\n",
- layoutFile->getPrintableSource().string(), tree.getLineNumber());
- } else {
- size_t len;
- addProguardKeepRule(keep,
- String8(tree.getAttributeStringValue(classIndex, &len)), NULL,
- layoutFile->getPrintableSource(), tree.getLineNumber());
+ } else if (tagAttrPairs != NULL) {
+ ssize_t tagIndex = tagAttrPairs->indexOfKey(tag);
+ if (tagIndex >= 0) {
+ const NamespaceAttributePair& nsAttr = tagAttrPairs->valueAt(tagIndex);
+ ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr);
+ if (attrIndex < 0) {
+ // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n",
+ // layoutFile->getPrintableSource().string(), tree.getLineNumber(),
+ // tag.string(), nsAttr.ns, nsAttr.attr);
+ } else {
+ size_t len;
+ addProguardKeepRule(keep,
+ String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
+ layoutFile->getPrintableSource(), tree.getLineNumber());
+ }
}
}
}
@@ -2048,25 +2061,42 @@ writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
return NO_ERROR;
}
+static void addTagAttrPair(KeyedVector<String8, NamespaceAttributePair>* dest,
+ const char* tag, const char* ns, const char* attr) {
+ dest->add(String8(tag), NamespaceAttributePair(ns, attr));
+}
+
status_t
writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
{
status_t err;
+
+ // tag:attribute pairs that should be checked in layout files.
+ KeyedVector<String8, NamespaceAttributePair> kLayoutTagAttrPairs;
+ addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
+ addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
+
+ // tag:attribute pairs that should be checked in xml files.
+ KeyedVector<String8, NamespaceAttributePair> kXmlTagAttrPairs;
+ addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
+ addTagAttrPair(&kXmlTagAttrPairs, "Header", RESOURCES_ANDROID_NAMESPACE, "fragment");
+
const Vector<sp<AaptDir> >& dirs = assets->resDirs();
const size_t K = dirs.size();
for (size_t k=0; k<K; k++) {
const sp<AaptDir>& d = dirs.itemAt(k);
const String8& dirName = d->getLeaf();
const char* startTag = NULL;
- const char* altTag = NULL;
+ const KeyedVector<String8, NamespaceAttributePair>* tagAttrPairs = NULL;
if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
- altTag = "view";
+ tagAttrPairs = &kLayoutTagAttrPairs;
} else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
startTag = "PreferenceScreen";
+ tagAttrPairs = &kXmlTagAttrPairs;
} else {
continue;
}
-
+
const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles();
const size_t N = groups.size();
for (size_t i=0; i<N; i++) {
@@ -2074,7 +2104,7 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
const size_t M = files.size();
for (size_t j=0; j<M; j++) {
- err = writeProguardForXml(keep, files.valueAt(j), startTag, altTag);
+ err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs);
if (err < 0) {
return err;
}
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index abdc9d7..f4be839 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -191,11 +191,8 @@ public interface SipAudioCall {
*/
void continueCall() throws SipException;
- /** Puts the device to in-call mode. */
- void setInCallMode();
-
/** Puts the device to speaker mode. */
- void setSpeakerMode();
+ void setSpeakerMode(boolean speakerMode);
/** Toggles mute. */
void toggleMute();
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
index 474bc4b..7161309 100644
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ b/voip/java/android/net/sip/SipAudioCallImpl.java
@@ -485,16 +485,9 @@ public class SipAudioCallImpl extends SipSessionAdapter
return mMuted;
}
- public synchronized void setInCallMode() {
+ public synchronized void setSpeakerMode(boolean speakerMode) {
((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
- .setSpeakerphoneOn(false);
- ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
- .setMode(AudioManager.MODE_NORMAL);
- }
-
- public synchronized void setSpeakerMode() {
- ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
- .setSpeakerphoneOn(true);
+ .setSpeakerphoneOn(speakerMode);
}
public void sendDtmf(int code) {
@@ -587,8 +580,15 @@ public class SipAudioCallImpl extends SipSessionAdapter
Log.d(TAG, " not sending");
audioStream.setMode(RtpStream.MODE_RECEIVE_ONLY);
}
+ } else {
+ /* The recorder volume will be very low if the device is in
+ * IN_CALL mode. Therefore, we have to set the mode to NORMAL
+ * in order to have the normal microphone level.
+ */
+ ((AudioManager) mContext.getSystemService
+ (Context.AUDIO_SERVICE))
+ .setMode(AudioManager.MODE_NORMAL);
}
- setInCallMode();
AudioGroup audioGroup = new AudioGroup();
audioStream.join(audioGroup);
@@ -614,7 +614,6 @@ public class SipAudioCallImpl extends SipSessionAdapter
mRtpSession = null;
}
}
- setInCallMode();
}
private int getLocalMediaPort() {